Python通过Pygame实现一个经典扫雷游戏

2026-06-27编程代码255409

Pygame 是 Python 中一个功能强大的 2D 游戏开发库,它提供了处理图形、声音、输入和事件循环的完整工具集,本文通过 Pygame 实现一个经典扫雷游戏项目,需要的朋友可以参考下

目录
  • 项目概述
  • 游戏实现
    • 初始化与基础设置
      • 游戏状态枚举
    • 核心类设计
      • 1. 格子类(Cell)
      • 2. 游戏主类(MineSweeperGame)
  • 全部代码

    项目概述

    Pygame 是 Python 中一个功能强大的 2D 游戏开发库,它提供了处理图形、声音、输入和事件循环的完整工具集。本文通过 Pygame 实现一个经典扫雷游戏项目。

    在这个游戏中,玩家需要在一个网格中找出所有地雷而不触发它们。玩家点击格子揭示内容,数字表示周围 8 个相邻格子中地雷的数量。其中:

    • 标记:经典操作模式,左键点击:揭示格子,翻开指定格子,查看内容;右键点击:标记/取消标记,对疑似地雷的格子添加或移除旗帜标记。
    • 揭示:中键点击:对已揭示的数字格,若周围旗帜数=数字,自动揭示其余格子。
    • 键盘操作:R:重新开始游戏;M:切换难度。
    • 胜利条件:所有「非地雷格子」都被揭示 = 游戏胜利。
    • 失败条件:揭示了一个「地雷格子」 = 游戏失败。
    • 首次点击保护:第一次点击永远不会踩雷,地雷在点击后动态生成。

    游戏实现

    初始化与基础设置

    在游戏启动前,我们需要初始化 Pygame 及其子模块,并定义游戏所需的基础参数。

    # 初始化 Pygame
    pygame.init()
    
    # 难度配置:(行数, 列数, 地雷数)
    DIFFICULTIES = {
        'easy': (9, 9, 10),
        'medium': (16, 16, 40),
        'hard': (16, 30, 99)
    }
    
    # 默认难度
    CURRENT_DIFFICULTY = 'medium'
    ROWS, COLS, MINE_COUNT = DIFFICULTIES[CURRENT_DIFFICULTY]
    
    # 屏幕设置
    CELL_SIZE = 30
    UI_HEIGHT = 60
    WIDTH = COLS * CELL_SIZE
    HEIGHT = ROWS * CELL_SIZE + UI_HEIGHT
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("Pygame Minesweeper")
    
    # 颜色定义(经典扫雷风格)
    COLORS = {
        'bg': (192, 192, 192),
        'cell': (210, 210, 210),
        'cell_revealed': (180, 180, 180),
        'border_light': (255, 255, 255),
        'border_dark': (128, 128, 128),
        'mine': (0, 0, 0),
        'flag': (255, 0, 0),
        'wrong_flag': (255, 100, 100),
        'text': {
            1: (0, 0, 255),    # 蓝
            2: (0, 128, 0),    # 绿
            3: (255, 0, 0),    # 红
            4: (0, 0, 128),    # 深蓝
            5: (128, 0, 0),    # 棕
            6: (0, 128, 128),  # 青
            7: (0, 0, 0),      # 黑
            8: (128, 128, 128) # 灰
        }
    }
    

    字体加载函数

    def load_safe_font(size, bold=False, italic=False):
        """加载字体"""
        try:
            return pygame.font.SysFont('arial', size, bold=bold, italic=italic)
        except (TypeError, OSError, AttributeError):
            try:
                font_dir = os.path.join(os.environ.get("WINDIR", "C:\\Windows"), "Fonts")
                font_path = os.path.join(font_dir, "arial.ttf")
                if os.path.exists(font_path):
                    f = pygame.font.Font(font_path, size)
                    if bold:
                        f.set_bold(True)
                    if italic:
                        f.set_italic(True)
                    return f
            except Exception:
                pass
            # 最终回退到默认字体
            f = pygame.font.Font(None, size)
            if bold:
                f.set_bold(True)
            if italic:
                f.set_italic(True)
            return f
    
    # 初始化字体
    font = load_safe_font(20, bold=True)
    big_font = load_safe_font(36, bold=True)
    small_font = load_safe_font(16)
    ui_font = load_safe_font(18)
    

    该函数采用三级降级策略

    1. 优先使用 SysFont 加载系统字体
    2. 失败时尝试直接读取 Windows 字体目录
    3. 最终回退到 pygame 内置默认字体

    确保游戏在不同环境(Windows/Linux/macOS)和 pygame 版本下都能正常显示文字。

    游戏状态枚举

    from enum import Enum
    
    class GameState(Enum):
        READY = 1      # 游戏准备中(首次点击前)
        PLAYING = 2    # 游戏进行中
        WON = 3        # 游戏胜利
        LOST = 4       # 游戏失败
    

    使用枚举管理游戏状态,使状态判断更清晰、类型更安全。

    核心类设计

    1. 格子类(Cell)

    Cell 类封装了扫雷中每个格子的所有状态和行为。

    构造函数 __init__

    def __init__(self, row, col):
        self.row = row                    # 行索引
        self.col = col                    # 列索引
        self.is_mine = False              # 是否为地雷
        self.is_revealed = False          # 是否已被揭示
        self.is_flagged = False           # 是否被标记为旗帜
        self.is_wrong_flag = False        # 是否错误标记(游戏结束时显示)
        self.adjacent_mines = 0           # 周围地雷数量(0-8)
    

    计算周围地雷数 count_adjacent_mines

    def count_adjacent_mines(self, grid):
        """计算周围8格的地雷数量"""
        count = 0
        for dr in [-1, 0, 1]:
            for dc in [-1, 0, 1]:
                if dr == 0 and dc == 0:
                    continue
                nr, nc = self.row + dr, self.col + dc
                if 0 <= nr < len(grid) and 0 <= nc < len(grid[0]):
                    if grid[nr][nc].is_mine:
                        count += 1
        self.adjacent_mines = count
    

    遍历周围 8 个方向,统计地雷数量并存储。

    揭示格子 reveal

    def reveal(self):
        """揭示格子"""
        if self.is_revealed or self.is_flagged:
            return
        self.is_revealed = True
    

    标记格子为已揭示,并避免重复操作。

    绘制方法 draw

    def draw(self, surface, cell_size, offset_x, offset_y, game_state):
        x = offset_x + self.col * cell_size
        y = offset_y + self.row * cell_size
        
        # 绘制格子背景(3D边框效果)
        if self.is_revealed:
            pygame.draw.rect(surface, COLORS['cell_revealed'], 
                            (x, y, cell_size, cell_size))
        else:
            pygame.draw.rect(surface, COLORS['cell'], 
                            (x, y, cell_size, cell_size))
            # 绘制亮/暗边框营造立体感
            pygame.draw.line(surface, COLORS['border_light'], (x, y), (x + cell_size, y), 2)
            pygame.draw.line(surface, COLORS['border_light'], (x, y), (x, y + cell_size), 2)
            pygame.draw.line(surface, COLORS['border_dark'], (x + cell_size, y), (x + cell_size, y + cell_size), 2)
            pygame.draw.line(surface, COLORS['border_dark'], (x, y + cell_size), (x + cell_size, y + cell_size), 2)
        
        # 绘制内容:地雷/数字/旗帜
        if self.is_revealed:
            if self.is_mine:
                # 绘制地雷(圆形+十字)
                cx, cy = x + cell_size//2, y + cell_size//2
                pygame.draw.circle(surface, COLORS['mine'], (cx, cy), cell_size//3)
                pygame.draw.line(surface, COLORS['flag'], (cx-8, cy-8), (cx+8, cy+8), 2)
                pygame.draw.line(surface, COLORS['flag'], (cx+8, cy-8), (cx-8, cy+8), 2)
            elif self.adjacent_mines > 0:
                color = COLORS['text'].get(self.adjacent_mines, (0, 0, 0))
                text = font.render(str(self.adjacent_mines), True, color)
                text_rect = text.get_rect(center=(x + cell_size//2, y + cell_size//2))
                surface.blit(text, text_rect)
        elif self.is_flagged:
            # 绘制旗帜(旗杆+旗面)
            color = COLORS['wrong_flag'] if (self.is_wrong_flag and game_state == GameState.LOST) else COLORS['flag']
            pygame.draw.line(surface, (100, 100, 100), 
                            (x + cell_size//3, y + cell_size//4), 
                            (x + cell_size//3, y + 3*cell_size//4), 2)
            pygame.draw.polygon(surface, color, [
                (x + cell_size//3, y + cell_size//4),
                (x + cell_size//3 + 12, y + cell_size//3),
                (x + cell_size//3, y + cell_size//2 - 2)
            ])
    

    2. 游戏主类(MineSweeperGame)

    MineSweeperGame 类是整个游戏的控制中心,负责初始化、状态管理、事件处理、逻辑更新和渲染。

    构造函数 __init__

    def __init__(self, rows=16, cols=16, mines=40):
        self.rows = rows
        self.cols = cols
        self.mine_count = mines
        self.cell_size = CELL_SIZE
        self.offset_x = 0
        self.offset_y = UI_HEIGHT
        
        self.grid = []           # 格子二维数组
        self.first_click = True  # 是否首次点击(用于保证首次安全)
        self.state = GameState.READY
        self.start_time = None
        self.elapsed_time = 0
        self.flags_placed = 0
        
        self.create_grid()
    

    创建网格 create_grid

    def create_grid(self):
        """创建空白网格"""
        self.grid = []
        for r in range(self.rows):
            row = [Cell(r, c) for c in range(self.cols)]
            self.grid.append(row)
    

    生成地雷 place_mines

    def place_mines(self, exclude_row, exclude_col):
        """排除首次点击位置后随机放置地雷"""
        positions = [(r, c) for r in range(self.rows) for c in range(self.cols) 
                     if not (r == exclude_row and c == exclude_col)]
        mine_positions = random.sample(positions, self.mine_count)
        
        for r, c in mine_positions:
            self.grid[r][c].is_mine = True
        
        # 计算每个格子的相邻地雷数
        for row in self.grid:
            for cell in row:
                cell.count_adjacent_mines(self.grid)
    

    扩散算法 flood_fill

    def flood_fill(self, start_row, start_col):
        """BFS扩散揭示空白区域:揭示所有相连的空白格+边缘数字格"""
        queue = deque([(start_row, start_col)])
        visited = set()
        
        while queue:
            r, c = queue.popleft()
            if (r, c) in visited:
                continue
            visited.add((r, c))
            
            cell = self.grid[r][c]
            
            # 只跳过被标记的格子,允许已揭示的格子继续扩散
            if cell.is_flagged:
                continue
            
            # 揭示当前格子(如果已揭示,reveal() 会直接返回,无副作用)
            cell.reveal()
            
            # 只有空白格子(相邻地雷数=0)才继续扩散周围
            if cell.adjacent_mines == 0 and not cell.is_mine:
                for dr in [-1, 0, 1]:
                    for dc in [-1, 0, 1]:
                        if dr == 0 and dc == 0:
                            continue
                        nr, nc = r + dr, c + dc
                        if 0 <= nr < self.rows and 0 <= nc < self.cols:
                            neighbor = self.grid[nr][nc]
                            # 只将未揭示且未标记的邻居加入队列
                            if not neighbor.is_revealed and not neighbor.is_flagged:
                                queue.append((nr, nc))
    

    检查胜利条件 check_win

    def check_win(self):
        """检查是否所有非地雷格子都被揭示"""
        for row in self.grid:
            for cell in row:
                if not cell.is_mine and not cell.is_revealed:
                    return False
        return True
    

    快速揭示 chord_reveal

    def chord_reveal(self, row, col):
        """双击已揭示数字:若旗帜数=数字,揭示其余格子"""
        cell = self.grid[row][col]
        if not cell.is_revealed or cell.adjacent_mines == 0:
            return
        
        flag_count = 0
        to_reveal = []
        
        for dr in [-1, 0, 1]:
            for dc in [-1, 0, 1]:
                if dr == 0 and dc == 0:
                    continue
                nr, nc = row + dr, col + dc
                if 0 <= nr < self.rows and 0 <= nc < self.cols:
                    neighbor = self.grid[nr][nc]
                    if neighbor.is_flagged:
                        flag_count += 1
                    elif not neighbor.is_revealed:
                        to_reveal.append((nr, nc))
        
        if flag_count == cell.adjacent_mines:
            for nr, nc in to_reveal:
                target = self.grid[nr][nc]
                if target.is_mine:
                    self.state = GameState.LOST
                    self.reveal_all_mines()
                    return
                target.reveal()
                if target.adjacent_mines == 0 and not target.is_mine:
                    self.flood_fill(nr, nc)
            if self.check_win():
                self.state = GameState.WON
                self.mark_correct_flags()
    

    事件处理方法 handle_events

    def handle_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return False
            
            if event.type == pygame.MOUSEBUTTONDOWN:
                if self.state in [GameState.READY, GameState.PLAYING]:
                    mx, my = pygame.mouse.get_pos()
                    col = (mx - self.offset_x) // self.cell_size
                    row = (my - self.offset_y) // self.cell_size
                    
                    if 0 <= row < self.rows and 0 <= col  0:
                                self.chord_reveal(row, col)
            
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_r:      # R: 重开
                    self.__init__(self.rows, self.cols, self.mine_count)
                elif event.key == pygame.K_m:    # M: 切换难度
                    self.change_difficulty()
                elif event.key == pygame.K_ESCAPE:  # ESC: 退出
                    return False
        
        return True
    

    绘制方法 draw

    def draw(self):
        screen.fill(COLORS['bg'])
        
        # 绘制所有格子
        for row in self.grid:
            for cell in row:
                cell.draw(screen, self.cell_size, self.offset_x, self.offset_y, self.state)
        
        # 绘制顶部信息栏
        self.draw_ui()
        
        # 游戏状态遮罩
        if self.state == GameState.WON:
            self.draw_overlay("VICTORY!", "Time: " + str(self.elapsed_time) + "s",
                             (100, 255, 100), (200, 255, 200))
        elif self.state == GameState.LOST:
            self.draw_overlay("BOOM!", "Better luck next time!",
                             (255, 100, 100), (255, 200, 200))
        
        pygame.display.flip()
    

    主循环方法 run

    def run(self):
        clock = pygame.time.Clock()
        running = True
        while running:
            running = self.handle_events()
            self.draw()
            clock.tick(60)
        
        pygame.quit()
        sys.exit()
    

    全部代码

    import pygame
    import random
    import sys
    import os
    from enum import Enum
    from collections import deque
    
    # ==================== 初始化配置 ====================
    pygame.init()
    
    # 难度配置:(行数, 列数, 地雷数)
    DIFFICULTIES = {
        'easy': (9, 9, 10),
        'medium': (16, 16, 40),
        'hard': (16, 30, 99)
    }
    
    # 默认难度
    CURRENT_DIFFICULTY = 'medium'
    ROWS, COLS, MINE_COUNT = DIFFICULTIES[CURRENT_DIFFICULTY]
    
    CELL_SIZE = 30
    UI_HEIGHT = 60
    WIDTH = COLS * CELL_SIZE
    HEIGHT = ROWS * CELL_SIZE + UI_HEIGHT
    
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("Pygame Minesweeper")
    
    # 颜色定义(经典扫雷风格)
    COLORS = {
        'bg': (192, 192, 192),
        'cell': (210, 210, 210),
        'cell_revealed': (180, 180, 180),
        'border_light': (255, 255, 255),
        'border_dark': (128, 128, 128),
        'mine': (0, 0, 0),
        'flag': (255, 0, 0),
        'wrong_flag': (255, 100, 100),
        'text': {
            1: (0, 0, 255),  # Blue
            2: (0, 128, 0),  # Green
            3: (255, 0, 0),  # Red
            4: (0, 0, 128),  # Dark Blue
            5: (128, 0, 0),  # Brown
            6: (0, 128, 128),  # Teal
            7: (0, 0, 0),  # Black
            8: (128, 128, 128)  # Gray
        }
    }
    
    
    # ==================== 字体加载函数 ====================
    def load_safe_font(size, bold=False, italic=False):
        """安全加载字体,修复 pygame 2.6+ 字体初始化 bug"""
        try:
            return pygame.font.SysFont('arial', size, bold=bold, italic=italic)
        except (TypeError, OSError, AttributeError):
            try:
                font_dir = os.path.join(os.environ.get("WINDIR", "C:\\Windows"), "Fonts")
                font_path = os.path.join(font_dir, "arial.ttf")
                if os.path.exists(font_path):
                    f = pygame.font.Font(font_path, size)
                    if bold:
                        f.set_bold(True)
                    if italic:
                        f.set_italic(True)
                    return f
            except Exception:
                pass
            f = pygame.font.Font(None, size)
            if bold:
                f.set_bold(True)
            if italic:
                f.set_italic(True)
            return f
    
    
    # 初始化字体
    font = load_safe_font(20, bold=True)
    big_font = load_safe_font(36, bold=True)
    small_font = load_safe_font(16)
    ui_font = load_safe_font(18)
    
    
    # ==================== 游戏状态枚举 ====================
    class GameState(Enum):
        READY = 1
        PLAYING = 2
        WON = 3
        LOST = 4
    
    
    # ==================== 格子类 ====================
    class Cell:
        def __init__(self, row, col):
            self.row = row
            self.col = col
            self.is_mine = False
            self.is_revealed = False
            self.is_flagged = False
            self.is_wrong_flag = False
            self.adjacent_mines = 0
    
        def count_adjacent_mines(self, grid):
            """计算周围8格的地雷数量"""
            count = 0
            for dr in [-1, 0, 1]:
                for dc in [-1, 0, 1]:
                    if dr == 0 and dc == 0:
                        continue
                    nr, nc = self.row + dr, self.col + dc
                    if 0 <= nr < len(grid) and 0 <= nc  0:
                    color = COLORS['text'].get(self.adjacent_mines, (0, 0, 0))
                    text = font.render(str(self.adjacent_mines), True, color)
                    text_rect = text.get_rect(center=(x + cell_size // 2, y + cell_size // 2))
                    surface.blit(text, text_rect)
            elif self.is_flagged:
                color = COLORS['wrong_flag'] if (self.is_wrong_flag and game_state == GameState.LOST) else COLORS['flag']
                pygame.draw.line(surface, (100, 100, 100),
                                 (x + cell_size // 3, y + cell_size // 4),
                                 (x + cell_size // 3, y + 3 * cell_size // 4), 2)
                pygame.draw.polygon(surface, color, [
                    (x + cell_size // 3, y + cell_size // 4),
                    (x + cell_size // 3 + 12, y + cell_size // 3),
                    (x + cell_size // 3, y + cell_size // 2 - 2)
                ])
    
    
    # ==================== 游戏主类 ====================
    class MineSweeperGame:
        def __init__(self, rows=16, cols=16, mines=40):
            self.rows = rows
            self.cols = cols
            self.mine_count = mines
            self.cell_size = CELL_SIZE
            self.offset_x = 0
            self.offset_y = UI_HEIGHT
    
            self.grid = []
            self.first_click = True
            self.state = GameState.READY
            self.start_time = None
            self.elapsed_time = 0
            self.flags_placed = 0
    
            self.create_grid()
    
        def create_grid(self):
            """创建空白网格"""
            self.grid = []
            for r in range(self.rows):
                row = [Cell(r, c) for c in range(self.cols)]
                self.grid.append(row)
    
        def place_mines(self, exclude_row, exclude_col):
            """排除首次点击位置后随机放置地雷"""
            positions = [(r, c) for r in range(self.rows) for c in range(self.cols)
                         if not (r == exclude_row and c == exclude_col)]
            mine_positions = random.sample(positions, self.mine_count)
    
            for r, c in mine_positions:
                self.grid[r][c].is_mine = True
    
            for row in self.grid:
                for cell in row:
                    cell.count_adjacent_mines(self.grid)
    
        def flood_fill(self, start_row, start_col):
            """BFS扩散揭示空白区域:揭示所有相连的空白格+边缘数字格"""
            queue = deque([(start_row, start_col)])
            visited = set()
    
            while queue:
                r, c = queue.popleft()
                if (r, c) in visited:
                    continue
                visited.add((r, c))
    
                cell = self.grid[r][c]
    
                # 只跳过被标记的格子,允许已揭示的格子继续扩散
                if cell.is_flagged:
                    continue
    
                # 揭示当前格子(如果已揭示,reveal() 会直接返回,无副作用)
                cell.reveal()
    
                # 只有空白格子(相邻地雷数=0)才继续扩散周围
                if cell.adjacent_mines == 0 and not cell.is_mine:
                    for dr in [-1, 0, 1]:
                        for dc in [-1, 0, 1]:
                            if dr == 0 and dc == 0:
                                continue
                            nr, nc = r + dr, c + dc
                            if 0 <= nr < self.rows and 0 <= nc < self.cols:
                                neighbor = self.grid[nr][nc]
                                # 只将未揭示且未标记的邻居加入队列
                                if not neighbor.is_revealed and not neighbor.is_flagged:
                                    queue.append((nr, nc))
    
        def check_win(self):
            """检查是否所有非地雷格子都被揭示"""
            for row in self.grid:
                for cell in row:
                    if not cell.is_mine and not cell.is_revealed:
                        return False
            return True
    
        def reveal_all_mines(self):
            """游戏失败时揭示所有地雷"""
            for row in self.grid:
                for cell in row:
                    if cell.is_mine:
                        cell.is_revealed = True
                    elif cell.is_flagged and not cell.is_mine:
                        cell.is_wrong_flag = True
    
        def mark_correct_flags(self):
            """游戏胜利时自动标记剩余地雷"""
            for row in self.grid:
                for cell in row:
                    if cell.is_mine and not cell.is_flagged:
                        cell.is_flagged = True
    
        def chord_reveal(self, row, col):
            """双击已揭示数字:若旗帜数=数字,揭示其余格子"""
            cell = self.grid[row][col]
            if not cell.is_revealed or cell.adjacent_mines == 0:
                return
    
            flag_count = 0
            to_reveal = []
    
            for dr in [-1, 0, 1]:
                for dc in [-1, 0, 1]:
                    if dr == 0 and dc == 0:
                        continue
                    nr, nc = row + dr, col + dc
                    if 0 <= nr < self.rows and 0 <= nc < self.cols:
                        neighbor = self.grid[nr][nc]
                        if neighbor.is_flagged:
                            flag_count += 1
                        elif not neighbor.is_revealed:
                            to_reveal.append((nr, nc))
    
            if flag_count == cell.adjacent_mines:
                for nr, nc in to_reveal:
                    target = self.grid[nr][nc]
                    if target.is_mine:
                        self.state = GameState.LOST
                        self.reveal_all_mines()
                        return
                    target.reveal()
                    if target.adjacent_mines == 0 and not target.is_mine:
                        self.flood_fill(nr, nc)
                if self.check_win():
                    self.state = GameState.WON
                    self.mark_correct_flags()
    
        def draw_ui(self):
            """绘制顶部信息栏"""
            pygame.draw.rect(screen, (80, 80, 90), (0, 0, WIDTH, UI_HEIGHT))
            pygame.draw.line(screen, (255, 255, 255), (0, UI_HEIGHT), (WIDTH, UI_HEIGHT), 2)
    
            if self.start_time and self.state == GameState.PLAYING:
                self.elapsed_time = (pygame.time.get_ticks() - self.start_time) // 1000
    
            time_text = ui_font.render("Time: " + str(self.elapsed_time) + "s", True, (255, 255, 255))
            screen.blit(time_text, (10, 20))
    
            remaining = max(0, self.mine_count - self.flags_placed)
            mine_text = ui_font.render("Mines: " + str(remaining), True, (255, 80, 80))
            mine_width = mine_text.get_width()
            screen.blit(mine_text, (WIDTH - mine_width - 10, 20))
    
            diff_text = ui_font.render("[" + CURRENT_DIFFICULTY.upper() + "]", True, (150, 200, 255))
            diff_width = diff_text.get_width()
            screen.blit(diff_text, (WIDTH // 2 - diff_width // 2, 20))
    
            if self.state == GameState.READY:
                status = ui_font.render("Click to Start", True, (200, 200, 200))
            elif self.state == GameState.PLAYING:
                status = ui_font.render("Good Luck!", True, (150, 255, 150))
            elif self.state == GameState.WON:
                status = ui_font.render("YOU WIN!", True, (100, 255, 100))
            else:
                status = ui_font.render("GAME OVER", True, (255, 100, 100))
    
            status_width = status.get_width()
            screen.blit(status, (WIDTH // 2 - status_width // 2, 40))
    
        def draw_overlay(self, title, subtitle, title_color, subtitle_color):
            """绘制游戏结束遮罩"""
            overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            overlay.fill((0, 0, 0, 160))
            screen.blit(overlay, (0, 0))
    
            title_surf = big_font.render(title, True, title_color)
            subtitle_surf = font.render(subtitle, True, subtitle_color)
            hint_surf = ui_font.render("Press [R] Restart | [M] Change Difficulty", True, (200, 200, 200))
    
            screen.blit(title_surf, (WIDTH // 2 - title_surf.get_width() // 2, HEIGHT // 2 - 60))
            screen.blit(subtitle_surf, (WIDTH // 2 - subtitle_surf.get_width() // 2, HEIGHT // 2 - 10))
            screen.blit(hint_surf, (WIDTH // 2 - hint_surf.get_width() // 2, HEIGHT // 2 + 40))
    
        def change_difficulty(self):
            """循环切换难度"""
            global CURRENT_DIFFICULTY, ROWS, COLS, MINE_COUNT
            diff_list = list(DIFFICULTIES.keys())
            idx = diff_list.index(CURRENT_DIFFICULTY)
            CURRENT_DIFFICULTY = diff_list[(idx + 1) % len(diff_list)]
            ROWS, COLS, MINE_COUNT = DIFFICULTIES[CURRENT_DIFFICULTY]
            self.__init__(ROWS, COLS, MINE_COUNT)
    
        def handle_events(self):
            """处理输入事件"""
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return False
    
                if event.type == pygame.MOUSEBUTTONDOWN:
                    if self.state in [GameState.READY, GameState.PLAYING]:
                        mx, my = pygame.mouse.get_pos()
                        col = (mx - self.offset_x) // self.cell_size
                        row = (my - self.offset_y) // self.cell_size
    
                        if 0 <= row < self.rows and 0 <= col  0:
                                    self.chord_reveal(row, col)
    
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_r:
                        self.__init__(self.rows, self.cols, self.mine_count)
                    elif event.key == pygame.K_m:
                        self.change_difficulty()
                    elif event.key == pygame.K_ESCAPE:
                        return False
    
            return True
    
        def draw(self):
            """绘制整个游戏画面"""
            screen.fill(COLORS['bg'])
    
            for row in self.grid:
                for cell in row:
                    cell.draw(screen, self.cell_size, self.offset_x, self.offset_y, self.state)
    
            self.draw_ui()
    
            if self.state == GameState.WON:
                self.draw_overlay("VICTORY!", "Time: " + str(self.elapsed_time) + "s",
                                  (100, 255, 100), (200, 255, 200))
            elif self.state == GameState.LOST:
                self.draw_overlay("BOOM!", "Better luck next time!",
                                  (255, 100, 100), (255, 200, 200))
    
            pygame.display.flip()
    
        def run(self):
            """游戏主循环"""
            clock = pygame.time.Clock()
            running = True
            while running:
                running = self.handle_events()
                self.draw()
                clock.tick(60)
    
            pygame.quit()
            sys.exit()
    
    
    # ==================== 程序入口 ====================
    if __name__ == "__main__":
        print("Pygame Minesweeper - Classic Edition")
        print("Controls:")
        print("  Left Click  : Reveal cell")
        print("  Right Click : Place/Remove flag")
        print("  Middle Click / Ctrl+Left: Quick reveal around number")
        print("  [R]  : Restart game")
        print("  [M]  : Change difficulty (Easy/Medium/Hard)")
        print("  [ESC]: Exit")
        print("-" * 40)
    
        game = MineSweeperGame(ROWS, COLS, MINE_COUNT)
        game.run()
    

    以上就是Python通过Pygame实现一个经典扫雷游戏的详细内容,更多关于Python Pygame扫雷游戏的资料请关注北冥有鱼【www.ufcn.cn】其它相关文章!

    您可能感兴趣的文章:

    • Python通过Pygame实现一个简化版弹幕射击躲避游戏
    • Python使用Pygame实现一个经典2048数字合并游戏
    • Python基于Pygame开发的经典打砖块游戏
    • Python利用Pygame实现一个经典贪吃蛇游戏
    • Python基于Pygame开发的记忆训练小游戏
    本文地址:https://www.ufcn.cn/article/2425993.html

    如非特殊说明,本站内容均来自于网友自主分享,概不代表本站观点,如有任何问题我们都将在收到反馈后的第一时间进行处理!