Загрузка данных
"""
Snake — классическая Змейка на Pygame
Группа 3: Калашников, Венчинский, Мисюра, Мартюгов
Управление:
стрелки — поворот
R — рестарт после Game Over
ESC — выход
Запуск:
pip install pygame
python main.py
"""
import pygame
import random
import sys
# ==========================================================
# НАСТРОЙКИ
# ==========================================================
WIDTH = 600
HEIGHT = 600
CELL = 20
COLS = WIDTH // CELL
ROWS = HEIGHT // CELL
FPS = 10 # скорость змейки (кадров в секунду)
BG_COLOR = (20, 20, 30)
GRID_COLOR = (30, 30, 45)
SNAKE_COLOR = (80, 200, 120)
SNAKE_HEAD_COLOR = (150, 255, 170)
FOOD_COLOR = (240, 80, 80)
TEXT_COLOR = (230, 230, 230)
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Святик лох")
clock = pygame.time.Clock()
font = pygame.font.SysFont("Arial", 22, bold=True)
big_font = pygame.font.SysFont("Arial", 50, bold=True)
# ==========================================================
# РОЛЬ 1 — Калашников: Food
# ==========================================================
class Food:
def __init__(self):
self.position = (5, 5)
def spawn(self, snake_body):
"""Поставить еду в случайную свободную клетку."""
# Подсказка 1: используй цикл while True
while True:
# Подсказка 2: случайные координаты в пределах поля
col = random.randint(0, COLS - 1)
row = random.randint(0, ROWS - 1)
# Подсказка 3: если клетка НЕ занята телом змейки — выходим
if (col, row) not in snake_body:
self.position = (col, row)
break
# Бонус: после каждого поедания двигай еду дважды — «телепортирующаяся» еда
# Второй спавн (телепортация)
while True:
col = random.randint(0, COLS - 1)
row = random.randint(0, ROWS - 1)
if (col, row) not in snake_body:
self.position = (col, row)
return
def draw(self, surface):
x = self.position[0] * CELL
y = self.position[1] * CELL
pygame.draw.rect(surface, FOOD_COLOR, (x + 2, y + 2, CELL - 4, CELL - 4), border_radius=6)
# блик
pygame.draw.circle(surface, (255, 200, 200), (x + CELL // 3, y + CELL // 3), 2)
# ==========================================================
# РОЛЬ 2 — Венчинский: Renderer
# ==========================================================
class Renderer:
def draw_grid(self, surface):
surface.fill(BG_COLOR)
for x in range(0, WIDTH, CELL):
pygame.draw.line(surface, GRID_COLOR, (x, 0), (x, HEIGHT))
for y in range(0, HEIGHT, CELL):
pygame.draw.line(surface, GRID_COLOR, (0, y), (WIDTH, y))
def draw_snake(self, surface, body):
"""Нарисовать змейку. body[0] — голова."""
# Подсказка 1: цикл по всем сегментам змейки
for i, (col, row) in enumerate(body):
# Подсказка 2: переводим координаты клеток в пиксели
x = col * CELL
y = row * CELL
# Подсказка 3: голова отличается по цвету от тела
color = SNAKE_HEAD_COLOR if i == 0 else SNAKE_COLOR
# Подсказка 4: рисуем сегмент змейки
pygame.draw.rect(surface, color, (x + 1, y + 1, CELL - 2, CELL - 2), border_radius=5)
# Бонус: рисуем глазки на голове
if i == 0:
# Левый глаз
eye_x1 = x + CELL // 4
eye_y = y + CELL // 4
pygame.draw.circle(surface, (0, 0, 0), (eye_x1, eye_y), 3)
# Правый глаз
eye_x2 = x + 3 * CELL // 4
pygame.draw.circle(surface, (0, 0, 0), (eye_x2, eye_y), 3)
# Блики в глазах (белые точки)
pygame.draw.circle(surface, (255, 255, 255), (eye_x1 - 1, eye_y - 1), 1)
pygame.draw.circle(surface, (255, 255, 255), (eye_x2 - 1, eye_y - 1), 1)
def draw_score(self, surface, score):
text = font.render(f"Счёт: {score}", True, TEXT_COLOR)
surface.blit(text, (10, 10))
def draw_game_over(self, surface, score):
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 180))
surface.blit(overlay, (0, 0))
text1 = big_font.render("GAME OVER", True, (220, 80, 80))
text2 = font.render(f"Твой счёт: {score}", True, TEXT_COLOR)
text3 = font.render("R — заново | ESC — выход", True, TEXT_COLOR)
surface.blit(text1, text1.get_rect(center=(WIDTH // 2, HEIGHT // 2 - 50)))
surface.blit(text2, text2.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 10)))
surface.blit(text3, text3.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 50)))
# ==========================================================
# РОЛЬ 3 — Мисюра: Snake (движение + столкновения)
# ==========================================================
class Snake:
def __init__(self):
mid_c, mid_r = COLS // 2, ROWS // 2
self.body = [(mid_c, mid_r), (mid_c - 1, mid_r), (mid_c - 2, mid_r)]
self.direction = (1, 0)
self.grow_pending = False
def set_direction(self, new_dir):
# запрет разворота на 180 градусов
if new_dir[0] == -self.direction[0] and new_dir[1] == -self.direction[1]:
return
self.direction = new_dir
def move(self):
"""Сдвинуть змейку на одну клетку в текущем направлении."""
# TODO (Мисюра): реализуй шаг змейки
head = self.body[0]
new_head = (head[0] + self.direction[0], head[1] + self.direction[1])
self.body.insert(0, new_head) # добавили новую голову спереди
if self.grow_pending == True:
pass
elif self.grow_pending == False:
self.body.pop()
self.grow_pending = False
# Бонус: добавь поле self.steps = 0 и считай шаги (для статистики).
pass
def grow(self):
self.grow_pending = True
def head(self):
return self.body[0]
def check_wall_collision(self):
c, r = self.head()
return c < 0 or c >= COLS or r < 0 or r >= ROWS
def check_self_collision(self):
return self.head() in self.body[1:]
# ==========================================================
# РОЛЬ 4 — Мартюгов: Game loop / input
# ==========================================================
def handle_input(event, snake, restart_callback, game_over):
"""Обрабатывает одно событие клавиатуры."""
# TODO (Мартюгов): обработай стрелки и клавишу рестарта
if event.key == pygame.K_UP: snake.set_direction((0, -1))
if event.key == pygame.K_DOWN: snake.set_direction((0, 1))
if event.key == pygame.K_LEFT: snake.set_direction((-1, 0))
if event.key == pygame.K_RIGHT: snake.set_direction((1, 0))
if event.key == pygame.K_r and game_over:
restart_callback()
if event.key == pygame.K_w: snake.set_direction((0, -1))
if event.key == pygame.K_s: snake.set_direction((0, 1))
if event.key == pygame.K_a: snake.set_direction((-1, 0))
if event.key == pygame.K_d: snake.set_direction((1, 0))
if event.key == pygame.K_r and game_over:
restart_callback()
pass
def main():
snake = Snake()
food = Food()
food.spawn(snake.body)
renderer = Renderer()
score = 0
game_over = False
def restart():
nonlocal snake, food, score, game_over
snake = Snake()
food = Food()
food.spawn(snake.body)
score = 0
game_over = False
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit(); sys.exit()
handle_input(event, snake, restart, game_over)
if not game_over:
snake.move()
if snake.check_wall_collision() or snake.check_self_collision():
game_over = True
elif snake.head() == food.position:
snake.grow()
food.spawn(snake.body)
score += 1
renderer.draw_grid(screen)
food.draw(screen)
renderer.draw_snake(screen, snake.body)
renderer.draw_score(screen, score)
if game_over:
renderer.draw_game_over(screen, score)
pygame.display.flip()
clock.tick(FPS)
if __name__ == "__main__":
main()