实现了完整的扫雷,但是很明显功能不如windows那个。首先,速度跟不上,所有格子出来的时候能明显的感觉到延迟;其次,没有那么好看,我本来想用地雷的图片,结果同一张图片不好放在多个按钮上,于是只有使用那个简单字符替代;最后,我不是完全清楚扫雷的规则,按照自己的理解设计算法,有的地方可能跟经典的扫雷有较大出入。
跟之前的纯粹的棋盘相比,这个完整的扫雷修改了一下棋盘。另外,我自己觉得之前设计棋盘的时候有的地方好像有点重复,但这个时候整个程序已经作了很多了,也不好推翻重来,如果各位有兴趣,希望能够进一步改进,并请将你的结果发到我的邮箱( hjjhegc@yahoo.com.cn),我好好学学,共同进步。至于我自己,恐怕不大会去进一步改进它了,接下来有很多事情要去做了,没有这么多闲功夫了。使用Py2exe转换成exe文件之后,在别人的没有Python环境的XP上也能够成功运行,但是整个需要的东西一共有5M多,实际上这个程序只有21K。。。。。。
整个程序500多行,有3个class。请不要嘲笑我的代码风格有问题,注释不够好,我只是做着玩,尽量做好,Python的很多东西都还没有学进去呢。做的时候因为Python的材料不多,我看Nutshell也不够,有时候也不够,还是自己去Tkinter.py的源码里边找的。整个原代码如下:
跟之前的纯粹的棋盘相比,这个完整的扫雷修改了一下棋盘。另外,我自己觉得之前设计棋盘的时候有的地方好像有点重复,但这个时候整个程序已经作了很多了,也不好推翻重来,如果各位有兴趣,希望能够进一步改进,并请将你的结果发到我的邮箱( hjjhegc@yahoo.com.cn),我好好学学,共同进步。至于我自己,恐怕不大会去进一步改进它了,接下来有很多事情要去做了,没有这么多闲功夫了。使用Py2exe转换成exe文件之后,在别人的没有Python环境的XP上也能够成功运行,但是整个需要的东西一共有5M多,实际上这个程序只有21K。。。。。。
整个程序500多行,有3个class。请不要嘲笑我的代码风格有问题,注释不够好,我只是做着玩,尽量做好,Python的很多东西都还没有学进去呢。做的时候因为Python的材料不多,我看Nutshell也不够,有时候也不够,还是自己去Tkinter.py的源码里边找的。整个原代码如下:
- #coding:UTF-8
- #Author: Hegc Huang
- import time, random
- import sys
- import Tkinter, tkMessageBox, tkFont
- from types import *
- __AUTHOR__ = "Hegc Huang"
- __NAME__ = "Mine Game"
- __VERSION__ = "1.0"
- __LOSE__ = 'lose.gif'
- __WIN__ = 'win.gif'
- __LBEVENT__ = '<ButtonRelease-1>'
- __RBEVENT__ = '<ButtonRelease-3>'
- __TITLE__ = 'Mine Game v1.0 - Hegc Huang for Jessica'
- #class for Mines
- class Mines:
- """Class for the Mine Game's data structure.
- when initializing the game, note that:
- Mine(minecount=16, width=9, height=None)
- width: Chessboard's width.
- minecount: Count of mines.
- height: Chessboard's height.
- minecount must less than width*height, and if height isn't set, it equals width as default.
- so, Mines() means a 9*9 chessboard, and 16 mines;
- Mines(17, 10) means 10*10 chessboard, and 17 mines;
- Mines(17, 10, 11) means 10*11 chessboard, and 17 mines.
- as a result of random generator, minecount may change to a realistic number.
- """
- def __init__ (self, minecount=16, width=9, height=None):
- if height == None:
- height = width
- self._width = width #for private use
- self._minecount = minecount #for private use
- self._height = height #
- #print "width= %d, height= %d" % (width, height)
- if self._minecount>=self._width * self._height:
- print 'too small a chessboard. to exit.'
- sys.exit()
- self.mines = None
- self.chessboard = None
- self.reset(minecount, width, height)
- #self.mines = [0 for x in range(minecount)] #each 0, total minecount
- #self.chessboard = [[0 for x in range(width)] for y in range(height)]#size: width*height
- #print self.chessboard
- #self.__initialize()
- def __initialize (self):
- random.seed(time.time()) #set seed for random
- count = 0
- size = self._width * self._height - 1
- while count<self._minecount:
- randresult = int(random.random()*size + 1) #random for chess
- if not self.check(count, randresult):
- randresult = int(random.random()*size + 1) #random for chess
- self.mines[count] = randresult
- count += 1
- del randresult
- #end initialize mines[]
- #self.mines.sort() #unuseful
- #chessboard init
- for r in self.mines:
- x = r//self._width
- y = r%self._width
- #print 'x = %d, y = %d' % (x, y)
- if self.chessboard[x][y] == -1:
- self.mines.remove(r)
- continue
- self.chessboard[x][y] = -1
- #
- self._minecount = len(self.mines)
- allmines = 0 #all indeed mines
- cx = 0
- while cx<self._height:
- cy = 0
- while cy<self._width:
- c = self.getcount(cx, cy)
- #print 'c = ', c
- if c==-1:
- allmines += 1
- self.chessboard[cx][cy] = c
- cy += 1
- #print self.chessboard[cx][:self._height]
- cx += 1
- #self.minecount = allmines
- #end initialize chessboard[][]
- #print "Mines : ", self.mines, ' ;XX; ', allmines
- #print 'All mines = ', self._minecount
- def check (self, count, rr):
- if self.mines[:count].__contains__(rr):
- return False
- return True
- #for external call
- def ismine (self, x, y):
- if self.chessboard[x][y] == -1:
- return True;
- return False
- def getcount (self, x, y):
- #print 'x=%d, y=%d' % (x, y)
- ret = 0;
- if self.chessboard[x][y]==-1:
- ret =-1
- elif x==0 and y==0 and self.chessboard[x][y]!=-1:
- if self.chessboard[x+1][y] == -1:
- ret += 1
- if self.chessboard[x+1][y+1] == -1:
- ret +=1
- if self.chessboard[x][y+1] == -1:
- ret +=1
- elif x==self._height-1 and y==self._width-1 and self.chessboard[x][y]!=-1:
- if self.chessboard[x-1][y] == -1:
- ret += 1
- if self.chessboard[x-1][y-1] == -1:
- ret +=1
- if self.chessboard[x][y-1] == -1:
- ret +=1
- elif y==0 and self.chessboard[x][y]!=-1:
- if self.chessboard[x-1][y] == -1:
- ret += 1
- if self.chessboard[x-1][y+1] == -1:
- ret +=1
- if self.chessboard[x][y+1] == -1:
- ret +=1
- if x < self._height-1:
- if self.chessboard[x+1][y] == -1:
- ret += 1
- if self.chessboard[x+1][y+1] == -1:
- ret += 1
- elif x==0 and self.chessboard[x][y]!=-1:
- if self.chessboard[x+1][y] == -1:
- ret += 1
- if self.chessboard[x+1][y-1] == -1:
- ret +=1
- if self.chessboard[x][y-1] == -1:
- ret +=1
- if y < self._width-1:
- if self.chessboard[x+1][y+1] == -1:
- ret += 1
- if self.chessboard[x][y+1] == -1:
- ret += 1
- elif x==self._height-1 and self.chessboard[x][y]!=-1:
- if self.chessboard[x-1][y] == -1:
- ret += 1
- if self.chessboard[x-1][y-1] == -1:
- ret +=1
- if self.chessboard[x][y-1] == -1:
- ret +=1
- if self.chessboard[x-1][y+1] == -1:
- ret += 1
- if self.chessboard[x][y+1] == -1:
- ret += 1
- elif y==self._width-1 and self.chessboard[x][y]!=-1:
- if self.chessboard[x-1][y] == -1:
- ret += 1
- if self.chessboard[x+1][y] == -1:
- ret +=1
- if self.chessboard[x][y-1] == -1:
- ret +=1
- if self.chessboard[x+1][y-1] == -1:
- ret += 1
- if self.chessboard[x-1][y-1] == -1:
- ret += 1
- elif self.chessboard[x][y]!=-1:
- if self.chessboard[x-1][y-1] == -1:
- ret += 1
- if self.chessboard[x-1][y] == -1:
- ret +=1
- if self.chessboard[x-1][y+1] == -1:
- ret +=1
- if self.chessboard[x][y+1] == -1:
- ret += 1
- if self.chessboard[x+1][y+1] == -1:
- ret += 1
- if self.chessboard[x+1][y] == -1:
- ret +=1
- if self.chessboard[x+1][y-1] == -1:
- ret += 1
- if self.chessboard[x][y-1] == -1:
- ret += 1
- return ret
- #end getcount
- def reset (self, minecount = 16, width = 9, height = 9):
- if self.mines:
- del self.mines
- if self.chessboard:
- del self.chessboard
- self._width = width
- self._height = height
- self._minecount = minecount
- self.mines = [0 for x in range(minecount)] #each 0, total minecount
- self.chessboard = [[0 for x in range(width)] for y in range(height)]#size: width*height
- self.__initialize()
- #print self.chessboard
- #the following two methods is from Tkinter.py
- def _flatten(tuple):
- """Internal function."""
- res = ()
- for item in tuple:
- if type(item) in (TupleType, ListType):
- res = res + _flatten(item)
- elif item is not None:
- res = res + (item,)
- return res
- def _cnfmerge(cnfs):
- """Internal function."""
- if type(cnfs) is DictionaryType:
- return cnfs
- elif type(cnfs) in (NoneType, StringType):
- return cnfs
- else:
- cnf = {}
- for c in _flatten(cnfs):
- try:
- cnf.update(c)
- except (AttributeError, TypeError), msg:
- print "_cnfmerge: fallback due to:", msg
- for k, v in c.items():
- cnf[k] = v
- return cnf
- #class for Game
- class Game:
- def __init__ (self, master, mines):
- self._mines = mines
- self._errormines = []
- self._master = master
- self._font = tkFont.Font(weight=tkFont.BOLD, size=12)
- self._allcount = mines._minecount
- self.createimage()
- self.initframe()
- def createimage (self):
- self.__LOSE__ = Tkinter.PhotoImage(file=__LOSE__)
- self.__WIN__ = Tkinter.PhotoImage(file=__WIN__)
- def power (self, event):
- w = event.widget
- t = w.cget('text')
- if t == 'l':
- w.config(text='w', image=self.__WIN__)
- _m = self._mines
- self.restart(_m._minecount, _m._width, _m._height)
- def restart (self, ms, row, column, rs = True): #rs = True for real restart
- #print 'ms = %d; r = %d, c = %d' % (ms, row, column)
- if self.mainframe:
- self.mainframe.destroy()
- if rs:
- self._mines.reset(ms, row, column)
- self._allcount = self._mines._minecount
- self._errormines = []
- self.countlabel.config(text=self._allcount, font=self._font, fg='#FA3CAD')
- self.mainframe = Tkinter.Frame(self._master)
- self.buildmain(self.mainframe)
- self.mainframe.pack(side=Tkinter.BOTTOM, fill=Tkinter.BOTH)
- def initframe (self):
- self.funcframe = Tkinter.Frame(self._master)
- #
- self.countlabel = Tkinter.Label(self.funcframe) #display the number of mines
- self.countlabel.pack(side = Tkinter.LEFT, expand=3, fill=Tkinter.BOTH)
- #
- #self.canvas = Tkinter.Canvas(self.funcframe)
- #self.canvas.pack(side = Tkinter.LEFT, fill=Tkinter.X)
- #self.canvas.create_text(10, 10, text=self._mines.minecount, fill='#FF3333')
- #
- self.powerbutton = Tkinter.Button(self.funcframe, image=self.__WIN__, text='w')
- self.powerbutton.bind(__LBEVENT__, self.power)
- self.powerbutton.pack(side=Tkinter.RIGHT)
- #
- self.funcframe.pack(side=Tkinter.TOP)
- #
- self.mainframe = None
- _m = self._mines
- self.restart(_m._minecount, _m._width, _m._height, False)#use restart to init
- def buildmain (self, mf):
- width = self._mines._width
- height = self._mines._height
- #mf = self.mainframe
- r = 0
- #print "R=%d, C=%d" % (height, width)
- while r < height:
- c = 0
- while c < width:
- button = Tkinter.Button(mf, text='', width=2, height=1)#width=2, height=1
- button.grid(row=r,column=c,rowspan=1, columnspan=1)
- button.bind(__LBEVENT__, self.lbclick)
- button.bind(__RBEVENT__, self.lbclick)
- #print "c = %d" % c
- c += 1
- r += 1
- #print "r = %d" % r
- def lbclick (self, event):#handle click the chessboard
- w = event.widget
- g = w.grid_info()
- r = int(g['row']) #for row
- c = int(g['column'])#for column
- mnum = event.num #mouse button
- #print u"鼠标num = ", mnum
- if mnum == 1: #left button
- self.clickleft(w, r, c)
- elif mnum == 3: #right button
- self.clickright(w, r, c)
- #if self._mines.ismine(r, c):
- def clickright (self, w, r, c):
- t = w.cget('text')
- if t=='?':
- w.config(text='')
- w.bind(__LBEVENT__, self.lbclick)
- elif t=='$':
- w.config(text='?')
- w.unbind(__LBEVENT__)
- _m = self._mines
- ml = r*_m._width + c #mine location
- if not _m.ismine(r,c):
- self._errormines.remove(ml)
- self._allcount += 1 #allcount + 1
- self.countlabel.config(text=self._allcount, font=self._font, fg='#FA3CAD')
- else:
- _m.mines.append(ml)
- else:
- w.config(text='$')
- w.unbind(__LBEVENT__)
- _m = self._mines
- ml = r*_m._width + c #mine location
- if not _m.ismine(r,c):
- self._errormines.append(ml)
- self._allcount -= 1 #allcount - 1
- self.countlabel.config(text=self._allcount, font=self._font, fg='#FA3CAD')
- else:
- _m.mines.remove(ml)
- self._allcount -= 1 #allcount - 1
- self.countlabel.config(text=self._allcount, font=self._font, fg='#FA3CAD')
- if self._allcount==0: #all found
- if len(self._errormines)>0: #some error
- self._showresult(r, c)
- else:
- self._showresult(r, c, 'WIN')
- def _showresult (self, r, c, result = 'LOSE'):
- if result == 'WIN':
- tkMessageBox.showinfo(message=u'Congratulations!!/nYou Winned.', title=u'Win')
- return;
- self.powerbutton.config(text='l', image=self.__LOSE__)
- for m in self._errormines:
- x = m//self._mines._width
- y = m%self._mines._width
- self._forbutton(x, y, True, text='X', state=Tkinter.DISABLED, disabledforeground='#FFAA00', relief=Tkinter.FLAT) #show all error mines
- for m in self._mines.mines:
- x = m//self._mines._width
- y = m%self._mines._width
- self._forbutton(x, y, True, text='#', state=Tkinter.DISABLED, disabledforeground='#FFAFCD', relief=Tkinter.FLAT) #show all mines, and unbind all action
- tkMessageBox.showinfo(message=u'Sorry!!/nBut you Failed.', title=u'Fail') #third, tell the result 'LOSE'
- def clickleft (self, w, r, c):
- if self._mines.ismine(r, c): #click a mine, game over
- ml = r*self._mines._width + c
- self._mines.mines.remove(ml)
- self._forbutton(r, c, True, text='#', state=Tkinter.DISABLED, disabledforeground='#FF0000', relief=Tkinter.FLAT)
- #for m in self._mines.mines:
- # x = m//self._mines._width
- # y = m%self._mines._width
- # self._forbutton(x, y, True, text='#', state=Tkinter.DISABLED, disabledforeground='#FF0000', relief=Tkinter.FLAT) #show all mines, and unbind all action
- #self.powerbutton.config(text='l', image=self.__LOSE__)
- #tkMessageBox.showinfo(message=u'踩到雷了!!', title=u'失败') #third, tell the result 'LOSE'
- self._showresult(r, c)
- else:
- m = self._mines
- t = ''
- n = m.chessboard[r][c]
- if n>0:
- t = '%s' % n
- w.config(relief = Tkinter.FLAT, state = Tkinter.DISABLED, text = t)
- self.unbind(w) #disable, and unbind
- if n==0: #none around
- self._showaround(r, c)
- def _forbutton (self, r, c, ub, cnf={}, **kw): #imitate the Button.__init__(), in fact, 'n=v's impose on kw(tuple). ub true for unbind
- bt = self.mainframe.grid_slaves(row=r, column=c)[0]
- state = bt.cget('state')
- if state == Tkinter.DISABLED:
- return 'done'
- #bt.config(relief = Tkinter.FLAT, state = st, text = t, disabledforeground = dfg)
- if kw:
- #print 'cnf = ', cnf, ' ; kw = ', kw #cnf is alwayes {}
- cnf = _cnfmerge((cnf, kw))
- #print 'CNF = ', cnf
- bt.config(cnf)
- if ub:
- self.unbind (bt)
- return None
- def _decide (self, r, c):
- n = self._mines.chessboard[r][c]
- if n==0:
- st = self._forbutton(r, c, True, state=Tkinter.DISABLED, relief=Tkinter.FLAT)
- #print 'st = ', st
- if st == None: #hasn't handled
- self._showaround(r, c)
- else:
- self._forbutton(r, c, True, text = '%s' % n, state=Tkinter.DISABLED, relief=Tkinter.FLAT)
- def _showaround (self, r, c):
- cb = self._mines.chessboard
- if r>0:
- _r = r-1 #north
- _c = c
- #self._forbutton(_r, _c, '')
- self._decide(_r, _c)
- if c>0: #northwest
- _r = r-1
- _c = c-1
- #self._forbutton(_r, _c, '')
- self._decide(_r, _c)
- if c<self._mines._width-1: #northeast
- _r = r-1
- _c = c+1
- #self._forbutton(_r, _c, '')
- self._decide(_r, _c)
- if r<self._mines._height-1:
- _r = r+1 #south
- _c = c
- #self._forbutton(_r, _c, '')
- self._decide(_r, _c)
- if c>0: #southwest
- _r = r+1
- _c = c-1
- #self._forbutton(_r, _c, '')
- self._decide(_r, _c)
- if c<self._mines._width-1: #southeast
- _r = r+1
- _c = c+1
- #self._forbutton(_r, _c, '')
- self._decide(_r, _c)
- if c>0: #west
- _r = r
- _c = c-1
- #self._forbutton(_r, _c, '')
- self._decide(_r, _c)
- if c<self._mines._width-1: #east
- _r = r
- _c = c+1
- #self._forbutton(_r, _c, '')
- self._decide(_r, _c)
- def unbind (self, w):
- w.unbind(__LBEVENT__)
- w.unbind(__RBEVENT__)
- #main class
- class MineGame:
- def __init__ (self):
- self.mine = Mines(40, 16, 16) #默认:中级
- self.root = Tkinter.Tk()
- self.root.title(__TITLE__)
- self.game = Game(self.root, self.mine)
- self._addmenu(self.root)
- self.root.wm_resizable(False, False) #can not resize
- self.root.mainloop()
- def _addmenu (self, _master):
- bar = Tkinter.Menu()
- fil = Tkinter.Menu()
- for x in u'初级', u'中级', u'高级', u'自定义', '-', u'退出':
- if x=='-':
- fil.add_separator()
- else:
- fil.add_command(label=x, command = lambda x=x: self.newgame(x))
- bar.add_cascade(label=u'游戏', menu=fil)
- _master.config(menu = bar)
- def newgame (self, x):
- if x==u'初级':
- self.game.restart(10, 9, 9)
- elif x==u'中级':
- self.game.restart(40, 16, 16)
- elif x==u'高级':
- self.game.restart(99, 30, 16)
- elif x==u'自定义':
- def _okb ():
- try:
- wd = int(wfunc.get())
- hg = int(hfunc.get())
- mc = int(cfunc.get())
- except TypeError:
- tkMessageBox.showwarning(message=u'请输入正确的数字', title='警告')
- return
- m = wd * hg
- if m//mc>10:
- tkMessageBox.showwarning(message=u'棋盘太大了,而雷太少了。/n请确保地雷数目不少于棋盘大小的1/10。', title='警告')
- return
- elif m//mc<3:
- tkMessageBox.showwarning(message=u'棋盘太小了,而雷太多了。/n请确保地雷数目不超过棋盘大小的1/4。', title='警告')
- return
- root.destroy()
- self.root.wm_resizable(True, True)
- self.game.restart(mc, wd, hg)
- self.root.wm_resizable(False, False)
- def _ccb ():
- root.destroy()
- root = Tkinter.Tk()
- fram = Tkinter.Frame(root)
- Tkinter.Label(fram, text=u'宽度(6~30 ): ').pack(side=Tkinter.LEFT)
- wfunc = Tkinter.Entry(fram)
- wfunc.pack(side=Tkinter.RIGHT, fill=Tkinter.BOTH, expand=1)
- fram.pack()
- fram = Tkinter.Frame(root)
- Tkinter.Label(fram, text=u'高度(6~24 ): ').pack(side=Tkinter.LEFT)
- hfunc = Tkinter.Entry(fram)
- hfunc.pack(side=Tkinter.RIGHT, fill=Tkinter.BOTH, expand=1)
- fram.pack()
- fram = Tkinter.Frame(root)
- Tkinter.Label(fram, text=u'雷数(6~180): ').pack(side=Tkinter.LEFT)
- cfunc = Tkinter.Entry(fram)
- cfunc.pack(side=Tkinter.RIGHT, fill=Tkinter.BOTH, expand=1)
- fram.pack()
- fram = Tkinter.Frame(root)
- Tkinter.Button(fram,text=u'确定', command=_okb).pack(side= Tkinter.LEFT)
- Tkinter.Button(fram,text=u'取消', command=_ccb).pack(side= Tkinter.RIGHT)
- fram.pack()
- root.mainloop()
- elif x==u'退出':
- sys.exit()
- #main start function
- def start (arg):
- if arg=='c':
- mines = Mines(16, 9)
- print Mines.__doc__
- _l = len(mines.chessboard)
- l = 0
- while l<_l:
- for i in mines.chessboard[:][l]:
- if i==-1:
- i = '/x0F'
- print i, ' ',/
- print ''
- l += 1
- else:
- MineGame()
- argc = len(sys.argv)
- if argc>1:
- arg = sys.argv[1]
- start(arg)
- else:
- start('g')