使用Python开发经典扫雷游戏

#新星杯·14天创作挑战营·第11期#

使用Python开发经典扫雷游戏

在这篇教程中,我们将学习如何使用Python和Tkinter库开发一个经典的扫雷游戏。这个项目不仅能帮助你理解图形界面编程,还能提升你的算法思维能力。
在这里插入图片描述
在这里插入图片描述

项目概述

我们将实现以下功能:

  1. 创建游戏界面和网格系统
  2. 实现随机地雷布置
  3. 添加左键点击揭示和右键标记功能
  4. 实现递归揭示空白格子
  5. 添加胜利和失败判定
  6. 实现计时器和地雷计数器

完整代码实现

import tkinter as tk
from tkinter import messagebox
import random
import time

class Minesweeper:
    def __init__(self, master):
        self.master = master
        self.master.title('扫雷')
        self.mine_count = 40  # 地雷数量
        self.grid_size = 16   # 网格大小
        self.squares_left = self.grid_size * self.grid_size - self.mine_count
        
        # 游戏状态
        self.game_over = False
        self.start_time = None
        
        # 创建菜单
        self.create_menu()
        
        # 创建顶部信息栏
        self.create_info_frame()
        
        # 创建主游戏区域
        self.create_board()
        
        # 初始化游戏
        self.init_game()

    def create_menu(self):
        menubar = tk.Menu(self.master)
        game_menu = tk.Menu(menubar, tearoff=0)
        game_menu.add_command(label="新游戏", command=self.new_game)
        game_menu.add_separator()
        game_menu.add_command(label="退出", command=self.master.quit)
        menubar.add_cascade(label="游戏", menu=game_menu)
        self.master.config(menu=menubar)

    def create_info_frame(self):
        self.info_frame = tk.Frame(self.master)
        self.info_frame.pack(fill=tk.X)
        
        # 地雷计数器
        self.mine_label = tk.Label(self.info_frame, text=f"地雷: {self.mine_count}")
        self.mine_label.pack(side=tk.LEFT, padx=5)
        
        # 计时器
        self.time_label = tk.Label(self.info_frame, text="时间: 0")
        self.time_label.pack(side=tk.RIGHT, padx=5)

    def create_board(self):
        self.board_frame = tk.Frame(self.master)
        self.board_frame.pack()
        
        self.buttons = []
        for i in range(self.grid_size):
            row = []
            for j in range(self.grid_size):
                button = tk.Button(self.board_frame, width=2, height=1)
                button.grid(row=i, column=j)
                button.bind('<Button-1>', lambda e, x=i, y=j: self.left_click(x, y))
                button.bind('<Button-3>', lambda e, x=i, y=j: self.right_click(x, y))
                row.append(button)
            self.buttons.append(row)

    def init_game(self):
        # 初始化地雷网格
        self.grid = [[0 for _ in range(self.grid_size)] for _ in range(self.grid_size)]
        self.revealed = [[False for _ in range(self.grid_size)] for _ in range(self.grid_size)]
        self.flags = [[False for _ in range(self.grid_size)] for _ in range(self.grid_size)]
        
        # 随机放置地雷
        mines_placed = 0
        while mines_placed < self.mine_count:
            x = random.randint(0, self.grid_size-1)
            y = random.randint(0, self.grid_size-1)
            if self.grid[x][y] != -1:  # -1 表示地雷
                self.grid[x][y] = -1
                mines_placed += 1
        
        # 计算每个格子周围的地雷数
        for i in range(self.grid_size):
            for j in range(self.grid_size):
                if self.grid[i][j] != -1:
                    self.grid[i][j] = self.count_adjacent_mines(i, j)

    def count_adjacent_mines(self, x, y):
        count = 0
        for i in range(max(0, x-1), min(self.grid_size, x+2)):
            for j in range(max(0, y-1), min(self.grid_size, y+2)):
                if self.grid[i][j] == -1:
                    count += 1
        return count

    def left_click(self, x, y):
        if self.game_over or self.flags[x][y]:
            return
            
        if not self.start_time:
            self.start_time = time.time()
            self.update_timer()
        
        if self.grid[x][y] == -1:
            self.game_over = True
            self.reveal_all()
            messagebox.showinfo("游戏结束", "踩到地雷了!")
            return
            
        self.reveal_square(x, y)
        
        if self.squares_left == 0:
            self.game_over = True
            messagebox.showinfo("恭喜", "你赢了!")

    def right_click(self, x, y):
        if self.game_over or self.revealed[x][y]:
            return
            
        self.flags[x][y] = not self.flags[x][y]
        self.buttons[x][y].config(text='🚩' if self.flags[x][y] else '')
        
        # 更新地雷计数
        flag_count = sum(row.count(True) for row in self.flags)
        self.mine_label.config(text=f"地雷: {self.mine_count - flag_count}")

    def reveal_square(self, x, y):
        if self.revealed[x][y] or self.flags[x][y]:
            return
            
        self.revealed[x][y] = True
        self.squares_left -= 1
        
        if self.grid[x][y] == 0:
            self.buttons[x][y].config(state=tk.DISABLED, relief=tk.SUNKEN)
            # 递归揭示相邻的空白格子
            for i in range(max(0, x-1), min(self.grid_size, x+2)):
                for j in range(max(0, y-1), min(self.grid_size, y+2)):
                    if not self.revealed[i][j]:
                        self.reveal_square(i, j)
        else:
            self.buttons[x][y].config(text=str(self.grid[x][y]), 
                                    state=tk.DISABLED, 
                                    relief=tk.SUNKEN)

    def reveal_all(self):
        for i in range(self.grid_size):
            for j in range(self.grid_size):
                if self.grid[i][j] == -1:
                    self.buttons[i][j].config(text='💣', bg='red')
                elif not self.revealed[i][j]:
                    self.buttons[i][j].config(text=str(self.grid[i][j]), 
                                            state=tk.DISABLED, 
                                            relief=tk.SUNKEN)

    def update_timer(self):
        if not self.game_over and self.start_time:
            elapsed_time = int(time.time() - self.start_time)
            self.time_label.config(text=f"时间: {elapsed_time}")
            self.master.after(1000, self.update_timer)

    def new_game(self):
        self.game_over = False
        self.start_time = None
        self.squares_left = self.grid_size * self.grid_size - self.mine_count
        self.time_label.config(text="时间: 0")
        self.mine_label.config(text=f"地雷: {self.mine_count}")
        
        # 重置所有按钮
        for i in range(self.grid_size):
            for j in range(self.grid_size):
                self.buttons[i][j].config(text='', state=tk.NORMAL, 
                                        relief=tk.RAISED, bg='SystemButtonFace')
        
        self.init_game()

def main():
    root = tk.Tk()
    game = Minesweeper(root)
    root.mainloop()

if __name__ == '__main__':
    main()

代码详解

1. 类的初始化

def __init__(self, master):
    self.master = master
    self.master.title('扫雷')
    self.mine_count = 40  # 地雷数量
    self.grid_size = 16   # 网格大小

这部分代码初始化了游戏的基本参数,包括:

  • 游戏窗口设置
  • 地雷数量
  • 网格大小

2. 游戏界面创建

游戏界面包含三个主要部分:

  • 菜单栏:包含新游戏和退出选项
  • 信息栏:显示剩余地雷数和计时器
  • 游戏区域:包含所有方块按钮

3. 游戏逻辑实现

主要的游戏逻辑包括:

  • 随机布置地雷
  • 计算每个格子周围的地雷数
  • 处理左键点击(揭示)和右键点击(标记)
  • 实现递归揭示空白格子
  • 判断游戏胜利或失败

游戏功能

  1. 基本操作

    • 左键点击:揭示格子
    • 右键点击:标记/取消标记地雷
    • 菜单栏:开始新游戏或退出
  2. 游戏机制

    • 随机分布的地雷
    • 数字提示周围地雷数量
    • 自动揭示相连的空白格子
    • 标记可疑的地雷位置
  3. 界面特性

    • 实时计时器
    • 剩余地雷计数
    • 游戏胜负提示
    • 所有地雷位置显示(游戏结束时)

运行效果

运行代码后,你将看到:

  1. 一个16x16的方格网格
  2. 顶部显示地雷数量和计时器
  3. 点击格子会显示数字或触发连锁反应
  4. 右键可以标记可疑的地雷位置

扩展优化建议

  1. 界面优化

    • 添加难度选择
    • 优化界面样式
    • 添加声音效果
    • 添加首次点击保护
  2. 功能增强

    • 添加排行榜系统
    • 实现存档功能
    • 添加自定义设置
    • 实现双击快速操作
  3. 游戏性提升

    • 添加提示功能
    • 实现撤销操作
    • 添加特殊道具
    • 增加新的游戏模式

注意事项

  1. 运行前确保已安装Python和Tkinter库(Python通常自带Tkinter)
  2. 游戏参数(如地雷数量、网格大小)可以根据需要调整
  3. 建议在开发时多添加注释,便于后续维护
  4. 可以根据玩家水平调整游戏难度

进阶思考

  1. 如何优化首次点击体验?
  2. 如何实现更高效的递归算法?
  3. 如何添加更多的游戏模式?
  4. 如何实现网络对战功能?

总结

这个扫雷游戏项目展示了Python在图形界面开发方面的能力。通过这个项目,你可以学习到:

  • Tkinter库的使用
  • 事件处理机制
  • 递归算法的应用
  • 面向对象编程的实践

这个基础版本提供了扎实的框架,你可以在此基础上添加更多功能,创造出属于自己的特色版本。祝你编程愉快!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值