Загрузка данных
"""
Snake — классическая Змейка на Pygame
Группа 3: Калашников, Венчинский, Мисюра, Мартюгов
Управление:
стрелки/WASD — поворот
R — рестарт после Game Over
ESC — выход из игры (везде)
Q — отмена в меню сложности
C — открыть меню сложности / подтвердить выбор
"""
import pygame
import random
import sys
# ==========================================================
# НАСТРОЙКИ
# ==========================================================
WIDTH = 600
HEIGHT = 600
CELL = 20
COLS = WIDTH // CELL
ROWS = HEIGHT // CELL
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("Comic Sans MS", 22, bold=True)
big_font = pygame.font.SysFont("Comic Sans MS", 50, bold=True)
small_font = pygame.font.SysFont("Comic Sans MS", 18, bold=True)
def difficulty_screen():
screen.fill(BG_COLOR)
title = big_font.render("ВЫБЕРИТЕ СЛОЖНОСТЬ", True, TEXT_COLOR)
screen.blit(title, title.get_rect(center=(WIDTH // 2, 120)))
easy = font.render("1 - ЛЕГКО (8 FPS)", True, TEXT_COLOR)
medium = font.render("2 - СРЕДНЕ (10 FPS)", True, TEXT_COLOR)
hard = font.render("3 - СЛОЖНО (15 FPS)", True, TEXT_COLOR)
hint = small_font.render("C - подтвердить выбор", True, TEXT_COLOR)
exit_text = small_font.render("ESC - ВЫХОД ИЗ ИГРЫ", True, (200, 80, 80))
screen.blit(easy, easy.get_rect(center=(WIDTH // 2, 250)))
screen.blit(medium, medium.get_rect(center=(WIDTH // 2, 310)))
screen.blit(hard, hard.get_rect(center=(WIDTH // 2, 370)))
screen.blit(hint, hint.get_rect(center=(WIDTH // 2, 450)))
screen.blit(exit_text, exit_text.get_rect(center=(WIDTH // 2, 520)))
pygame.display.flip()
selected = 10
confirmed = False
while not confirmed:
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_1:
selected = 8
easy = font.render("1 - ЛЕГКО (8 FPS) ✓", True, (80, 200, 80))
medium = font.render("2 - СРЕДНЕ (10 FPS)", True, TEXT_COLOR)
hard = font.render("3 - СЛОЖНО (15 FPS)", True, TEXT_COLOR)
screen.fill(BG_COLOR)
screen.blit(title, title.get_rect(center=(WIDTH // 2, 120)))
screen.blit(easy, easy.get_rect(center=(WIDTH // 2, 250)))
screen.blit(medium, medium.get_rect(center=(WIDTH // 2, 310)))
screen.blit(hard, hard.get_rect(center=(WIDTH // 2, 370)))
screen.blit(hint, hint.get_rect(center=(WIDTH // 2, 450)))
screen.blit(exit_text, exit_text.get_rect(center=(WIDTH // 2, 520)))
pygame.display.flip()
elif event.key == pygame.K_2:
selected = 10
easy = font.render("1 - ЛЕГКО (8 FPS)", True, TEXT_COLOR)
medium = font.render("2 - СРЕДНЕ (10 FPS) ✓", True, (80, 200, 80))
hard = font.render("3 - СЛОЖНО (15 FPS)", True, TEXT_COLOR)
screen.fill(BG_COLOR)
screen.blit(title, title.get_rect(center=(WIDTH // 2, 120)))
screen.blit(easy, easy.get_rect(center=(WIDTH // 2, 250)))
screen.blit(medium, medium.get_rect(center=(WIDTH // 2, 310)))
screen.blit(hard, hard.get_rect(center=(WIDTH // 2, 370)))
screen.blit(hint, hint.get_rect(center=(WIDTH // 2, 450)))
screen.blit(exit_text, exit_text.get_rect(center=(WIDTH // 2, 520)))
pygame.display.flip()
elif event.key == pygame.K_3:
selected = 15
easy = font.render("1 - ЛЕГКО (8 FPS)", True, TEXT_COLOR)
medium = font.render("2 - СРЕДНЕ (10 FPS)", True, TEXT_COLOR)
hard = font.render("3 - СЛОЖНО (15 FPS) ✓", True, (80, 200, 80))
screen.fill(BG_COLOR)
screen.blit(title, title.get_rect(center=(WIDTH // 2, 120)))
screen.blit(easy, easy.get_rect(center=(WIDTH // 2, 250)))
screen.blit(medium, medium.get_rect(center=(WIDTH // 2, 310)))
screen.blit(hard, hard.get_rect(center=(WIDTH // 2, 370)))
screen.blit(hint, hint.get_rect(center=(WIDTH // 2, 450)))
screen.blit(exit_text, exit_text.get_rect(center=(WIDTH // 2, 520)))
pygame.display.flip()
elif event.key == pygame.K_c:
confirmed = True
elif event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
return selected
def show_difficulty_menu():
screen.fill(BG_COLOR)
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 200))
screen.blit(overlay, (0, 0))
title = big_font.render("СМЕНА СЛОЖНОСТИ", True, TEXT_COLOR)
screen.blit(title, title.get_rect(center=(WIDTH // 2, 100)))
easy = font.render("1 - ЛЕГКО (8 FPS)", True, TEXT_COLOR)
medium = font.render("2 - СРЕДНЕ (10 FPS)", True, TEXT_COLOR)
hard = font.render("3 - СЛОЖНО (15 FPS)", True, TEXT_COLOR)
confirm = font.render("C - ПОДТВЕРДИТЬ", True, (80, 200, 80))
cancel = font.render("Q - ОТМЕНА", True, (200, 80, 80))
exit_text = font.render("ESC - ВЫХОД ИЗ ИГРЫ", True, (200, 80, 80))
pause = small_font.render("ИГРА НА ПАУЗЕ", True, TEXT_COLOR)
screen.blit(easy, easy.get_rect(center=(WIDTH // 2, 220)))
screen.blit(medium, medium.get_rect(center=(WIDTH // 2, 280)))
screen.blit(hard, hard.get_rect(center=(WIDTH // 2, 340)))
screen.blit(confirm, confirm.get_rect(center=(WIDTH // 2, 410)))
screen.blit(cancel, cancel.get_rect(center=(WIDTH // 2, 460)))
screen.blit(exit_text, exit_text.get_rect(center=(WIDTH // 2, 510)))
screen.blit(pause, pause.get_rect(center=(WIDTH // 2, 560)))
pygame.display.flip()
selected = 10
confirmed = False
while not confirmed:
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_1:
selected = 8
easy = font.render("1 - ЛЕГКО (8 FPS) ✓", True, (80, 200, 80))
medium = font.render("2 - СРЕДНЕ (10 FPS)", True, TEXT_COLOR)
hard = font.render("3 - СЛОЖНО (15 FPS)", True, TEXT_COLOR)
screen.fill(BG_COLOR)
screen.blit(overlay, (0, 0))
screen.blit(title, title.get_rect(center=(WIDTH // 2, 100)))
screen.blit(easy, easy.get_rect(center=(WIDTH // 2, 220)))
screen.blit(medium, medium.get_rect(center=(WIDTH // 2, 280)))
screen.blit(hard, hard.get_rect(center=(WIDTH // 2, 340)))
screen.blit(confirm, confirm.get_rect(center=(WIDTH // 2, 410)))
screen.blit(cancel, cancel.get_rect(center=(WIDTH // 2, 460)))
screen.blit(exit_text, exit_text.get_rect(center=(WIDTH // 2, 510)))
screen.blit(pause, pause.get_rect(center=(WIDTH // 2, 560)))
pygame.display.flip()
elif event.key == pygame.K_2:
selected = 10
easy = font.render("1 - ЛЕГКО (8 FPS)", True, TEXT_COLOR)
medium = font.render("2 - СРЕДНЕ (10 FPS) ✓", True, (80, 200, 80))
hard = font.render("3 - СЛОЖНО (15 FPS)", True, TEXT_COLOR)
screen.fill(BG_COLOR)
screen.blit(overlay, (0, 0))
screen.blit(title, title.get_rect(center=(WIDTH // 2, 100)))
screen.blit(easy, easy.get_rect(center=(WIDTH // 2, 220)))
screen.blit(medium, medium.get_rect(center=(WIDTH // 2, 280)))
screen.blit(hard, hard.get_rect(center=(WIDTH // 2, 340)))
screen.blit(confirm, confirm.get_rect(center=(WIDTH // 2, 410)))
screen.blit(cancel, cancel.get_rect(center=(WIDTH // 2, 460)))
screen.blit(exit_text, exit_text.get_rect(center=(WIDTH // 2, 510)))
screen.blit(pause, pause.get_rect(center=(WIDTH // 2, 560)))
pygame.display.flip()
elif event.key == pygame.K_3:
selected = 15
easy = font.render("1 - ЛЕГКО (8 FPS)", True, TEXT_COLOR)
medium = font.render("2 - СРЕДНЕ (10 FPS)", True, TEXT_COLOR)
hard = font.render("3 - СЛОЖНО (15 FPS) ✓", True, (80, 200, 80))
screen.fill(BG_COLOR)
screen.blit(overlay, (0, 0))
screen.blit(title, title.get_rect(center=(WIDTH // 2, 100)))
screen.blit(easy, easy.get_rect(center=(WIDTH // 2, 220)))
screen.blit(medium, medium.get_rect(center=(WIDTH // 2, 280)))
screen.blit(hard, hard.get_rect(center=(WIDTH // 2, 340)))
screen.blit(confirm, confirm.get_rect(center=(WIDTH // 2, 410)))
screen.blit(cancel, cancel.get_rect(center=(WIDTH // 2, 460)))
screen.blit(exit_text, exit_text.get_rect(center=(WIDTH // 2, 510)))
screen.blit(pause, pause.get_rect(center=(WIDTH // 2, 560)))
pygame.display.flip()
elif event.key == pygame.K_c:
confirmed = True
elif event.key == pygame.K_q:
return None
elif event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
return selected
# ==========================================================
# РОЛЬ 1 — Калашников: Food
# ==========================================================
class Food:
def __init__(self):
self.position = (5, 5)
def spawn(self, snake_body):
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)
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):
for i, (col, row) in enumerate(body):
x = col * CELL
y = row * CELL
color = SNAKE_HEAD_COLOR if i == 0 else SNAKE_COLOR
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, fps):
text = font.render(f"Счёт: {score}", True, TEXT_COLOR)
surface.blit(text, (10, 10))
if fps == 8:
difficulty_text = small_font.render("Сложность: ЛЕГКО", True, TEXT_COLOR)
elif fps == 10:
difficulty_text = small_font.render("Сложность: СРЕДНЕ", True, TEXT_COLOR)
else:
difficulty_text = small_font.render("Сложность: СЛОЖНО", True, TEXT_COLOR)
surface.blit(difficulty_text, (10, 40))
hint_text = small_font.render("C - сменить сложность", True, TEXT_COLOR)
surface.blit(hint_text, (WIDTH - 180, 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 — выход | C — сложность", 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):
if new_dir[0] == -self.direction[0] and new_dir[1] == -self.direction[1]:
return
self.direction = new_dir
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_pending:
self.body.pop()
self.grow_pending = False
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
# ==========================================================
def handle_input(event, snake, restart_callback, game_over, change_difficulty_callback):
if event.type != pygame.KEYDOWN:
return
if event.key == pygame.K_UP or event.key == pygame.K_w:
snake.set_direction((0, -1))
elif event.key == pygame.K_DOWN or event.key == pygame.K_s:
snake.set_direction((0, 1))
elif event.key == pygame.K_LEFT or event.key == pygame.K_a:
snake.set_direction((-1, 0))
elif event.key == pygame.K_RIGHT or event.key == pygame.K_d:
snake.set_direction((1, 0))
elif event.key == pygame.K_r and game_over:
restart_callback()
elif event.key == pygame.K_c:
change_difficulty_callback()
def new_game():
snake = Snake()
food = Food()
food.spawn(snake.body)
return snake, food, 0
def main():
current_fps = difficulty_screen()
snake, food, score = new_game()
renderer = Renderer()
game_over = False
def restart():
nonlocal snake, food, score, game_over
snake, food, score = new_game()
game_over = False
def change_difficulty():
nonlocal current_fps, snake, food, score, game_over
new_fps = show_difficulty_menu()
if new_fps is not None and new_fps != current_fps:
current_fps = new_fps
snake, food, score = new_game()
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 and event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
handle_input(event, snake, restart, game_over, change_difficulty)
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)
if game_over:
renderer.draw_game_over(screen, score)
else:
renderer.draw_score(screen, score, current_fps)
pygame.display.flip()
clock.tick(current_fps)
if __name__ == "__main__":
main()