feat(第7讲 字母卡牌): 实现记忆游戏并添加开始界面

- 新增 jiyi.py 文件,实现字母翻牌记忆游戏功能
- 添加 youxijiemian.py 文件,创建游戏开始界面
- 使用 turtle 和 tkinter 模块分别实现游戏和界面
- 支持选择不同难度的游戏模式
This commit is contained in:
sairate 2025-07-09 15:54:21 +08:00
parent d4c4cd1af9
commit 3d1a9e092c

View File

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