询问
以图形方式给出早期Windows挖地雷的python代码
回答
以下是一个简单的使用 Python 和 Tkinter 库实现的经典早期 Windows 挖地雷游戏(Minesweeper)的图形化版本。这个程序允许玩家点击格子来探索地雷,标记可能的地雷位置,并判断游戏胜负。
Windows 风格的挖地雷游戏(Python实现)
import tkinter as tk
import random
import time
class Minesweeper:
def __init__(self, master, width=10, height=10, num_mines=20):
self.master = master
self.master.title("挖地雷")
self.width = width # 网格宽度
self.height = height # 网格高度
self.num_mines = num_mines # 地雷数量
self.buttons = []
self.mines = set() # 存储地雷的坐标
self.revealed = set() # 存储已打开的格子
self.flags = set() # 存储标记的格子
self.game_over = False
# 创建画布和按钮
self.canvas = tk.Canvas(self.master, width=width*30, height=height*30)
self.canvas.pack()
self.create_grid()
self.place_mines()
def create_grid(self):
"""创建按钮网格"""
for row in range(self.height):
button_row = []
for col in range(self.width):
button = tk.Button(self.master, text='', width=3, height=1, command=lambda r=row, c=col: self.reveal(r, c))
button.grid(row=row, column=col)
button_row.append(button)
self.buttons.append(button_row)
def place_mines(self):
"""随机放置地雷"""
while len(self.mines) < self.num_mines:
mine = (random.randint(0, self.height-1), random.randint(0, self.width-1))
self.mines.add(mine)
def reveal(self, row, col):
"""揭示格子内容"""
if self.game_over or (row, col) in self.revealed or (row, col) in self.flags:
return
self.revealed.add((row, col))
if (row, col) in self.mines:
self.buttons[row][col].config(text="💣", bg="red")
self.game_over = True
self.show_game_over("游戏失败!")
return
else:
# 计算相邻地雷的数量
adjacent_mines = self.count_adjacent_mines(row, col)
if adjacent_mines > 0:
self.buttons[row][col].config(text=str(adjacent_mines), relief=tk.SUNKEN)
else:
self.buttons[row][col].config(relief=tk.SUNKEN)
# 如果没有地雷,递归揭示相邻格子
for r in range(max(0, row-1), min(self.height, row+2)):
for c in range(max(0, col-1), min(self.width, col+2)):
if (r, c) != (row, col) and (r, c) not in self.revealed:
self.reveal(r, c)
# 检查游戏胜利条件
if len(self.revealed) == self.width * self.height - self.num_mines:
self.game_over = True
self.show_game_over("游戏胜利!")
def count_adjacent_mines(self, row, col):
"""计算相邻格子的地雷数量"""
adjacent_mines = 0
for r in range(max(0, row-1), min(self.height, row+2)):
for c in range(max(0, col-1), min(self.width, col+2)):
if (r, c) in self.mines:
adjacent_mines += 1
return adjacent_mines
def show_game_over(self, message):
"""显示游戏结束的消息"""
for r in range(self.height):
for c in range(self.width):
if (r, c) in self.mines:
self.buttons[r][c].config(text="💣", bg="red")
else:
self.buttons[r][c].config(state=tk.DISABLED)
self.canvas.create_text(self.width*15, self.height*15, text=message, font=("Arial", 20), fill="black")
if __name__ == "__main__":
root = tk.Tk()
game = Minesweeper(root, width=10, height=10, num_mines=20)
root.mainloop()
代码功能和说明:
游戏初始化:
游戏包含一个 10x10 的网格,默认情况下有 20 个地雷。
使用 Tkinter 创建图形界面,每个格子用一个按钮表示。
创建按钮网格并随机放置地雷。
按钮点击和地雷揭示: 玩家点击按钮后会触发 reveal 函数,如果点击的是地雷,游戏结束;否则,按钮显示周围地雷的数量。如果周围没有地雷,会递归揭示周围的按钮。
如果玩家揭示了所有非地雷格子,游戏胜利。
计算周围的地雷数量:
使用 count_adjacent_mines 函数来计算某个格子周围的地雷数量。如果该格子周围没有地雷,则继续递归揭示相邻的格子。
游戏胜负判断:
如果玩家点击到地雷,显示“游戏失败”。
如果玩家揭示了所有非地雷格子,显示“游戏胜利”。
地雷显示:游戏结束时,所有地雷会显示在对应的按钮上,玩家无法再点击其他格子。
运行游戏:
将代码复制到 Python 环境中并运行,确保已安装 tkinter 库。
游戏会弹出窗口,玩家可以点击格子来探索,避免点击到地雷。
游戏会根据玩家的表现显示“游戏胜利”或“游戏失败”。
扩展功能:
标记功能:玩家可以右键点击格子标记疑似地雷的位置。
难度设置:根据玩家选择的难度改变网格的大小和地雷的数量。
计时功能:为游戏添加一个计时器,记录玩家完成游戏的时间。
如果您有其他问题或想进一步扩展这个游戏,随时告诉我!
运行代码,失败
Traceback (most recent call last):
File "C:\Users\xiang\Desktop\Minesweeper.py", line 90, in
game = Minesweeper(root, width=10, height=10, num_mines=20)
File "C:\Users\xiang\Desktop\Minesweeper.py", line 21, in init
self.create_grid()
File "C:\Users\xiang\Desktop\Minesweeper.py", line 30, in create_grid
button.grid(row=row, column=col)
File "C:\Users\xiang\AppData\Local\Programs\Python\Python312\Lib\tkinter_init_.py", line 2569, in grid_configure
self.tk.call(
_tkinter.TclError: cannot use geometry manager grid inside . which already has slaves managed by pack这个错误发生是因为在Tkinter中,你不能同时使用pack和grid布局管理器。你要么使用pack()来管理控件的位置,要么使用grid()来管理。
在你的代码中,self.canvas.pack()使用了pack(),而self.buttons[row][col].grid()则使用了grid()。这就导致了冲突,因为一个窗口不能同时使用两种布局管理器。
import tkinter as tk
from tkinter import messagebox
import random
class Minesweeper:
def __init__(self, master, size=10, mines=10):
self.master = master
self.size = size
self.mines = mines
self.board = [[0] * size for _ in range(size)]
self.buttons = [[None] * size for _ in range(size)]
self.revealed = [[False] * size for _ in range(size)]
self.flags = [[False] * size for _ in range(size)]
self.game_over = False
self.create_widgets()
self.place_mines()
self.calculate_numbers()
def create_widgets(self):
for row in range(self.size):
for col in range(self.size):
button = tk.Button(self.master, width=2, height=1,
command=lambda r=row, c=col: self.reveal(r, c))
button.bind('', lambda e, r=row, c=col: self.toggle_flag(r, c))
button.grid(row=row, column=col)
self.buttons[row][col] = button
def place_mines(self):
count = 0
while count < self.mines:
row = random.randint(0, self.size - 1)
col = random.randint(0, self.size - 1)
if self.board[row][col] != 'M':
self.board[row][col] = 'M'
count += 1
def calculate_numbers(self):
for row in range(self.size):
for col in range(self.size):
if self.board[row][col] == 'M':
continue
mines_count = 0
for dr in [-1, 0, 1]:
for dc in [-1, 0, 1]:
if 0 <= row + dr < self.size and 0 <= col + dc < self.size:
if self.board[row + dr][col + dc] == 'M':
mines_count += 1
self.board[row][col] = mines_count
def reveal(self, row, col):
if self.game_over or self.flags[row][col] or self.revealed[row][col]:
return
self.revealed[row][col] = True
if self.board[row][col] == 'M':
self.buttons[row][col].config(text='M', bg='red')
self.game_over = True
messagebox.showinfo("游戏结束", "你踩到地雷了!")
return
self.buttons[row][col].config(text=str(self.board[row][col]), relief=tk.SUNKEN)
if self.board[row][col] == 0:
for dr in [-1, 0, 1]:
for dc in [-1, 0, 1]:
if 0 <= row + dr < self.size and 0 <= col + dc < self.size:
if not self.revealed[row + dr][col + dc]:
self.reveal(row + dr, col + dc)
if self.check_win():
self.game_over = True
messagebox.showinfo("胜利", "恭喜,你赢了!")
def toggle_flag(self, row, col):
if self.game_over or self.revealed[row][col]:
return
if self.flags[row][col]:
self.buttons[row][col].config(text='', bg='SystemButtonFace')
self.flags[row][col] = False
else:
self.buttons[row][col].config(text='F', bg='yellow')
self.flags[row][col] = True
def check_win(self):
for row in range(self.size):
for col in range(self.size):
if self.board[row][col] != 'M' and not self.revealed[row][col]:
return False
return True
if __name__ == "__main__":
root = tk.Tk()
root.title("扫雷游戏")
game = Minesweeper(root, size=10, mines=10)
root.mainloop()
解决方案:
你有两种方法可以解决这个问题:
统一使用 grid() 布局管理器:将所有组件的布局都改为grid()。
统一使用 pack() 布局管理器:将所有组件的布局都改为pack()。
在这种情况下,由于你的按钮是按网格排列的,推荐使用 grid() 来管理按钮的布局。对于canvas,你也可以使用grid(),而不是pack(),这样就可以避免布局冲突。
主要修改:
canvas.grid():将 canvas 使用 grid() 布局,而不是 pack()。
按钮 grid():所有按钮都使用 grid() 来管理,而不是 pack()。
这样就避免了布局冲突,确保所有组件都通过相同的布局管理器(grid())进行管理。您可以尝试运行这段代码,看看是否解决了问题。