import tkinter as tk from tkinter import messagebox import random # 扫雷游戏类 class MineSweeper: def __init__(self, master, rows, cols, mines): self.master = master # 主窗口 self.rows = rows # 行数 self.cols = cols # 列数 self.mines = mines # 雷的数量 self.buttons = {} # 存放每个按钮控件的字典 {(r, c): Button} self.board = [[0 for _ in range(cols)] for _ in range(rows)] # 雷区,-1 表示地雷,其他为周围雷的数量 self.revealed = [[False for _ in range(cols)] for _ in range(rows)] # 是否已翻开 self.flags = [[False for _ in range(cols)] for _ in range(rows)] # 是否标记为旗帜 self.game_over = False # 游戏是否结束 self.generate_mines() # 随机布雷并更新数字 self.create_widgets() # 创建按钮控件界面 # 布雷并更新周围格子的数字 def generate_mines(self): count = 0 while count < self.mines: r = random.randint(0, self.rows - 1) c = random.randint(0, self.cols - 1) if self.board[r][c] == -1: continue # 避免重复布雷 self.board[r][c] = -1 count += 1 # 更新周围8格的数字 for i in range(r - 1, r + 2): for j in range(c - 1, c + 2): if 0 <= i < self.rows and 0 <= j < self.cols and self.board[i][j] != -1: self.board[i][j] += 1 # 创建游戏按钮界面 def create_widgets(self): for r in range(self.rows): for c in range(self.cols): # 每个格子是一个 Button,绑定左键点击翻格子、右键标旗 b = tk.Button(self.master, width=2, height=1, font=('Arial', 14), command=lambda r=r, c=c: self.reveal_cell(r, c)) b.bind("", lambda e, r=r, c=c: self.toggle_flag(r, c)) b.grid(row=r, column=c) self.buttons[(r, c)] = b # 翻开一个格子 def reveal_cell(self, r, c): if self.game_over or self.revealed[r][c] or self.flags[r][c]: return # 游戏结束、已翻开或已标旗不响应 if self.board[r][c] == -1: # 踩到地雷 self.buttons[(r, c)].config(text="💣", bg="red") self.game_over = True self.show_all_mines() messagebox.showerror("游戏结束", "你踩到雷啦!") return # 否则开始翻格子(递归展开空格) self.flood_fill(r, c) self.check_win() # 类似于 BFS 的空白格子展开 def flood_fill(self, r, c): if not (0 <= r < self.rows and 0 <= c < self.cols): return # 越界 if self.revealed[r][c] or self.flags[r][c]: return # 已翻开或被标记不处理 self.revealed[r][c] = True val = self.board[r][c] btn = self.buttons[(r, c)] # 设置按钮为灰色、不可点击、显示数字(或空) btn.config(relief=tk.SUNKEN, text=str(val) if val > 0 else "", state=tk.DISABLED, bg="lightgray") if val == 0: # 如果是 0,继续向四周递归展开 for dr in (-1, 0, 1): for dc in (-1, 0, 1): if dr != 0 or dc != 0: self.flood_fill(r + dr, c + dc) # 标记旗帜(右键) def toggle_flag(self, r, c): if self.revealed[r][c] or self.game_over: return if self.flags[r][c]: self.flags[r][c] = False self.buttons[(r, c)].config(text="") # 取消旗帜 else: self.flags[r][c] = True self.buttons[(r, c)].config(text="🚩") # 添加旗帜 # 显示所有地雷 def show_all_mines(self): for r in range(self.rows): for c in range(self.cols): if self.board[r][c] == -1: self.buttons[(r, c)].config(text="💣") # 检查是否胜利 def check_win(self): for r in range(self.rows): for c in range(self.cols): if self.board[r][c] != -1 and not self.revealed[r][c]: return # 还有未翻开的非雷格子 self.game_over = True messagebox.showinfo("胜利!", "恭喜你,排雷成功!") # 游戏入口,获取参数并启动游戏 def start_game(): try: # 从输入框获取参数并转为整数 rows = int(entry_rows.get()) cols = int(entry_cols.get()) mines = int(entry_mines.get()) # 检查输入合理性:不能为负、雷不能太多、不能太大 if rows <= 0 or cols <= 0 or mines <= 0 or mines*4 >= rows * cols or rows >= 20 or cols >= 20: raise ValueError except: messagebox.showerror("输入错误", "请输入合理的行列和雷数") return # 关闭设置窗口,打开主游戏窗口 settings_window.destroy() root = tk.Tk() root.title("扫雷") MineSweeper(root, rows, cols, mines) root.mainloop() # 设置窗口(输入行、列、雷数) settings_window = tk.Tk() settings_window.title("扫雷设置") tk.Label(settings_window, text="行数:").grid(row=0, column=0) entry_rows = tk.Entry(settings_window) entry_rows.insert(0, "20") # 默认行数 entry_rows.grid(row=0, column=1) tk.Label(settings_window, text="列数:").grid(row=1, column=0) entry_cols = tk.Entry(settings_window) entry_cols.insert(0, "10") # 默认列数 entry_cols.grid(row=1, column=1) tk.Label(settings_window, text="雷数:").grid(row=2, column=0) entry_mines = tk.Entry(settings_window) entry_mines.insert(0, "10") # 默认雷数 entry_mines.grid(row=2, column=1) # 开始游戏按钮 tk.Button(settings_window, text="开始游戏", command=start_game).grid(row=3, column=0, columnspan=2) # 启动设置窗口主循环 settings_window.mainloop()