python 实现扫雷 (图形界面,事件绑定)

news/2024/10/31 1:31:48/

本程序实现了扫雷功能,实现了左键打开地块,右键标棋,中键范围打开的功能,采用tkinter图形化,事件绑定实现功能。

代码如下:

import tkinter as tk
from random import randint
import tkinter.simpledialog
from tkinter import messagebox# 一般左键打开
def ordinary_Left(t, xx, yy, x, y):global Minefield, Bif Minefield[xx][yy] == -1:  # 踩到了雷B[xx][yy]['bg'] = 'red'  # 标出踩到的雷l2['fg'] = 'black'  # 游戏结束for i in range(x):  # 打开所有雷for j in range(y):B[i][j].bind('<Button-1>', end)B[i][j].bind('<Button-2>', end)B[i][j].bind('<Button-3>', end)if Minefield[i][j] == -1:if not B[i][j]['bg'] == 'red':B[i][j]['bg'] = 'whitesmoke'  # 换颜色B[i][j]['text'] = '☢'  # 换成雷图标elif Minefield[xx][yy] == 0:  # 空位vacancy(xx, yy, x, y)  # 递归开附近空位else:open(xx, yy, x, y)# 开空位
def vacancy(xx, yy, x, y):try:global Minefield, Bif Minefield[xx][yy] == 0:  # 是空位,下一步for i in [0, -1, 1]:if (xx + i) < 0 or (xx + i) == x:  # x越界跳过continuefor j in [0, -1, 1]:if (yy + j) < 0 or (yy + j) == y:  # y越界跳过continue# 打开过也跳过if B[xx + i][yy + j]['bg'] == 'whitesmoke':continueopen(xx + i, yy + j, x, y)  # 空位周围随便开if Minefield[xx + i][yy + j] == 0:  # 如果是空位vacancy(xx + i, yy + j, x, y)  # 递归except:messagebox.showwarning('警告', '报错,可能是空位过多,递归超限')return# 一般右键标雷
def ordinary_Right(t, xx, yy, x, y):t = 0global mine_2mine_2 -= 1var.set(mine_2)B[xx][yy].bind('<Button-1>', end)B[xx][yy].bind('<Button-2>', end)B[xx][yy].bind('<Button-3>',lambda t=t, xx=xx, yy=yy, x=x, y=y: Flag_Right(t, xx, yy, x, y))B[xx][yy]['text'] = '⚑'  # 换成旗帜图标# 右键取消
def Flag_Right(t, xx, yy, x, y):t = 0global mine_2mine_2 += 1var.set(mine_2)B[xx][yy].bind('<Button-1>',lambda t=t, xx=xx, yy=yy, x=x, y=y: ordinary_Left(t, xx, yy, x, y))B[xx][yy].bind('<Button-2>', end)B[xx][yy].bind('<Button-3>',lambda t=t, xx=xx, yy=yy, x=x, y=y: ordinary_Right(t, xx, yy, x, y))B[xx][yy]['text'] = ''  # 换成空图标# 打开:
def open(xx, yy, x, y):global B, Minefield, colour, lie, mine_1, l2t = 0B[xx][yy].bind('<Button-1>', end)  # 开空位B[xx][yy].bind('<Button-3>', end)if Minefield[xx][yy] == -1:  # 雷B[xx][yy]['bg'] = 'whitesmoke'  # 换颜色B[xx][yy]['text'] = '☢'  # 换成雷图标elif not Minefield[xx][yy] == 0:# 中键操作B[xx][yy].bind('<Button-2>', lambda t=t, xx=xx, yy=yy, x=x, y=y: open_Middle(t, xx, yy, x, y))B[xx][yy]['bg'] = 'whitesmoke'  # 换颜色B[xx][yy]['text'] = Minefield[xx][yy]  # 换成数字B[xx][yy]['fg'] = colour[Minefield[xx][yy]]  # 文字颜色else:  # 空位B[xx][yy]['bg'] = 'whitesmoke'  # 换颜色lie -= 1  # 开一个少一个格子if lie == mine_1 and not l2['fg'] == 'break':  # 小于等于雷数,游戏结束l2['fg'] = 'black'for i in range(x):  # 打开所有雷for j in range(y):B[i][j].bind('<Button-1>', end)B[i][j].bind('<Button-2>', end)B[i][j].bind('<Button-3>', end)if Minefield[i][j] == -1:B[i][j]['bg'] = 'whitesmoke'  # 换颜色B[i][j]['text'] = '☢'  # 换成雷图标# 打开中键范围开启
def open_Middle(t, xx, yy, x, y):global Minefieldn = 0for i in [-1, 0, 1]:if (xx + i) < 0 or (xx + i) == x:  # x越界跳过continuefor j in [-1, 0, 1]:if (yy + j) < 0 or (yy + j) == y:  # y越界跳过continue# 如果是旗帜if B[xx + i][yy + j]['text'] == '⚑':n += 1if n == Minefield[xx][yy]:  # 标雷数相同for i in [-1, 0, 1]:if (xx + i) < 0 or (xx + i) == x:  # x越界跳过continuefor j in [-1, 0, 1]:if (yy + j) < 0 or (yy + j) == y:  # y越界跳过continue# 打开过也跳过if not B[xx + i][yy + j]['bg'] == 'whitesmoke':# 旗帜跳过if not B[xx + i][yy + j]['text'] == '⚑':ordinary_Left(0, xx + i, yy + j, x, y)# 覆盖用方法
def end(t):pass# 生成游戏,长x,宽y,n个雷。
def generate(x, y, n):global Minefield, mir, main_window, B, mine_1, mine_2, lie, l2, varmine_1 = n  # 真实地雷数mine_2 = n  # 地雷数-旗帜数var.set(mine_2)lie = x * y  # 地块大小Minefield = [[0 for i in range(y)] for i in range(x)]while n:  # 埋下n个雷xx = randint(0, x - 1)  # 生成一个随机坐标yy = randint(0, y - 1)if not (Minefield[xx][yy] < 0):  # 如果该位置没有雷Minefield[xx][yy] = -1  # 埋下一个雷n -= 1  # 雷数-1for i in [-1, 0, 1]:if (xx + i) < 0 or (xx + i) == x:  # x越界跳过continuefor j in [-1, 0, 1]:if (yy + j) < 0 or (yy + j) == y:  # y越界跳过continueif not Minefield[xx + i][yy + j] == -1:  # 如果不是埋雷地点Minefield[xx + i][yy + j] += 1  # 数值加1# 雷区生成完毕,生成按钮l2['fg'] = 'whitesmoke'mir.destroy()  # 删除上次按钮mir = tk.Frame(main_window)  # 放雷区的框架mir.grid(row=0, column=0, sticky="nsew")B = []  # 按钮组t = 0for i in range(x):B.append([])mir.grid_rowconfigure(i, weight=1)  # row为i,缩放比为1for j in range(y):mir.grid_columnconfigure(j, weight=1)  # column为i,缩放比为1B[i].append(tk.Button(mir, text="", width=2, height=2, bg='lightgray'))  # 添加按钮# 绑定左键和右键方法B[i][j].bind('<Button-1>', lambda t=t, i=i, j=j, x=x, y=y: ordinary_Left(t, i, j, x, y))B[i][j].bind('<Button-3>', lambda t=t, i=i, j=j, x=x, y=y: ordinary_Right(t, i, j, x, y))B[i][j].grid(row=i, column=j, sticky=tk.N + tk.S + tk.W + tk.E)  # 添加到主窗口显示# 自定义参数对话框
def custom():def determine(x, y, n):  # 确定函数try:x, y, n = int(x), int(y), int(n)  # str转intexcept:messagebox.showwarning('警告', '请输入正确的数字')return# 判断数字是否正确if x > 30 or y > 50 or x < 1 or y < 1 or n < 1 or n >= x * y:messagebox.showwarning('警告', '请输入正确的数字')returngenerate(x, y, n)cusrom_window.destroy()  # 关闭窗口global main_windowcusrom_window = tk.Toplevel(main_window)cusrom_window.title("自定义")cusrom_window.geometry("200x120")  # 大小yl = tk.Label(cusrom_window, text='长(1~50):')  # 输入框前的字xl = tk.Label(cusrom_window, text='宽(1~30):')nl = tk.Label(cusrom_window, text='雷(xy>n>0):')var_y = tkinter.StringVar()var_x = tkinter.StringVar()var_n = tkinter.StringVar()y = tk.Entry(cusrom_window, textvariable=var_y)  # 输入框x = tk.Entry(cusrom_window, textvariable=var_x)n = tk.Entry(cusrom_window, textvariable=var_n)yl.grid(column=0, row=0)  # 布局xl.grid(column=0, row=2)nl.grid(column=0, row=4)y.grid(column=1, row=0)x.grid(column=1, row=2)n.grid(column=1, row=4)B = tk.Button(cusrom_window, text="确定", command=lambda: determine(x.get(), y.get(), n.get()))B.grid(column=0, row=5)def mine():global l2, mine_2, varmine_window = tk.Toplevel(main_window)  # 信息窗口mine_window.title("地雷数")mine_window.geometry("200x100+200+200")  # 大小mine_window.resizable(0, 0)  # 不允许拉伸改变大小l1 = tkinter.Label(mine_window, text='剩余地雷:', fg='blue')l2 = tkinter.Label(mine_window, text='game over', fg='whitesmoke')var = tkinter.StringVar()var.set(mine_2)mine = tkinter.Label(mine_window, textvariable=var, fg='blue')l1.grid(row=0, column=0, )mine.grid(row=0, column=1, )l2.grid(row=1, column=0, )# 雷区,主窗口,雷场,颜色,炸弹数
global Minefield, main_window, mir, colour,mine_2
mine_2 = 0
colour = ['w', 'blue', 'green', 'red', 'navy', 'maroon', 'teal', 'black', 'indigo']  # 文字颜色
main_window = tk.Tk()  # 调用Tk()创建主窗口
main_window.title("扫雷")  # 给主窗口起一个名字
main_window.geometry("450x450+400+200")  # 大小
menubar = tk.Menu(main_window)  # 菜单栏
# 难度菜单是菜单栏的子菜单,且不能窗口化
difficulty = tk.Menu(menubar, tearoff=False)
difficulty.add_command(label='简单', command=lambda: generate(9, 9, 10))  # 难度子菜单添加选项
difficulty.add_command(label='中等', command=lambda: generate(16, 16, 40))  # command为要调用的函数
difficulty.add_command(label='困难', command=lambda: generate(16, 30, 99))
difficulty.add_separator()  # 分割线
difficulty.add_command(label='自定义', command=custom)
menubar.add_cascade(label='难度', menu=difficulty)  # 菜单难度选项
menubar.add_cascade(label='地雷数', command=mine)  # 菜单难度选项
mine()
main_window.config(menu=menubar)  # 窗口与菜单关联
main_window.grid_rowconfigure(0, weight=1)  # row为0,缩放比为1
main_window.grid_columnconfigure(0, weight=1)  # column为0,缩放比为1
mir = tk.Frame(main_window)  # 放雷区的框架
mir.grid(row=0, column=0, sticky="nsew")  # 放置框架
generate(9, 9, 10)
main_window.mainloop()  # 开启主循环,让窗口处于显示状态

结果示例:

改进方案:

因为是先生成雷区,玩家再进行扫雷,有可能会第一次就踩雷,可以先生成所有按钮,每个按钮左键绑定一个函数,触发这个函数后,再进行雷区生成,生成的时候避开第一个点击的按钮位置就行。

对于雷区大雷少的情况,空位比较多,递归的时候可能会越界,可以用sys库中的sys.setrecursionlimit(1500)将递归深度改大,我这里不想导那么多库,就没整,你们可以试试,就两行的事

其他:此程序为新学期练手用,欢迎找bug


http://www.ppmy.cn/news/128924.html

相关文章

matplotlib基础学习

http://blog.csdn.net/pipisorry/article/details/37742423 matplotlib介绍 matplotlib 是python最著名的绘图库&#xff0c;它提供了一整套和matlab相似的命令API&#xff0c;十分适合交互式地进行制图。而且也可以方便地将它作为绘图控件&#xff0c;嵌入GUI应用程序中。它…

3-24

tkinter 窗口大小定义&#xff1a; 800x600为窗口大小&#xff0c;1010为窗口所在位置。记住x是英文字母的x而不是乘号 root.geometry("800x6001010") 控件属性&#xff1a; #### Dimension 控件大小&#xff1b; #### Color 控件颜色&#xff1b; #### Font 控件字…

【数据集】计算机视觉,深度学习,数据挖掘数据集整理

金融 美国劳工部统计局官方发布数据上证A股日线数据&#xff0c;1999.12.09 至 2016.06.08&#xff0c;前复权&#xff0c;1095支股票深证A股日线数据&#xff0c;1999.12.09 至 2016.06.08&#xff0c;前复权&#xff0c;1766支股票深证创业板日线数据&#xff0c;1999.12.09 …

RK3288[android 7.1]调试笔记 修改默认的PreviewSize预览分辨率和PictureSize照片大小

RK3288[android 7.1]调试笔记 修改默认的PreviewSize预览分辨率和PictureSize照片大小 从log中知道自带摄像头应用是会根据支持的分辨率并结合屏的分辨率去选择最优分辨率的 01-18 16:52:13.918 226 706 I CameraHal: initDefaultParameters(170): dsy log:initDefaultPa…

2021第三届长安杯检材一wp

第一题 题目请计算检材一Apk的SHA256值 Writeup 使用windows自带的计算工具get-filehash 进入文件所在目录 输入Get-FileHash .\检材一-zhibo.apk -Algorithm SHA256 得到hash值&#xff1a;3FECE1E93BE4F422C8446B77B6863EB6A39F19D8FA71FF0250AAC10F8BDDE73A 第二题 …

penn treebank_Penn Manor致力于开源

penn treebank 开源在各级教育中发挥着越来越大的作用。 宾夕法尼亚州的Penn Manor学区是其中一种接受开源的校务委员会。 学区已经推出了该州最大的开源学生笔记本电脑计划&#xff0c;向学生分发了3500台基于Linux的计算机。 但是Penn Manor对开源的承诺远不只是分发笔记本电…

【2020-BNUZ-IT节程序设计竞赛网络赛题解】E. 因为感觉打不过就全点速度与攻击了

【2020-BNUZ-IT节程序设计竞赛网络赛题解】E. 因为感觉打不过就全点速度与攻击了 题面题目大意题目思路容易卡住的点题解C 语言代码C代码Java代码python代码 题后语 题面 题目大意 小陈由于血量很低&#xff0c;所以只能在「无敌金身」的10秒内击败敌人&#xff0c;否则就会被…

深度学习(9): LeNet-5 网络模型小结

文章目录 1 MNIST手写字体数据集2 LeNet-5 网络模型3 实验4 小结参考资料 注&#xff1a;转载请标明原文出处链接&#xff1a;https://xiongyiming.blog.csdn.net/article/details/100013565 1989年&#xff0c;Yann Lecun在论文 Gradient-Based Learning Applied to Documen…