import pygame import random import json import os from datetime import datetime import math # Инициализация Pygame pygame.init() # Константы SCREEN_WIDTH = 1000 SCREEN_HEIGHT = 700 BLOCK_SIZE = 30 GRID_WIDTH = 10 GRID_HEIGHT = 20 # Вычисляем отступы для центрирования игрового поля GAME_WIDTH = GRID_WIDTH * BLOCK_SIZE # 300px GAME_HEIGHT = GRID_HEIGHT * BLOCK_SIZE # 600px # Левый отступ для игрового поля (200px для левой панели + отступ) LEFT_PANEL_WIDTH = 220 GAME_X = LEFT_PANEL_WIDTH + 20 # Игровое поле начинается после левой панели RIGHT_PANEL_X = GAME_X + GAME_WIDTH + 20 # Правая панель после игрового поля RIGHT_PANEL_WIDTH = SCREEN_WIDTH - RIGHT_PANEL_X - 20 # Цвета - современная палитра COLORS = { 'bg_dark': (15, 20, 30), 'bg_light': (25, 35, 45), 'grid': (40, 50, 65), 'grid_bright': (60, 75, 95), 'text_primary': (220, 230, 245), 'text_secondary': (150, 170, 190), 'text_accent': (100, 200, 255), 'shadow': (0, 0, 0, 100), 'panel_bg': (30, 40, 55, 200), } # Цвета фигур SHAPE_COLORS = [ (0, 200, 255), # I (255, 220, 50), # O (200, 50, 255), # T (255, 150, 50), # L (80, 150, 255), # J (50, 255, 150), # S (255, 80, 100) # Z ] # Формы фигур SHAPES = [ [[1, 1, 1, 1]], [[1, 1], [1, 1]], [[0, 1, 0], [1, 1, 1]], [[1, 0, 0], [1, 1, 1]], [[0, 0, 1], [1, 1, 1]], [[0, 1, 1], [1, 1, 0]], [[1, 1, 0], [0, 1, 1]] ] def get_font(size, bold=False): """Получение шрифта с поддержкой кириллицы""" try: fonts = ["segoeui.ttf", "arial.ttf", "DejaVuSans.ttf"] for font in fonts: try: return pygame.font.Font(font, size) except: continue return pygame.font.SysFont("segoeui", size, bold=bold) except: return pygame.font.Font(None, size) class Particle: def __init__(self, x, y, color): self.x = x self.y = y self.vx = random.uniform(-3, 3) self.vy = random.uniform(-5, -1) self.color = color self.life = 30 self.size = random.randint(2, 5) def update(self): self.x += self.vx self.y += self.vy self.vy += 0.3 self.life -= 1 return self.life > 0 def draw(self, screen): alpha = self.life / 30 color = tuple(int(c * alpha) for c in self.color) pygame.draw.circle(screen, color, (int(self.x), int(self.y)), self.size) class HighScoreManager: def __init__(self): self.filename = "tetris_scores.json" self.scores = self.load_scores() def load_scores(self): if os.path.exists(self.filename): try: with open(self.filename, 'r', encoding='utf-8') as f: return json.load(f) except: return [] return [] def save_scores(self): with open(self.filename, 'w', encoding='utf-8') as f: json.dump(self.scores, f, ensure_ascii=False, indent=2) def add_score(self, score, level): self.scores.append({ 'score': score, 'level': level, 'date': datetime.now().strftime("%d.%m.%Y %H:%M") }) self.scores.sort(key=lambda x: x['score'], reverse=True) self.scores = self.scores[:10] self.save_scores() def get_high_score(self): return self.scores[0]['score'] if self.scores else 0 class Button: def __init__(self, x, y, width, height, text, color, hover_color, text_color=COLORS['text_primary']): self.rect = pygame.Rect(x, y, width, height) self.text = text self.color = color self.hover_color = hover_color self.text_color = text_color self.is_hovered = False self.animation = 0 def draw(self, screen, font): self.animation += (0.1 if self.is_hovered else -0.1) self.animation = max(0, min(1, self.animation)) current_color = self.hover_color if self.is_hovered else self.color shadow_rect = self.rect.inflate(4, 4) pygame.draw.rect(screen, COLORS['shadow'], shadow_rect, border_radius=10) pygame.draw.rect(screen, current_color, self.rect, border_radius=8) pygame.draw.rect(screen, COLORS['text_primary'], self.rect, 2, border_radius=8) if self.is_hovered: glow = pygame.Surface((self.rect.width + 20, self.rect.height + 20)) glow.set_alpha(50) pygame.draw.rect(glow, self.hover_color, glow.get_rect(), border_radius=10) screen.blit(glow, (self.rect.x - 10, self.rect.y - 10)) text_surface = font.render(self.text, True, self.text_color) text_rect = text_surface.get_rect(center=self.rect.center) screen.blit(text_surface, 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 Tetris: def __init__(self): self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption("Тетрис Делюкс") self.clock = pygame.time.Clock() # Шрифты self.font_title = get_font(72, True) self.font_large = get_font(48, True) self.font_medium = get_font(32, True) self.font_small = get_font(20, False) self.high_score_manager = HighScoreManager() self.game_state = "menu" self.particles = [] # Создаём кнопки self.create_buttons() # Сбрасываем игру self.reset_game() self.menu_offset = 0 self.menu_direction = 1 def create_buttons(self): button_width = 250 button_height = 60 center_x = SCREEN_WIDTH // 2 - button_width // 2 self.menu_buttons = [ Button(center_x, 350, button_width, button_height, "Начать игру", (80, 150, 80), (100, 200, 100)), Button(center_x, 430, button_width, button_height, "Управление", (60, 100, 150), (80, 140, 200)), Button(center_x, 510, button_width, button_height, "Рекорды", (100, 80, 120), (140, 110, 170)), Button(center_x, 590, button_width, button_height, "Выход", (150, 60, 60), (200, 80, 80)) ] self.pause_buttons = [ Button(SCREEN_WIDTH//2 - 120, 400, 220, 50, "Продолжить", (80, 150, 80), (100, 200, 100)), Button(SCREEN_WIDTH//2 - 120, 470, 220, 50, "Заново", (60, 100, 150), (80, 140, 200)), Button(SCREEN_WIDTH//2 - 120, 540, 220, 50, "В меню", (100, 80, 120), (140, 110, 170)) ] self.game_over_buttons = [ Button(SCREEN_WIDTH//2 - 120, 450, 240, 55, "Играть снова", (80, 150, 80), (100, 200, 100)), Button(SCREEN_WIDTH//2 - 120, 520, 240, 55, "Главное меню", (60, 100, 150), (80, 140, 200)) ] # Создаём кнопку "Назад" self.back_button = Button(SCREEN_WIDTH//2 - 100, SCREEN_HEIGHT - 80, 200, 50, "Назад", (60, 100, 150), (80, 140, 200)) def reset_game(self): self.grid = [[None for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)] self.current_piece = self.new_piece() self.next_piece = self.new_piece() self.score = 0 self.lines = 0 self.level = 1 self.game_over = False self.paused = False self.fall_time = 0 self.update_fall_speed() def update_fall_speed(self): self.fall_speed = max(80, 500 - (self.level - 1) * 25) def new_piece(self): shape_idx = random.randint(0, len(SHAPES) - 1) shape = [row[:] for row in SHAPES[shape_idx]] color = SHAPE_COLORS[shape_idx] return { 'shape': shape, 'color': color, 'x': GRID_WIDTH // 2 - len(shape[0]) // 2, 'y': 0 } def valid_move(self, piece, x, y): for i, row in enumerate(piece['shape']): for j, cell in enumerate(row): if cell: new_x = x + j new_y = y + i if (new_x < 0 or new_x >= GRID_WIDTH or new_y >= GRID_HEIGHT or (new_y >= 0 and self.grid[new_y][new_x] is not None)): return False return True def merge_piece(self): for i, row in enumerate(self.current_piece['shape']): for j, cell in enumerate(row): if cell: x = (self.current_piece['x'] + j) * BLOCK_SIZE + BLOCK_SIZE//2 y = (self.current_piece['y'] + i) * BLOCK_SIZE + BLOCK_SIZE//2 for _ in range(3): self.particles.append(Particle(x + GAME_X, y, self.current_piece['color'])) for i, row in enumerate(self.current_piece['shape']): for j, cell in enumerate(row): if cell: x = self.current_piece['x'] + j y = self.current_piece['y'] + i if y >= 0: self.grid[y][x] = self.current_piece['color'] self.clear_lines() self.current_piece = self.next_piece self.next_piece = self.new_piece() if not self.valid_move(self.current_piece, self.current_piece['x'], self.current_piece['y']): self.game_over = True self.high_score_manager.add_score(self.score, self.level) def clear_lines(self): lines_cleared = 0 y = GRID_HEIGHT - 1 while y >= 0: if all(self.grid[y][x] is not None for x in range(GRID_WIDTH)): for x in range(GRID_WIDTH): if self.grid[y][x]: for _ in range(5): self.particles.append(Particle( x * BLOCK_SIZE + BLOCK_SIZE//2 + GAME_X, y * BLOCK_SIZE + BLOCK_SIZE//2, self.grid[y][x] )) del self.grid[y] self.grid.insert(0, [None for _ in range(GRID_WIDTH)]) lines_cleared += 1 y += 1 y -= 1 if lines_cleared > 0: self.lines += lines_cleared points = [0, 100, 300, 500, 800] self.score += points[lines_cleared] * self.level new_level = self.lines // 10 + 1 if new_level > self.level: self.level = new_level self.update_fall_speed() def move(self, dx, dy): if self.game_over or self.paused: return new_x = self.current_piece['x'] + dx new_y = self.current_piece['y'] + dy if self.valid_move(self.current_piece, new_x, new_y): self.current_piece['x'] = new_x self.current_piece['y'] = new_y return True elif dy == 1: self.merge_piece() return False def rotate(self): if self.game_over or self.paused: return rotated_shape = list(zip(*self.current_piece['shape'][::-1])) rotated_shape = [list(row) for row in rotated_shape] original_shape = self.current_piece['shape'] self.current_piece['shape'] = rotated_shape if self.valid_move(self.current_piece, self.current_piece['x'], self.current_piece['y']): return True self.current_piece['shape'] = original_shape return False def hard_drop(self): if self.game_over or self.paused: return while self.move(0, 1): pass def toggle_pause(self): """Переключение паузы""" if not self.game_over: self.paused = not self.paused if self.paused: self.game_state = "paused" else: self.game_state = "playing" def draw_left_panel(self): """Отрисовка левой панели с управлением""" panel_rect = pygame.Rect(10, 20, LEFT_PANEL_WIDTH, SCREEN_HEIGHT - 40) pygame.draw.rect(self.screen, COLORS['bg_light'], panel_rect, border_radius=15) pygame.draw.rect(self.screen, COLORS['grid_bright'], panel_rect, 2, border_radius=15) current_y = 30 # Заголовок title = self.font_medium.render("УПРАВЛЕНИЕ", True, COLORS['text_accent']) self.screen.blit(title, (20, current_y)) current_y += 50 # Клавиши управления controls = [ ("← →", "Влево/Вправо"), ("↑", "Поворот"), ("↓", "Ускорение"), ("ПРОБЕЛ", "Сброс вниз"), ("P", "Пауза"), ("ESC", "Главное меню") ] for key, desc in controls: key_text = self.font_small.render(key, True, COLORS['text_accent']) self.screen.blit(key_text, (20, current_y)) desc_text = self.font_small.render(desc, True, COLORS['text_secondary']) self.screen.blit(desc_text, (20, current_y + 20)) current_y += 55 # Дополнительная информация current_y += 20 info_title = self.font_small.render("СОВЕТЫ", True, COLORS['text_accent']) self.screen.blit(info_title, (20, current_y)) current_y += 30 tips = [ "Заполняйте линии", "4 линии = бонус", "Чем выше уровень,", "тем быстрее падение" ] for tip in tips: tip_text = self.font_small.render(tip, True, COLORS['text_secondary']) self.screen.blit(tip_text, (20, current_y)) current_y += 25 def draw_right_panel(self): """Отрисовка правой панели со статистикой""" panel_rect = pygame.Rect(RIGHT_PANEL_X, 20, RIGHT_PANEL_WIDTH, SCREEN_HEIGHT - 40) pygame.draw.rect(self.screen, COLORS['bg_light'], panel_rect, border_radius=15) pygame.draw.rect(self.screen, COLORS['grid_bright'], panel_rect, 2, border_radius=15) current_y = 30 panel_x = RIGHT_PANEL_X # Следующая фигура next_title = self.font_medium.render("СЛЕДУЮЩАЯ", True, COLORS['text_accent']) self.screen.blit(next_title, (panel_x + 20, current_y)) current_y += 40 preview_size = 120 preview_x = panel_x + (RIGHT_PANEL_WIDTH - preview_size) // 2 preview_rect = pygame.Rect(preview_x - 10, current_y - 10, preview_size + 20, preview_size + 20) pygame.draw.rect(self.screen, COLORS['bg_dark'], preview_rect, border_radius=10) shape = self.next_piece['shape'] shape_width = len(shape[0]) * BLOCK_SIZE shape_height = len(shape) * BLOCK_SIZE offset_x = preview_x + (preview_size - shape_width) // 2 offset_y = current_y + (preview_size - shape_height) // 2 for i, row in enumerate(shape): for j, cell in enumerate(row): if cell: rect = pygame.Rect(offset_x + j * BLOCK_SIZE, offset_y + i * BLOCK_SIZE, BLOCK_SIZE - 2, BLOCK_SIZE - 2) pygame.draw.rect(self.screen, self.next_piece['color'], rect) pygame.draw.rect(self.screen, COLORS['text_primary'], rect, 1) current_y += preview_size + 20 # Статистика stats = [ ("СЧЁТ", f"{self.score:,}"), ("ЛИНИИ", str(self.lines)), ("УРОВЕНЬ", str(self.level)), ("РЕКОРД", f"{self.high_score_manager.get_high_score():,}") ] for title, value in stats: title_surf = self.font_small.render(title, True, COLORS['text_secondary']) self.screen.blit(title_surf, (panel_x + 20, current_y)) value_surf = self.font_large.render(value, True, COLORS['text_accent']) self.screen.blit(value_surf, (panel_x + 20, current_y + 30)) current_y += 80 def draw_grid(self): """Отрисовка игрового поля""" # Фон игрового поля game_rect = pygame.Rect(GAME_X, 0, GAME_WIDTH, GAME_HEIGHT) pygame.draw.rect(self.screen, COLORS['bg_dark'], game_rect) # Сетка и фигуры for y in range(GRID_HEIGHT): for x in range(GRID_WIDTH): rect = pygame.Rect(GAME_X + x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1) if self.grid[y][x] is not None: color = self.grid[y][x] pygame.draw.rect(self.screen, color, rect) bright_color = tuple(min(255, c + 50) for c in color) pygame.draw.rect(self.screen, bright_color, rect, 2) else: pygame.draw.rect(self.screen, COLORS['grid'], rect, 1) # Текущая фигура if self.current_piece: for i, row in enumerate(self.current_piece['shape']): for j, cell in enumerate(row): if cell: x = GAME_X + (self.current_piece['x'] + j) * BLOCK_SIZE y = (self.current_piece['y'] + i) * BLOCK_SIZE rect = pygame.Rect(x, y, BLOCK_SIZE - 1, BLOCK_SIZE - 1) pygame.draw.rect(self.screen, self.current_piece['color'], rect) bright = tuple(min(255, c + 50) for c in self.current_piece['color']) pygame.draw.rect(self.screen, bright, rect, 2) def draw_menu(self): self.menu_offset += 0.01 * self.menu_direction if abs(self.menu_offset) > 0.1: self.menu_direction *= -1 self.screen.fill(COLORS['bg_dark']) for i in range(50): alpha = 50 + math.sin(self.menu_offset + i * 0.2) * 30 x = (i * 50) % SCREEN_WIDTH y = (i * 30) % SCREEN_HEIGHT pygame.draw.circle(self.screen, (30, 40, 60), (x, y), 3) title_y = 150 + math.sin(self.menu_offset * 2) * 5 title = self.font_title.render("ТЕТРИС", True, COLORS['text_accent']) title_rect = title.get_rect(center=(SCREEN_WIDTH//2, title_y)) self.screen.blit(title, title_rect) subtitle = self.font_small.render("ДЕЛЮКС", True, COLORS['text_secondary']) subtitle_rect = subtitle.get_rect(center=(SCREEN_WIDTH//2, title_y + 60)) self.screen.blit(subtitle, subtitle_rect) line_y = title_y + 90 pygame.draw.line(self.screen, COLORS['text_accent'], (SCREEN_WIDTH//2 - 200, line_y), (SCREEN_WIDTH//2 + 200, line_y), 2) for button in self.menu_buttons: button.draw(self.screen, self.font_medium) def draw_pause_menu(self): overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT)) overlay.set_alpha(180) overlay.fill(COLORS['bg_dark']) self.screen.blit(overlay, (0, 0)) pause_rect = pygame.Rect(SCREEN_WIDTH//2 - 200, SCREEN_HEIGHT//2 - 150, 400, 300) pygame.draw.rect(self.screen, COLORS['bg_light'], pause_rect, border_radius=15) pygame.draw.rect(self.screen, COLORS['text_accent'], pause_rect, 3, border_radius=15) pause_title = self.font_large.render("ПАУЗА", True, COLORS['text_accent']) title_rect = pause_title.get_rect(center=(SCREEN_WIDTH//2, SCREEN_HEIGHT//2 - 80)) self.screen.blit(pause_title, title_rect) for button in self.pause_buttons: button.draw(self.screen, self.font_small) def draw_game_over(self): overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT)) overlay.set_alpha(200) overlay.fill(COLORS['bg_dark']) self.screen.blit(overlay, (0, 0)) game_over_rect = pygame.Rect(SCREEN_WIDTH//2 - 250, SCREEN_HEIGHT//2 - 200, 500, 400) pygame.draw.rect(self.screen, COLORS['bg_light'], game_over_rect, border_radius=15) pygame.draw.rect(self.screen, (200, 60, 60), game_over_rect, 3, border_radius=15) game_over_text = self.font_large.render("ИГРА ОКОНЧЕНА", True, (255, 100, 100)) text_rect = game_over_text.get_rect(center=(SCREEN_WIDTH//2, SCREEN_HEIGHT//2 - 120)) self.screen.blit(game_over_text, text_rect) result_y = SCREEN_HEIGHT//2 - 40 results = [ f"Счёт: {self.score}", f"Линии: {self.lines}", f"Уровень: {self.level}" ] for result in results: text = self.font_medium.render(result, True, COLORS['text_primary']) self.screen.blit(text, (SCREEN_WIDTH//2 - text.get_width()//2, result_y)) result_y += 50 for button in self.game_over_buttons: button.draw(self.screen, self.font_small) def draw_controls(self): self.screen.fill(COLORS['bg_dark']) title = self.font_large.render("УПРАВЛЕНИЕ", True, COLORS['text_accent']) title_rect = title.get_rect(center=(SCREEN_WIDTH//2, 60)) self.screen.blit(title, title_rect) controls = [ ("← →", "Движение влево/вправо"), ("↑", "Поворот фигуры"), ("↓", "Ускоренное падение"), ("ПРОБЕЛ", "Мгновенный сброс"), ("P", "Пауза"), ("ESC", "Главное меню") ] y = 150 for key, desc in controls: key_surf = self.font_medium.render(key, True, COLORS['text_accent']) self.screen.blit(key_surf, (SCREEN_WIDTH//2 - 150, y)) desc_surf = self.font_small.render(desc, True, COLORS['text_primary']) self.screen.blit(desc_surf, (SCREEN_WIDTH//2 - 20, y + 5)) y += 60 if self.back_button: self.back_button.draw(self.screen, self.font_small) def draw_high_scores(self): self.screen.fill(COLORS['bg_dark']) title = self.font_large.render("ТАБЛИЦА РЕКОРДОВ", True, COLORS['text_accent']) title_rect = title.get_rect(center=(SCREEN_WIDTH//2, 60)) self.screen.blit(title, title_rect) if not self.high_score_manager.scores: no_scores = self.font_medium.render("Пока нет рекордов!", True, COLORS['text_secondary']) no_rect = no_scores.get_rect(center=(SCREEN_WIDTH//2, 300)) self.screen.blit(no_scores, no_rect) else: headers = ["#", "Счёт", "Уровень", "Дата"] x_positions = [150, 350, 550, 650] for i, header in enumerate(headers): text = self.font_medium.render(header, True, COLORS['text_accent']) self.screen.blit(text, (x_positions[i], 130)) y = 190 for i, score_data in enumerate(self.high_score_manager.scores[:10], 1): if i == 1: color = (255, 215, 0) else: color = COLORS['text_primary'] rank_text = self.font_medium.render(str(i), True, color) self.screen.blit(rank_text, (x_positions[0], y)) score_text = self.font_medium.render(f"{score_data['score']:,}", True, color) self.screen.blit(score_text, (x_positions[1], y)) level_text = self.font_medium.render(str(score_data['level']), True, COLORS['text_primary']) self.screen.blit(level_text, (x_positions[2], y)) date_text = self.font_small.render(score_data['date'], True, COLORS['text_secondary']) self.screen.blit(date_text, (x_positions[3], y)) y += 45 if self.back_button: self.back_button.draw(self.screen, self.font_small) def run(self): running = True while running: current_time = pygame.time.get_ticks() if self.game_state == "playing" and not self.game_over and not self.paused: if current_time - self.fall_time > self.fall_speed: self.move(0, 1) self.fall_time = current_time for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if event.type == pygame.KEYDOWN: if self.game_state == "playing": if event.key == pygame.K_LEFT: self.move(-1, 0) elif event.key == pygame.K_RIGHT: self.move(1, 0) elif event.key == pygame.K_DOWN: self.move(0, 1) elif event.key == pygame.K_UP: self.rotate() elif event.key == pygame.K_SPACE: self.hard_drop() elif event.key == pygame.K_p: self.toggle_pause() elif event.key == pygame.K_ESCAPE: self.game_state = "menu" self.paused = False elif self.game_state == "menu" and event.key == pygame.K_RETURN: self.reset_game() self.game_state = "playing" elif self.game_state == "game_over": if event.key == pygame.K_r: self.reset_game() self.game_state = "playing" elif event.key == pygame.K_ESCAPE: self.game_state = "menu" elif self.game_state == "paused": if event.key == pygame.K_p: self.toggle_pause() elif event.key == pygame.K_ESCAPE: self.game_state = "menu" self.paused = False # Обработка кнопок меню if self.game_state == "menu": for i, button in enumerate(self.menu_buttons): if button.handle_event(event): if i == 0: self.reset_game() self.game_state = "playing" elif i == 1: self.game_state = "controls" elif i == 2: self.game_state = "high_scores" elif i == 3: running = False elif self.game_state == "paused": for i, button in enumerate(self.pause_buttons): if button.handle_event(event): if i == 0: # Продолжить self.toggle_pause() elif i == 1: # Заново self.reset_game() self.game_state = "playing" self.paused = False elif i == 2: # В меню self.game_state = "menu" self.paused = False elif self.game_state == "game_over": for i, button in enumerate(self.game_over_buttons): if button.handle_event(event): if i == 0: # Играть снова self.reset_game() self.game_state = "playing" elif i == 1: # Главное меню self.game_state = "menu" elif self.game_state == "controls": if self.back_button and self.back_button.handle_event(event): self.game_state = "menu" elif self.game_state == "high_scores": if self.back_button and self.back_button.handle_event(event): self.game_state = "menu" # Обновление частиц self.particles = [p for p in self.particles if p.update()] # Отрисовка if self.game_state == "menu": self.draw_menu() elif self.game_state == "playing": self.screen.fill(COLORS['bg_dark']) self.draw_left_panel() self.draw_grid() self.draw_right_panel() if self.game_over: self.draw_game_over() self.game_state = "game_over" elif self.paused: self.draw_pause_menu() elif self.game_state == "paused": # В состоянии паузы продолжаем отрисовывать игровое поле self.screen.fill(COLORS['bg_dark']) self.draw_left_panel() self.draw_grid() self.draw_right_panel() self.draw_pause_menu() elif self.game_state == "game_over": self.screen.fill(COLORS['bg_dark']) self.draw_left_panel() self.draw_grid() self.draw_right_panel() self.draw_game_over() elif self.game_state == "controls": self.draw_controls() elif self.game_state == "high_scores": self.draw_high_scores() for particle in self.particles: particle.draw(self.screen) pygame.display.flip() self.clock.tick(60) pygame.quit() if __name__ == "__main__": game = Tetris() game.run()