首先介绍扫雷游戏规则。扫雷游戏界面是有若干行和列的方块矩阵,用矩阵的行列号定位矩阵中的方块。每个方块都有一些状态,例如有无雷、有无标记等,用一个2维列表记录这些状态,为了和方块矩阵形成对应关系,把矩阵的第1个序号称作行号,第2个序号称作列号。矩阵中一些块下隐藏着雷,把这些块称作“有雷块”,同样块下无雷的块称作“无雷块”。每个块都有相邻的块,最多8个,在边角有5个或3个,把这些块称作某块的“相邻块”。本程序将使用按钮代替方块,将使用“有雷按钮”、“无雷按钮”和“相邻按钮”等名称。玩家如判断某块有雷,可用鼠标右键单击该块,使该块显示红旗。如怀疑某块有雷,右击方块两次,使该块显示问号。用鼠标左键单击标有红旗的块,程序不做任何处理,即使该块无雷被错标红旗。用鼠标左键单击未标红旗的块或标有问号的块,称作打开该方块。如左击到有雷块,该块将显示背景为红色的雷,说明这个雷被错误点击,然后显示所有未被红旗标记的雷,被红旗标记却无雷的块显示打叉的雷,表示标记错误,有雷而且被正确标记的块保留显示红旗,游戏结束。如左击到无雷块,则调用左击事件函数来计算左击块的所有相邻有雷块的数量(即雷数),请注意,是查看真实的雷数,不受相邻块中那些错标红旗无雷块的影响。把得到的雷数显示在左击块的上方。如被左击块的所有相邻块都是无雷块(雷数为0),左击块则显示空白。由于相邻的这些块无雷,因此可以被左击打开,但错误标记了红旗的无雷块不能被打开。所有这些需要打开的方块由程序模拟单击打开,采用递归调用单击事件函数方法,来查看这些块的所有相邻有雷块的数量,在递归调用中可能还需要继续递归调用,直到若干显示空白的方块区域被一圈显示数字的方块所包围。当某块显示数字n,表示该块的所有相邻块中n块有雷,如玩家已在该块所有相邻块中为n块标记了红旗,认定这些块下有雷,无论这些标记正确与否,玩家都可双击这个显示数字n的块,相当于对被双击块的所有相邻、未打开且未标记红旗的块(包括带问号的块)都进行一次左键单击操作。如玩家在相邻块中把红旗错误标记到1个无雷块上,那么所有相邻块中一定存在1个没被红旗标记的有雷块,对这个有雷块左键单击,游戏就结束了,没标记红旗的有雷块显示底色为红色的雷,表示点击此雷出错。在WinXP中的经典扫雷游戏中,双击是鼠标左右键同时按下。现在的笔记本电脑鼠标触摸板不能实现鼠标左右键同时按下,因此本程序的双击是鼠标左键双击。玩家胜利的条件是:全部有雷块都被红旗标记,没有被错误标记红旗的无雷块,所有无雷块都被单击打开。
本程序模拟WinXP中的扫雷程序,只想表达设计扫雷游戏的基本思路,因此仅实现了大部分功能,包括游戏菜单下的“开局”、“初级”、“中级”、“高级”和“退出”共5个菜单项,没实现的菜单项有“自定义”、“标记”、“颜色”、“声音”和“扫雷英雄谱”共6项,除了“自定义”,其它都是辅助功能,用处不大。本想要实现“自定义”功能,但自定义要求玩家输入地雷矩阵的行数、列数和矩阵中雷的个数,因此要用到一个对话框,但tkinte中无此组件,要自己设计,增加不少代码,和扫雷游戏编程思路关系不大,最后放弃了。另外帮助菜单也比较简单,仅包括一个简单消息框。WinXP中的扫雷程序共有3级,初级矩阵为9x9,10个雷,中级16x16,40个雷,高级30x16,99个雷。级别偏难,因此参考4399中的扫雷游戏,改为初级矩阵为6x6,5个雷,中级9x9,15个雷,高级12x12,33个雷。另外,WinXP的扫雷程序中,重玩按钮显示的是一个图形,有2个动作:高兴和悲伤。本程序按钮显示的是文本:重玩。
本程序使用python tkinter按钮(Button)组件组成按钮矩阵(第154-162行)。初始所有按钮是立体状,按钮显示为空。当左击按钮(打开按钮),按钮变为平面,按钮显示其所有相邻有雷按钮数。用3维列表mineMap记录每个按钮的雷状态和相邻雷数,以2行2列按钮矩阵为例说明3维列表格式:mineMap=[[[0,-1],[0,-1]],[[0,-1],[0,-1]]],列表项为map[i][j][k],当k=0列表项是矩阵i行j列按钮的雷状态,当k=1是相邻雷数。雷状态是负数表示该按钮有雷,-1=有雷无标记 ,-2=有雷标记红旗,-3=有雷标记问号,是正数表示按钮无雷,0=无标记未打开 ,1=表示已打开,2=标记红旗,3=标记问号,请注意,这7种状态是互斥的,因此能用一个整数表示7种雷状态;相邻雷数=-1表示该按钮未打开,即未计算该按钮所有相邻有雷按钮数,为正数表示其已被打开,其值为该按钮所有相邻有雷按钮数(0-8),为0按钮显示空。函数putMine初始化3维列表。第8行用状态值[0,-1]初始化列表,0表示无雷未被标记未打开,-1表示未计算所有相邻有雷按钮数。第9行用来布雷,从总按钮数 (numOfrow * numOfCol)随机取出若干数,具体取出多少数决定于初始雷数,然后把各个数转换为列表的行列数,把在该位置按钮的雷状态由0改为-1,表示该位置有无标记的雷。游戏界面如下:

第167行生成主窗口。第170-184行生成菜单。第185-189行生成全局变量,第190行建立Label类对象显示地雷数。第192行建立Label类对象显示扫雷所用秒数。第193行建立Button类对象做为重玩按钮。第194-196行从文件生成PhotoImage类对象,用来在按钮上显示雷、带叉的雷以及红旗的图形。最后调用函数setGameLevel(6,6,5),在程序运行后将游戏设置为初级。有一点需要注意,仅拷贝源程序代码不能使程序正确运行,因为按钮所显示的3个图形,小红旗、地雷和打叉的地雷,需要从外部文件建立PhotoImage对象。这3个文件在程序所在文件夹的子文件夹pic中,文件名分别是:红旗.png、地雷2.png和地雷3.png。读者可自己建立这3个文件,图形大小为20*20,采用png文件目的是使背景能够透明,使png文件图形背景透明的网址:http://www.aigei.com/bgremover/。当然读者也可下载源文件,网址是:https://download.csdn.net/download/geng_zhaoying/12927644
下面介绍函数setGameLevel,它有3个参数分别是矩阵行数、列数和雷数。它完成两个任务,第1是程序运行后将游戏设为初级,第2是修改游戏等级。二者区别是修改游戏等级时要把前边等级按钮矩阵删除,再建立新矩阵。而程序运行后将游戏等级设为初级时还未有按钮矩阵,无法删除矩阵。如当前游戏等级和要修改等级相同,即行列数和雷数都相同,没有必要删除再重建(第144行)。如查到记录按钮对象的字典为空,说明程序刚运行还未建立按钮矩阵,因此无法删除按钮矩阵(第147行)。第154-162行是创建按钮矩阵。如numOfrow和numOfCol改变,主窗体尺寸、秒表和重玩按钮位置都要改变,第163-165行完成此功能。最后调用函数reSet()。
现在介绍函数reSet(),在重玩游戏前调用此函数完成游戏初始化。第115行用label组件显示所设定游戏等级的初始雷数,以后显示未标记的雷数,每用红旗标记一个雷,该值减1。第116行将秒表清0。第117行将3维列表mineMap清空,第118行调用函数putMine(),初始化3维列表mineMap,并重新布雷。第119-124行将在上次游戏中按钮被改变的属性恢复为初始状态:立体状、显示为空和背景色为银灰色。第125行将变量gameOver设置为false,使按钮能响应事件函数。最后3条语句和秒表有关。
秒表的工作原理参见本人博文:博文“在python tkinter窗口实现多线程秒表的两种方法”的第2种方法再讨论。第127行创建一个Timer类对象timer。timer在激活状态,关闭主窗口结束程序,会抛出异常,为避免此种情况,timer.setDaemon(True)语句使子线程成为主线程(主程序)的守护线程(第128行),当主进程结束后,子线程会随之结束。语句timer.start()在左击按钮事件函数btnClick中(第53行),用来启动定时器,启动后timer延迟1秒后将调用函数count()在子线程中运行,退出该函数,子线程结束。子线程只能启动一次,如子线程已启动处于激活状态,再次启动子线程,会抛出异常。这里必须要判定子线程不是在激活状态(第51行),才能执行timer.start()。在启动前令isTimerRun为True。在函数count中,如while语句满足循环条件,函数count将一直在子线程中运行,完成秒表计数功能。只要修改全局变量isTimerRun=false,就可退出函数,结束子线程,秒表计数结束。游戏无论是胜利还是失败都要结束秒表,在第57行、第78行和第112行令isTimerRun=false,使秒表停止计数。
菜单项初级、中级和高级的事件函数都是setGameLevel(row=0,col=0,mine=0),见第175-177行,但用lambda表达式给出的实参不同,修改实参的值可以修改游戏等级的行列数和地雷数。菜单项开局和重玩按钮的事件函数都是reSet(),因行列数和地雷数不变,只需要为重玩游戏做初始化工作。
在扫雷游戏中,按钮要响应鼠标右键单击、鼠标左键单击和双击事件。程序第154-162行语句中生成按钮矩阵,在建立按钮对象的语句中,用command指定左键单击事件函数是btnClick(x,y)(第158行),用lambda表达式为左键单击事件函数的两个参数提供实参,实参是被单击按钮所在位置的行列号,可参考本人博文:python3.8的tkinter按钮事件函数实现多个参数。还为按钮绑定鼠标右键单击和鼠标左键双击事件(第160- 161行),指定的事件函数是同一个函数but_RDclick(event,x=row,y=col),在事件函数中根据鼠标事件传递的参数event.num区分左右键,来确定当前是哪个事件调用了事件函数。可参考本人博文:增加鼠标右击按钮事件函数包含event和其它参数。函数but_RDclick的参数x和y是被单击按钮的行列号。
首先看和鼠标左键单击按钮事件函数btnClick(x,y)的相关函数。要计算被单击按钮所有相邻有雷按钮数,必须首先要找到这些相邻按钮,函数getAroundBut(x,y)完成这个工作(第11-13行),该函数返回列表,列表中包含x行y列按钮的所有相邻按钮的行列号。相邻按钮最多为8个,如按钮在边角可能有5个或3个。如某按钮行(列)号为n,其相邻按钮的行(列)号可能为n-1,n,n+1,行(列)号不能为负,为负取0,也不能大于等于最大行(列)号,否则取值最大行(列)号-1。当然相邻按钮不包括自己。函数getNumOfAroundMine(x,y)得到x行y列(被左击)按钮所有相邻有雷按钮数(第33-46行),当左击了有雷按钮,退出函数返回0(第35行),否则返回1(第46行)。第36行得到所有相邻按钮行列号的列表,逐一判断该列表中每一个按钮的雷状态值,如其为负,表明是雷,雷数+1。最后将所得相邻有雷按钮数保存到和左击按钮相对应mineMap列表的’相邻雷数’项中(第41行)。如所有相邻有雷按钮数为0,所有这些相邻按钮都可以被左击打开,即每个相邻按钮都需要计算自己的所有相邻有雷按钮数,可由程序递归调用函数getNumOfAroundMine来完成(第45行)。再看单击按钮事件函数btnClick,如游戏结束、按钮标记了红旗或按钮已打开,单击这些按钮后将退出该函数(第49-50行),第54行调用函数getNumOfAroundMine计算该按钮所有相邻有雷按钮数,返回0,表示点击了有雷按钮,游戏失败,调用函数showAllMines(函数在第14行),显示所有未被标记的雷,被左击的有雷按钮底色变红(第15行),错误标记红旗的无雷按钮显示打叉的雷,正确标记的有雷按钮保留红旗不变。函数btnClick最后设置一些变量值(第56-58行)。如单击了无雷按钮,则调用函数showNumOfMine,在所有已计算了所有相邻雷数的按钮上显示这个雷数(第60行),调用函数isWin判断是否胜利(第61行)。
最后介绍函数RClick_LDoubleClick(evt,x,y),是处理鼠标右击和鼠标左键双击的函数。如果evt.num== 1,是左键,是鼠标左键双击按钮事件,否则是右键单击事件。
先看双击显示数字n的按钮,只有n!=0且该按钮已被打开,双击才有效(第67行)。第68-72行计算被双击按钮的所有相邻按钮上显示红旗的数量(可能标记有错),如判断有n个红旗(第73行),双击就相当于对被双击按钮的所有相邻按钮(包括显示问号按钮)均进行1次左键单击按钮操作,即调用函数getNumOfAroundMine,如其返回值是0,相当于单击了有雷按钮,如果该按钮未被红旗标记(第75行),说明被标记为红旗的n个按钮中有一个标记不正确,游戏结束。
再看右击按钮事件。多次右击未打开的按钮,按钮显示按规律循环,如右击前按钮显示为空,右击后按钮显示红旗;如右击前按钮显示红旗,右击后按钮显示问号;如右击前按钮显示问号,右击后按钮显示为空。但实际上右击前,按钮下的雷有7种状态,右击后按钮属性也就有7种变化。如用判断语句根据右击前按钮下的雷状态得到右击后按钮属性的变化,需不少代码。为减少代码,因此建立一个字典如下:

myDict={-1:(-2,p,''),-2:(-3,'','?'),-3:(-1,'',''),0:(2,p,''),2:(3,'','?'),3:(0,'','')}

按钮的雷状态作为字典的键,字典的值是元组,有3个值,分别为单击后的雷状态值、按钮显示图形的PhotoImage对象和按钮的text属性值,例如,右击时按钮的雷状态为-3(有雷标记问号),右击后雷状态为-1(有雷无标记),后两项都为空字符串,因此按钮显示为空。另外字典只有6项,因右击时雷状态为1时,表示按钮下无雷已被单击打开,不做处理(第84行)。有了字典,右击按钮,得到按钮下的雷状态当前值,从字典取出单击后的雷状态值、按钮应显示的图形和按钮属性text对应的字符串。全部程序代码如下。再次提醒仅拷贝源程序不能运行,原因见前边解释。

from threading import Timer
import time
import random
import tkinter as tk
import tkinter.messagebox         #第8行中[0,-1]是按钮的雷状态和相邻雷数量,雷状态:-1=有雷,-2=有雷标												

用python tkinter组件实现扫雷游戏相关推荐

  1. python实现剪刀石头布_用Python Tkinter实现剪刀石头布小游戏的方法

    用Python Tkinter实现剪刀石头布小游戏的方法 发布时间:2020-12-07 10:38:11 来源:亿速云 阅读:90 作者:小新 这篇文章将为大家详细讲解有关用Python Tkint ...

  2. 如何用python编一个扫雷游戏_用 Python 做一个 Windows 扫雷游戏

    原标题:用 Python 做一个 Windows 扫雷游戏 本文代码基于 python3.6 和 pygame1.9.4. Windows XP 上的扫雷是无数80/90后的集体回忆,今天我们就用 P ...

  3. python3扫雷代码_GitHub - pantaduce/minesweeper: Python代码编写的扫雷游戏

    Minesweeper(扫雷) 这是一个由Python编写的扫雷游戏,基于tkinter/Tkinter开发,支持python2和python3. 功能 概述 基本功能:左键扫雷,右键标记 记录游戏步 ...

  4. 使用 python 的单人AI 扫雷游戏

    运行扫雷 1.确保安装了Python 3.6+. 2.安装Pygame. 3.克隆这个存储库: 设置 minesweeper.py ⚓ 扫雷游戏表示 class Minesweeper():def _ ...

  5. 如何用Python Tkinter实现剪刀石头布小游戏?

    编写剪刀石头布游戏 让我们使用Python 3和Tkinter开发相同的游戏.我们可以将游戏命名为Rock-Paper-Scissors-Lizard-Spock. 规则和玩法 Rock crushe ...

  6. Python tkinter版猜数游戏

    程序启动后,首先需要启动一次游戏并设置数值范围和猜测次数,然后可以猜数并输入,程序会根据实际情况进行大小提示,退出程序时提示战绩,例如共玩几次和成功几次. import random import t ...

  7. 【pygame】Python 制作 XP 经典扫雷游戏

    本扫雷也不必多说,先看文件格式: main.py Saolei _pycache_ mineblock.py mineblock.cpython-39.pyc resources 文章结尾有素材 再看 ...

  8. python小游戏源代码pygame_【pygame】Python 制作 XP 经典扫雷游戏(附源码)

    importsysimporttimefrom enum importEnumimportpygamefrom pygame.locals import * from mineblock import ...

  9. 教你利用python 的单人AI 扫雷游戏

    AI玩扫雷 很高兴又见面了!

最新文章

  1. 独家交付秘籍,你确定不点开看看?
  2. RocketMQ-Spring 毕业两周年,为什么能成为 Spring 生态中最受欢迎的 messaging 实现?
  3. ldconfig mysql_ldconfig命令介绍
  4. 【计蒜客 - 蓝桥训练】修建公路(贪心,或运算,dp)
  5. Qt文档阅读笔记-Visual Parent的初步理解(获取QML中的根节点及其子结点)
  6. jenkins配置sonar并扫描C#代码
  7. oracle 转成sql server,怎样把Oracle查询转换为SQL Server
  8. ubuntu如何查看系统是多少位和系统版本号
  9. 下载编译LineageOS代码
  10. SIM868——GPS加速定位方法
  11. 上海php程序员职友集,好程序员WEB前端培训|HTML5培训|H5培训-好程序员官网
  12. e站host地址_电脑网络:ip地址详解,小学生都看的懂
  13. google 输入栏不显示历史搜索记录方法
  14. 电商如何利用API接口获取商品信息数据
  15. zcash官方介绍 zk-SNARK circuit-QAP转化
  16. snmptester 最新版下载地址
  17. [ WARN] [1588040435.867625184]: MessageFilter [target=odom ]: Dropped 97.37% of messages so
  18. matlab化学程序,Matlab在化学的应用
  19. 0x80070070 错误
  20. vue纯前端下载表格

热门文章

  1. 手把手教你安装GNS3
  2. HIVE学习系列——windows Hadoop安装(上)
  3. Cadence Pspice添加外部白噪声
  4. 网络基础——IP地址子网掩码MAC地址DNS(详解)
  5. POI 导出Excel 带图片导出 使用XSSFWorkbook
  6. 学习笔记-Matlab算法篇-规划算法
  7. linux虚拟网口不同vlan,Openwrt创建虚拟网口(macvlan)
  8. windows下Oracle 11g数据库每天自动备份的实现方法
  9. class文件批量进行反编译的工具JAD,JODE
  10. 海量数据大课学习笔记(6)-短链平台项目创建+git代码管理+开发分层规范讲解--小滴课堂