import pygame import random import sys # Инициализация Pygame pygame.init() # Константы GAME_WIDTH = 600 GAME_HEIGHT = 600 INFO_WIDTH = 200 WINDOW_WIDTH = GAME_WIDTH + INFO_WIDTH WINDOW_HEIGHT = GAME_HEIGHT CELL_SIZE = 20 GRID_WIDTH = GAME_WIDTH // CELL_SIZE GRID_HEIGHT = GAME_HEIGHT // CELL_SIZE # Цвета BLACK = (10, 10, 10) DARK_GRAY = (30, 30, 30) GRAY = (60, 60, 60) WHITE = (255, 255, 255) GREEN = (0, 255, 100) DARK_GREEN = (0, 100, 0) YELLOW = (255, 255, 0) HEAD_COLOR = (0, 255, 100) RED = (255, 80, 80) BLUE = (80, 150, 255) PURPLE = (180, 100, 255) # Настройки игры BASE_FPS = 8 MAX_FPS = 20 class Snake: def __init__(self): start_x = GRID_WIDTH // 2 start_y = GRID_HEIGHT // 2 self.body = [(start_x, start_y)] self.direction = (1, 0) self.grow = False def move(self): head = self.body[0] new_head = (head[0] + self.direction[0], head[1] + self.direction[1]) self.body.insert(0, new_head) if not self.grow: self.body.pop() else: self.grow = False def change_direction(self, new_direction): # Запрещаем разворот на 180 градусов if (new_direction[0] * -1, new_direction[1] * -1) != self.direction: self.direction = new_direction return True return False def check_collision(self): head = self.body[0] if (head[0] < 0 or head[0] >= GRID_WIDTH or head[1] < 0 or head[1] >= GRID_HEIGHT): return True if head in self.body[1:]: return True return False def eat_food(self, food_position): if self.body[0] == food_position: self.grow = True return True return False class Button: def __init__(self, x, y, width, height, text, color, hover_color): self.rect = pygame.Rect(x, y, width, height) self.text = text self.color = color self.hover_color = hover_color self.is_hovered = False self.font = pygame.font.Font(None, 32) def draw(self, screen): color = self.hover_color if self.is_hovered else self.color pygame.draw.rect(screen, color, self.rect, border_radius=10) pygame.draw.rect(screen, WHITE, self.rect, 2, border_radius=10) text = self.font.render("СТАРТ", True, WHITE) text_rect = text.get_rect(center=self.rect.center) screen.blit(text, text_rect) def handle_event(self, event): if event.type == pygame.MOUSEMOTION: self.is_hovered = self.rect.collidepoint(event.pos) elif event.type == pygame.MOUSEBUTTONDOWN: if self.is_hovered: return True return False class Game: def __init__(self): self.screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption("Snake Game") # Шрифты self.title_font = pygame.font.Font(None, 48) self.font = pygame.font.Font(None, 36) self.small_font = pygame.font.Font(None, 24) self.tiny_font = pygame.font.Font(None, 20) # Кнопка старта button_x = GAME_WIDTH + (INFO_WIDTH - 160) // 2 self.start_button = Button(button_x, 80, 160, 50, "СТАРТ", DARK_GREEN, GREEN) self.reset_game() self.clock = pygame.time.Clock() self.game_state = "MENU" # MENU, PLAYING, GAME_OVER def reset_game(self): self.snake = Snake() self.food = self.generate_food() self.score = 0 self.high_score = self.load_high_score() self.paused = False self.current_fps = BASE_FPS def load_high_score(self): try: with open("highscore.txt", "r") as f: return int(f.read()) except: return 0 def save_high_score(self): if self.score > self.high_score: with open("highscore.txt", "w") as f: f.write(str(self.score)) self.high_score = self.score def generate_food(self): while True: food = (random.randint(0, GRID_WIDTH - 1), random.randint(0, GRID_HEIGHT - 1)) if food not in self.snake.body: return food def handle_events(self): for event in pygame.event.get(): if event.type == pygame.QUIT: return False # Кнопка в меню if self.game_state == "MENU": if self.start_button.handle_event(event): self.game_state = "PLAYING" self.reset_game() # Клавиши - УПРОЩЕННАЯ И ГАРАНТИРОВАННАЯ ПРОВЕРКА if event.type == pygame.KEYDOWN: # СПЕЦИАЛЬНО ДЛЯ WASD - ПРОВЕРЯЕМ БУКВЫ if event.unicode: # Если есть символьное представление key_char = event.unicode.lower() # Приводим к нижнему регистру if self.game_state == "PLAYING" and not self.paused: if key_char == 'w': self.snake.change_direction((0, -1)) elif key_char == 's': self.snake.change_direction((0, 1)) elif key_char == 'a': self.snake.change_direction((-1, 0)) elif key_char == 'd': self.snake.change_direction((1, 0)) # СТРЕЛКИ - проверяем по кодам if self.game_state == "PLAYING" and not self.paused: if event.key == pygame.K_UP: self.snake.change_direction((0, -1)) elif event.key == pygame.K_DOWN: self.snake.change_direction((0, 1)) elif event.key == pygame.K_LEFT: self.snake.change_direction((-1, 0)) elif event.key == pygame.K_RIGHT: self.snake.change_direction((1, 0)) # Пауза (P) if event.key == pygame.K_p and self.game_state == "PLAYING": self.paused = not self.paused # SPACE - универсальная клавиша if event.key == pygame.K_SPACE: if self.game_state == "MENU": self.game_state = "PLAYING" self.reset_game() elif self.game_state == "PLAYING": self.paused = not self.paused elif self.game_state == "GAME_OVER": self.game_state = "PLAYING" self.reset_game() # ESC - выход в меню или из игры if event.key == pygame.K_ESCAPE: if self.game_state == "PLAYING": self.game_state = "MENU" else: return False return True def update(self): if self.game_state != "PLAYING" or self.paused: return self.snake.move() self.current_fps = BASE_FPS + min((self.score // 5) * 2, MAX_FPS - BASE_FPS) if self.snake.check_collision(): self.game_state = "GAME_OVER" self.save_high_score() if self.snake.eat_food(self.food): self.score += 1 self.food = self.generate_food() def get_segment_color(self, index, total_length): if index == 0: return HEAD_COLOR elif index == 1 and total_length > 2: return YELLOW elif index == total_length - 1: return DARK_GREEN else: green_value = max(100, 200 - (index * 2)) return (0, green_value, 0) def draw_snake(self): total_length = len(self.snake.body) for i, (x, y) in enumerate(self.snake.body): rect = pygame.Rect(x * CELL_SIZE + 1, y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2) color = self.get_segment_color(i, total_length) if i == 0: pygame.draw.rect(self.screen, color, rect, border_radius=8) if self.snake.direction == (1, 0): eye1 = (rect.right - 6, rect.top + 5) eye2 = (rect.right - 6, rect.bottom - 6) elif self.snake.direction == (-1, 0): eye1 = (rect.left + 4, rect.top + 5) eye2 = (rect.left + 4, rect.bottom - 6) elif self.snake.direction == (0, -1): eye1 = (rect.left + 5, rect.top + 4) eye2 = (rect.right - 6, rect.top + 4) else: eye1 = (rect.left + 5, rect.bottom - 6) eye2 = (rect.right - 6, rect.bottom - 6) pygame.draw.circle(self.screen, WHITE, eye1, 2) pygame.draw.circle(self.screen, WHITE, eye2, 2) pygame.draw.circle(self.screen, BLACK, eye1, 1) pygame.draw.circle(self.screen, BLACK, eye2, 1) highlight_rect = pygame.Rect(rect.x + 2, rect.y + 2, 3, 3) pygame.draw.rect(self.screen, (150, 255, 150), highlight_rect) elif i == total_length - 1 and total_length > 1: pygame.draw.rect(self.screen, color, rect, border_radius=4) if len(self.snake.body) > 2: prev_seg = self.snake.body[i-1] if prev_seg[0] > x: points = [(rect.left, rect.top), (rect.left, rect.bottom), (rect.left - 3, rect.centery)] pygame.draw.polygon(self.screen, color, points) elif prev_seg[0] < x: points = [(rect.right, rect.top), (rect.right, rect.bottom), (rect.right + 3, rect.centery)] pygame.draw.polygon(self.screen, color, points) elif prev_seg[1] > y: points = [(rect.left, rect.top), (rect.right, rect.top), (rect.centerx, rect.top - 3)] pygame.draw.polygon(self.screen, color, points) elif prev_seg[1] < y: points = [(rect.left, rect.bottom), (rect.right, rect.bottom), (rect.centerx, rect.bottom + 3)] pygame.draw.polygon(self.screen, color, points) elif i == 1 and total_length > 2: pygame.draw.rect(self.screen, color, rect, border_radius=6) pygame.draw.line(self.screen, (200, 200, 0), (rect.x + 2, rect.centery), (rect.right - 2, rect.centery), 2) else: pygame.draw.rect(self.screen, color, rect, border_radius=6) if i % 2 == 0: pygame.draw.circle(self.screen, (0, 100, 0), (rect.centerx, rect.centery), 2) def draw_food(self): x, y = self.food center = (x * CELL_SIZE + CELL_SIZE//2, y * CELL_SIZE + CELL_SIZE//2) pulse = abs(50 - (pygame.time.get_ticks() // 10 % 100)) / 100 radius = CELL_SIZE//3 + int(pulse * 2) pygame.draw.circle(self.screen, RED, center, radius) pygame.draw.circle(self.screen, (255, 200, 200), (center[0] - 2, center[1] - 2), 2) leaf_points = [ (center[0] - 2, center[1] - radius - 1), (center[0], center[1] - radius - 3), (center[0] + 2, center[1] - radius - 1) ] pygame.draw.polygon(self.screen, GREEN, leaf_points) def draw_info_panel(self): # Фон панели pygame.draw.rect(self.screen, DARK_GRAY, (GAME_WIDTH, 0, INFO_WIDTH, GAME_HEIGHT)) pygame.draw.line(self.screen, GRAY, (GAME_WIDTH, 0), (GAME_WIDTH, GAME_HEIGHT), 2) y = 20 # Заголовок title = self.title_font.render("Змейка", True, GREEN) title_rect = title.get_rect(center=(GAME_WIDTH + INFO_WIDTH//2, y)) self.screen.blit(title, title_rect) y += 60 # Кнопка старта в меню if self.game_state == "Меню": self.start_button.draw(self.screen) y += 80 # Счет score_text = self.font.render("Очки", True, WHITE) self.screen.blit(score_text, (GAME_WIDTH + 20, y)) score_value = self.font.render(str(self.score), True, YELLOW) score_rect = score_value.get_rect(center=(GAME_WIDTH + INFO_WIDTH//2, y + 40)) self.screen.blit(score_value, score_rect) y += 80 # Рекорд high_text = self.small_font.render("Лучший", True, GRAY) self.screen.blit(high_text, (GAME_WIDTH + 20, y)) high_value = self.font.render(str(self.high_score), True, PURPLE) high_rect = high_value.get_rect(center=(GAME_WIDTH + INFO_WIDTH//2, y + 30)) self.screen.blit(high_value, high_rect) y += 80 # Скорость speed_text = self.small_font.render("Скорость", True, GRAY) self.screen.blit(speed_text, (GAME_WIDTH + 20, y)) speed_value = self.small_font.render(f"{self.current_fps} FPS", True, BLUE) speed_rect = speed_value.get_rect(center=(GAME_WIDTH + INFO_WIDTH//2, y + 25)) self.screen.blit(speed_value, speed_rect) y += 70 # Управление ctrl_text = self.small_font.render("Управление", True, WHITE) self.screen.blit(ctrl_text, (GAME_WIDTH + 20, y)) y += 25 controls = [ ("W / UP", "вверх"), ("S / DOWN", "вниз"), ("A / LEFT", "влево"), ("D / RIGHT", "вправо"), ("P / SPACE", "пауза"), ("SPACE", "старт"), ("ESC", "меню") ] for key, action in controls: key_text = self.tiny_font.render(key, True, GREEN) action_text = self.tiny_font.render(action, True, GRAY) self.screen.blit(key_text, (GAME_WIDTH + 20, y)) self.screen.blit(action_text, (GAME_WIDTH + 100, y)) y += 18 def draw(self): self.screen.fill(BLACK) if self.game_state == "MENU": # Меню for x in range(0, GAME_WIDTH, CELL_SIZE): pygame.draw.line(self.screen, (30, 30, 30), (x, 0), (x, GAME_HEIGHT)) for y in range(0, GAME_HEIGHT, CELL_SIZE): pygame.draw.line(self.screen, (30, 30, 30), (0, y), (GAME_WIDTH, y)) overlay = pygame.Surface((GAME_WIDTH, GAME_HEIGHT)) overlay.set_alpha(200) overlay.fill(BLACK) self.screen.blit(overlay, (0, 0)) title = self.title_font.render("Змейка", True, GREEN) title_rect = title.get_rect(center=(GAME_WIDTH//2, GAME_HEIGHT//2 - 50)) self.screen.blit(title, title_rect) hint = self.font.render("Нажмите пробел для начала", True, GRAY) hint_rect = hint.get_rect(center=(GAME_WIDTH//2, GAME_HEIGHT//2 + 20)) self.screen.blit(hint, hint_rect) else: # Игровое поле for x in range(0, GAME_WIDTH, CELL_SIZE): pygame.draw.line(self.screen, (30, 30, 30), (x, 0), (x, GAME_HEIGHT)) for y in range(0, GAME_HEIGHT, CELL_SIZE): pygame.draw.line(self.screen, (30, 30, 30), (0, y), (GAME_WIDTH, y)) self.draw_snake() self.draw_food() if self.paused: overlay = pygame.Surface((GAME_WIDTH, GAME_HEIGHT)) overlay.set_alpha(128) overlay.fill(BLACK) self.screen.blit(overlay, (0, 0)) pause = self.font.render("Пауза", True, WHITE) pause_rect = pause.get_rect(center=(GAME_WIDTH//2, GAME_HEIGHT//2)) self.screen.blit(pause, pause_rect) hint = self.small_font.render("SPACE - продолжить", True, GRAY) hint_rect = hint.get_rect(center=(GAME_WIDTH//2, GAME_HEIGHT//2 + 40)) self.screen.blit(hint, hint_rect) if self.game_state == "GAME_OVER": overlay = pygame.Surface((GAME_WIDTH, GAME_HEIGHT)) overlay.set_alpha(180) overlay.fill(BLACK) self.screen.blit(overlay, (0, 0)) go = self.title_font.render("Хана змейке", True, RED) go_rect = go.get_rect(center=(GAME_WIDTH//2, GAME_HEIGHT//2 - 30)) self.screen.blit(go, go_rect) restart = self.small_font.render("Нажмите пробел, чтоб начать сначала", True, WHITE) restart_rect = restart.get_rect(center=(GAME_WIDTH//2, GAME_HEIGHT//2 + 20)) self.screen.blit(restart, restart_rect) self.draw_info_panel() pygame.display.flip() def run(self): running = True while running: running = self.handle_events() self.update() self.draw() fps = self.current_fps if self.game_state == "Погнали" else BASE_FPS self.clock.tick(fps) pygame.quit() sys.exit() if __name__ == "__main__": game = Game() game.run()