from tkinter import *
import tkinter.messagebox as messagebox
import random
class main:
# 定义一个类,继承 tkinter 的 Button
# 用来保存按钮的状态和在网格布局中的位置
class minebtn(Button):
def __init__(self,master,xy,**kw):
Button.__init__(self,master,**kw)
self.xy = xy
self._state = 0
# 状态
# 0: 未点开
# 1: 已点开
# 2: 标记
# 3: 问号
def __init__(self):
# 定义规格及雷数
self.mode_list = [(9, 9, 10), (16, 16, 40), (32, 16, 99)]
self.mode = 2
self.width = self.mode_list[self.mode][0]
self.height = self.mode_list[self.mode][1]
self.minenum = self.mode_list[self.mode][2]
# 雷数的颜色
self.colorlist = ['green', # 绿色
'DodgerBlue', # 浅蓝色
'goldenrod',#
'blue', # 蓝色
'red', # 红色
'Chocolate4', # 棕色
'grey', # 灰色
'black'] # 黑色
self.root = Tk()
self.root.title('扫雷')
winfoWt = 30 * self.width
winfoHt = 31 * self.height
x = int((self.root.winfo_screenwidth() - winfoWt) / 2)
y = int((self.root.winfo_screenheight() - winfoHt) / 2 - 50)
self.root.geometry('+{}+{}'.format(x, y))
self.easyOrNo = Button(self.root, text='初级', command=self.EasyOrNo)
self.easyOrNo.grid(row=0, column=self.width - 3, columnspan=3)
self.setgui()
self.root.mainloop()
def setgui(self):
# GUI界面
self.mark = set()
self.rest = self.minenum # 剩余未标记的雷
self.restlabel = Label(self.root, text=f'雷数:{self.minenum}')
self.restlabel.grid(row=0, column=0, columnspan=3)
self.restart = Button(self.root, text='重开', command=self.Restart)
self.restart.grid(row=0, column=self.width - 7, columnspan=3)
self.chgmode = Button(self.root, text='大小', command=self.chgMode)
self.chgmode.grid(row=0, column=self.width - 5, columnspan=3)
self.mineplace = random.sample( range(self.width * self.height), self.minenum ) # 随机抽取雷
self.mineplace = [ ( x % self.width, x // self.width ) for x in self.mineplace ] # 将雷的序号转变为坐标
self.mines = {}
self.index = -1
for y in range(self.height):
for x in range(self.width):
# s = Style()
# s.configure('myButton', font('SimHei', 12, 'bold'))
self.mines[(x, y)] = self.minebtn(self.root, xy=(x,y), font=('黑体', 14, 'bold'), height=1, width=2, bd=1, relief='ridge')
self.mines[(x, y)].bind('<ButtonRelease-1>', lambda event:self._open(event.widget)) # 左键单击点开
self.mines[(x, y)].bind('<ButtonRelease-3>', lambda event:self.make(event.widget)) # 右键单击事件
self.mines[(x, y)].grid( row=y+1, column=x, sticky='nswe')
# print(self.mines[(1, 1)].winfo_reqwidth(), self.mines[(1, 1)].winfo_reqheight())
# 点开
def _open(self, widget):
# 确保第一次点击不碰到雷
self.index += 1
if self.index == 0:
_sum = self.sum_around0(widget)
if (widget.xy in self.mineplace) or _sum != 0:
self.mineplace = random.sample( range(self.width * self.height), self.minenum ) # 随机抽取雷
self.mineplace = [ ( x % self.width, x // self.width ) for x in self.mineplace ] # 将雷的序号转变为坐标
self.index = -1
self._open(widget)
xy = widget.xy
x = xy[0]
y = xy[1] # 获取当前按钮的坐标
# 如果已经点开了就什么也不做
if widget._state == 1:
return
if widget._state == 2:
return
# 如果是雷则显示全部雷的位置
if widget.xy in self.mineplace:
self.showmine(widget)
return
widget.configure(relief='flat', bg='white') # 更改当前按钮的样式
widget._state = 1 # 按钮状态设为点开
# 获取周围八个可能的雷的坐标
_sum, around_ = self.sum_around(widget)
#如果周围没有雷则打开周围未标记的雷,直到有雷为止
if _sum == 0:
widget['text'] = '0'
widget['fg'] = 'Lime'
for i, j in around_:
if self.mines[(i,j)]._state == 0:
self._open(self.mines[(i,j)])
else:
widget['text'] = _sum # 显示雷数
# 对应数字设置对应颜色
widget['fg'] = self.colorlist[_sum-1]
# 右键单击设置标记/问号
def make(self, widget):
#string = {0:'',2:'♀',3:'?'}
string = {0:'',2:'*',3:'?'}
if widget._state == 0:
widget._state = 2
widget['text'] = string[2]
widget['fg'] = 'black'
self.rest -= 1
self.restlabel['text'] = f'剩余:{self.rest}'
self.mark.add(widget.xy)
elif widget._state == 2:
widget._state = 3
widget['text'] = string[3]
widget['fg'] = 'red'
self.rest += 1
self.restlabel['text'] = f'剩余:{self.rest}'
self.mark.remove(widget.xy)
elif widget._state == 3:
widget._state = 0
widget['text'] = string[0]
self.display_messagebox()
# print(self.mark)
# 如果踩到雷,显示所有的雷
def showmine(self, widget):
self.rest = 0
self.restlabel['text'] = f'雷数:{self.rest}'
minePlace = set(self.mineplace)
markErr = self.mark - minePlace
mineRemain = minePlace - (self.mark & minePlace)
for i, j in mineRemain:
self.mines[(i, j)].configure(relief='flat', bg='white', text='ி', fg='purple') # 更改当前按钮的样式
self.mines[(i, j)]._state = 1
# 高亮当前点错的位置
widget.configure(relief='flat', bg='crimson', text='ி', fg='white')
widget._state = 1
curtClr = 0
self.flash(markErr, curtClr)
# 对于标记错误的, 在踩雷之后, 需要闪烁
def flash(self, markErr, curtClr):
for i, j in markErr:
self.mines[(i, j)]['fg'] = 'crimson'
def sum_around(self, widget):
# 获取周围八个可能的雷的坐标
x = widget.xy[0]
y = widget.xy[1]
around = [(x-1,y-1),
(x,y-1),
(x+1,y-1),
(x-1,y),
(x+1,y),
(x-1,y+1),
(x,y+1),
(x+1,y+1)]
_sum = 0
around_ = []
for o, p in around:
# 排除掉在雷区之外的位置
if 0 <= o <= self.width - 1 and 0 <= p <= self.height - 1:
around_.append((o,p))
# 计算周围的雷数
if self.mines[(o,p)].xy in self.mineplace:
_sum += 1
return (_sum, around_)
def sum_around0(self, widget):
# 获取周围八个可能的雷的坐标
x = widget.xy[0]
y = widget.xy[1]
around = [(x-1,y-1),
(x,y-1),
(x+1,y-1),
(x-1,y),
(x+1,y),
(x-1,y+1),
(x,y+1),
(x+1,y+1)]
_sum = 0
around_ = []
for o, p in around:
# 排除掉在雷区之外的位置
if 0 <= o <= self.width - 1 and 0 <= p <= self.height - 1:
around_.append((o,p))
# 计算周围的雷数
if self.mines[(o,p)].xy in self.mineplace:
_sum += 1
return _sum
def Restart(self):
easyOrNo = self.easyOrNo['text']
for widget in self.root.winfo_children():
widget.destroy()
self.easyOrNo = Button(self.root, text=easyOrNo, command=self.EasyOrNo)
self.easyOrNo.grid(row=0, column=self.width - 3, columnspan=3)
self.setgui()
def chgMode(self):
easyOrNo = self.easyOrNo['text']
for widget in self.root.winfo_children():
widget.destroy()
self.mode += 1
self.mode = self.mode % 3
self.width = self.mode_list[self.mode][0]
self.height = self.mode_list[self.mode][1]
self.minenum = self.mode_list[self.mode][2]
self.easyOrNo = Button(self.root, text=easyOrNo, command=self.EasyOrNo)
self.easyOrNo.grid(row=0, column=self.width - 3, columnspan=3)
winfoWt = 30 * self.width
winfoHt = 31 * self.height
x = int((self.root.winfo_screenwidth() - winfoWt) / 2)
y = int((self.root.winfo_screenheight() - winfoHt) / 2 - 50)
self.root.geometry('+{}+{}'.format(x, y))
self.setgui()
def EasyOrNo(self):
if self.easyOrNo['text'] == "初级":
for widget in self.root.winfo_children():
widget.destroy()
self.easyOrNo = Button(self.root, text='中级', command=self.EasyOrNo)
self.easyOrNo.grid(row=0, column=self.width - 3, columnspan=3)
self.mode_list = [(9, 9, 16), (16, 16, 52), (32, 16, 128)]
self.width = self.mode_list[self.mode][0]
self.height = self.mode_list[self.mode][1]
self.minenum = self.mode_list[self.mode][2]
elif self.easyOrNo['text'] == "中级":
for widget in self.root.winfo_children():
widget.destroy()
self.easyOrNo = Button(self.root, text='高级', command=self.EasyOrNo)
self.easyOrNo.grid(row=0, column=self.width - 3, columnspan=3)
self.mode_list = [(9, 9, 25), (16, 16, 64), (32, 16, 153)]
self.width = self.mode_list[self.mode][0]
self.height = self.mode_list[self.mode][1]
self.minenum = self.mode_list[self.mode][2]
elif self.easyOrNo['text'] == "高级":
for widget in self.root.winfo_children():
widget.destroy()
self.easyOrNo = Button(self.root, text='初级', command=self.EasyOrNo)
self.easyOrNo.grid(row=0, column=self.width - 3, columnspan=3)
self.mode_list = [(9, 9, 10), (16, 16, 40), (32, 16, 99)]
self.width = self.mode_list[self.mode][0]
self.height = self.mode_list[self.mode][1]
self.minenum = self.mode_list[self.mode][2]
self.setgui()
def display_messagebox(self):
if self.mark == set(self.mineplace):
messagebox.showinfo(title='SUCCESS',
message='Congratulations! You are so brilliant, let\'s try again ~ ~')
else:
pass
if __name__ == "__main__":
main()
这个代码有个原始版本, 也是从别处copy的, 具体出处我已经忘了. 我在原始版基础上进行了魔改, 效果还可以, 想要一个本地版扫雷小游戏的可以copy下来试试.