Загрузка данных


"""
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()