1. 程式人生 > >My Dream, My Empire!

My Dream, My Empire!

實現了完整的掃雷,但是很明顯功能不如windows那個。首先,速度跟不上,所有格子出來的時候能明顯的感覺到延遲;其次,沒有那麼好看,我本來想用地雷的圖片,結果同一張圖片不好放在多個按鈕上,於是只有使用那個簡單字元替代;最後,我不是完全清楚掃雷的規則,按照自己的理解設計演算法,有的地方可能跟經典的掃雷有較大出入。
跟之前的純粹的棋盤相比,這個完整的掃雷修改了一下棋盤。另外,我自己覺得之前設計棋盤的時候有的地方好像有點重複,但這個時候整個程式已經作了很多了,也不好推翻重來,如果各位有興趣,希望能夠進一步改進,並請將你的結果發到我的郵箱([email protected]),我好好學學,共同進步。至於我自己,恐怕不大會去進一步改進它了,接下來有很多事情要去做了,沒有這麼多閒功夫了。使用Py2exe轉換成exe檔案之後,在別人的沒有Python環境的XP上也能夠成功執行,但是整個需要的東西一共有5M多,實際上這個程式只有21K。。。。。。
整個程式500多行,有3個class。請不要嘲笑我的程式碼風格有問題,註釋不夠好,我只是做著玩,儘量做好,Python的很多東西都還沒有學進去呢。做的時候因為Python的材料不多,我看Nutshell也不夠,有時候也不夠,還是自己去Tkinter.py的原始碼裡邊找的。整個原始碼如下:
  1. #coding:UTF-8
  2. #Author: Hegc Huang
  3. import time, random
  4. import sys
  5. import Tkinter, tkMessageBox, tkFont
  6. from types import *
  7. __AUTHOR__  = "Hegc Huang"
  8. __NAME__    = "Mine Game"
  9. __VERSION__ = "1.0"
  10. __LOSE__    = 'lose.gif'
  11. __WIN__     = 'win.gif'
  12. __LBEVENT__ = '<ButtonRelease-1>'
  13. __RBEVENT__ = '<ButtonRelease-3>'
  14. __TITLE__   = 
    'Mine Game v1.0 - Hegc Huang for Jessica'
  15. #class for Mines
  16. class Mines:
  17. """Class for the Mine Game's data structure.
  18.     when initializing the game, note that:
  19.     Mine(minecount=16, width=9, height=None)
  20.     width: Chessboard's width.
  21.     minecount: Count of mines.
  22.     height: Chessboard's height.
  23.     minecount must less than width*height, and if height isn't set, it equals width as default.
  24.     so, Mines() means a 9*9 chessboard, and 16 mines;
  25.     Mines(17, 10) means 10*10 chessboard, and 17 mines;
  26.     Mines(17, 10, 11) means 10*11 chessboard, and 17 mines.
  27.     as a result of random generator, minecount may change to a realistic number.
  28.     """
  29.     def __init__ (self, minecount=16, width=9, height=None):
  30. if height == None:
  31.             height = width
  32. self._width        = width       #for private use
  33. self._minecount    = minecount     #for private use
  34. self._height       = height       #
  35. #print "width= %d, height= %d" % (width, height)
  36. ifself._minecount>=self._width * self._height:
  37. print'too small a chessboard. to exit.'
  38.             sys.exit()
  39. self.mines = None
  40. self.chessboard = None
  41. self.reset(minecount, width, height)
  42. #self.mines          = [0 for x in range(minecount)] #each 0, total minecount
  43. #self.chessboard     = [[0 for x in range(width)] for y in range(height)]#size: width*height
  44. #print self.chessboard
  45. #self.__initialize()
  46. def __initialize (self):
  47.         random.seed(time.time())    #set seed for random
  48.         count   = 0
  49.         size    = self._width * self._height - 1
  50. while count<self._minecount:
  51.             randresult = int(random.random()*size + 1)    #random for chess
  52. ifnotself.check(count, randresult):
  53.                 randresult = int(random.random()*size + 1)    #random for chess
  54. self.mines[count] = randresult
  55.             count += 1
  56. del randresult
  57. #end initialize mines[]
  58. #self.mines.sort()  #unuseful
  59. #chessboard init
  60. for r inself.mines:
  61.             x = r//self._width
  62.             y = r%self._width
  63. #print 'x = %d, y = %d' % (x, y)
  64. ifself.chessboard[x][y] == -1:
  65. self.mines.remove(r)
  66. continue
  67. self.chessboard[x][y] = -1
  68. #
  69. self._minecount = len(self.mines)
  70.         allmines = 0#all indeed mines
  71.         cx = 0
  72. while cx<self._height:
  73.             cy = 0
  74. while cy<self._width:
  75.                 c = self.getcount(cx, cy)
  76. #print 'c =  ', c
  77. if c==-1:
  78.                     allmines += 1
  79. self.chessboard[cx][cy] = c
  80.                 cy += 1
  81. #print self.chessboard[cx][:self._height]
  82.             cx += 1
  83. #self.minecount = allmines
  84. #end initialize chessboard[][]
  85. #print "Mines : ", self.mines, ' ;XX; ', allmines
  86. #print 'All mines = ', self._minecount
  87. def check (self, count, rr):
  88. ifself.mines[:count].__contains__(rr):
  89. returnFalse
  90. returnTrue
  91. #for external call
  92. def ismine (self, x, y):
  93. ifself.chessboard[x][y] == -1:
  94. returnTrue;
  95. returnFalse
  96. def getcount (self, x, y):
  97. #print 'x=%d, y=%d' % (x, y)
  98.         ret = 0;
  99. ifself.chessboard[x][y]==-1:
  100.             ret =-1
  101. elif x==0and y==0andself.chessboard[x][y]!=-1:
  102. ifself.chessboard[x+1][y] == -1:
  103.                 ret += 1
  104. ifself.chessboard[x+1][y+1] == -1:
  105.                 ret +=1
  106. ifself.chessboard[x][y+1] == -1:
  107.                 ret +=1
  108. elif x==self._height-1and y==self._width-1andself.chessboard[x][y]!=-1:
  109. ifself.chessboard[x-1][y] == -1:
  110.                 ret += 1
  111. ifself.chessboard[x-1][y-1] == -1:
  112.                 ret +=1
  113. ifself.chessboard[x][y-1] == -1:
  114.                 ret +=1
  115. elif y==0andself.chessboard[x][y]!=-1:
  116. ifself.chessboard[x-1][y] == -1:
  117.                 ret += 1
  118. ifself.chessboard[x-1][y+1] == -1:
  119.                 ret +=1
  120. ifself.chessboard[x][y+1] == -1:
  121.                 ret +=1
  122. if x < self._height-1:
  123. ifself.chessboard[x+1][y] == -1:
  124.                     ret += 1
  125. ifself.chessboard[x+1][y+1] == -1:
  126.                     ret += 1
  127. elif x==0andself.chessboard[x][y]!=-1:
  128. ifself.chessboard[x+1][y] == -1:
  129.                 ret += 1
  130. ifself.chessboard[x+1][y-1] == -1:
  131.                 ret +=1
  132. ifself.chessboard[x][y-1] == -1:
  133.                 ret +=1
  134. if y < self._width-1:
  135. ifself.chessboard[x+1][y+1] == -1:
  136.                     ret += 1
  137. ifself.chessboard[x][y+1] == -1:
  138.                     ret += 1
  139. elif x==self._height-1andself.chessboard[x][y]!=-1:
  140. ifself.chessboard[x-1][y] == -1:
  141.                 ret += 1
  142. ifself.chessboard[x-1][y-1] == -1:
  143.                 ret +=1
  144. ifself.chessboard[x][y-1] == -1:
  145.                 ret +=1
  146. ifself.chessboard[x-1][y+1] == -1:
  147.                 ret += 1
  148. ifself.chessboard[x][y+1] == -1:
  149.                 ret += 1
  150. elif y==self._width-1andself.chessboard[x][y]!=-1:
  151. ifself.chessboard[x-1][y] == -1:
  152.                 ret += 1
  153. ifself.chessboard[x+1][y] == -1:
  154.                 ret +=1
  155. ifself.chessboard[x][y-1] == -1:
  156.                 ret +=1
  157. ifself.chessboard[x+1][y-1] == -1:
  158.                 ret += 1
  159. ifself.chessboard[x-1][y-1] == -1:
  160.                 ret += 1
  161. elifself.chessboard[x][y]!=-1:
  162. ifself.chessboard[x-1][y-1] == -1:
  163.                 ret += 1
  164. ifself.chessboard[x-1][y] == -1:
  165.                 ret +=1
  166. ifself.chessboard[x-1][y+1] == -1:
  167.                 ret +=1
  168. ifself.chessboard[x][y+1] == -1:
  169.                 ret += 1
  170. ifself.chessboard[x+1][y+1] == -1:
  171.                 ret += 1
  172. ifself.chessboard[x+1][y] == -1:
  173.                 ret +=1
  174. ifself.chessboard[x+1][y-1] == -1:
  175.                 ret += 1
  176. ifself.chessboard[x][y-1] == -1:
  177.                 ret += 1
  178. return ret
  179. #end getcount
  180. def reset (self, minecount = 16, width = 9, height = 9):
  181. ifself.mines:
  182. delself.mines
  183. ifself.chessboard:
  184. delself.chessboard
  185. self._width         = width
  186. self._height        = height
  187. self._minecount     = minecount
  188. self.mines          = [0for x in range(minecount)] #each 0, total minecount
  189. self.chessboard     = [[0for x in range(width)] for y in range(height)]#size: width*height
  190. self.__initialize()
  191. #print self.chessboard
  192. #the following two methods is from Tkinter.py
  193. def _flatten(tuple):
  194. """Internal function."""
  195.     res = ()
  196. for item in tuple:
  197. if type(item) in (TupleType, ListType):
  198.             res = res + _flatten(item)
  199. elif item isnotNone:
  200.             res = res + (item,)
  201. return res
  202. def _cnfmerge(cnfs):
  203. """Internal function."""
  204. if type(cnfs) is DictionaryType:
  205. return cnfs
  206. elif type(cnfs) in (NoneType, StringType):
  207. return cnfs
  208. else:
  209.         cnf = {}
  210. for c in _flatten(cnfs):
  211. try:
  212.                 cnf.update(c)
  213. except (AttributeError, TypeError), msg:
  214. print"_cnfmerge: fallback due to:", msg
  215. for k, v in c.items():
  216.                     cnf[k] = v
  217. return cnf
  218. #class for Game
  219. class Game:
  220. def __init__ (self, master, mines):
  221. self._mines = mines
  222. self._errormines = []
  223. self._master = master
  224. self._font = tkFont.Font(weight=tkFont.BOLD, size=12)
  225. self._allcount = mines._minecount
  226. self.createimage()
  227. self.initframe()
  228. def createimage (self):
  229. self.__LOSE__ = Tkinter.PhotoImage(file=__LOSE__)
  230. self.__WIN__  = Tkinter.PhotoImage(file=__WIN__)
  231. def power (self, event):
  232.         w = event.widget
  233.         t = w.cget('text')
  234. if t == 'l':
  235.             w.config(text='w', image=self.__WIN__)
  236.         _m = self._mines
  237. self.restart(_m._minecount, _m._width, _m._height)
  238. def restart (self, ms, row, column, rs = True): #rs = True for real restart
  239. #print 'ms = %d; r = %d, c = %d' % (ms, row, column)
  240. ifself.mainframe:
  241. self.mainframe.destroy()
  242. if rs:
  243. self._mines.reset(ms, row, column)
  244. self._allcount = self._mines._minecount
  245. self._errormines = []
  246. self.countlabel.config(text=self._allcount, font=self._font, fg='#FA3CAD')
  247. self.mainframe = Tkinter.Frame(self._master)
  248. self.buildmain(self.mainframe)
  249. self.mainframe.pack(side=Tkinter.BOTTOM, fill=Tkinter.BOTH)
  250. def initframe (self):
  251. self.funcframe = Tkinter.Frame(self._master)
  252. #
  253. self.countlabel = Tkinter.Label(self.funcframe)   #display the number of mines
  254. self.countlabel.pack(side = Tkinter.LEFT, expand=3, fill=Tkinter.BOTH)
  255. #
  256. #self.canvas = Tkinter.Canvas(self.funcframe)
  257. #self.canvas.pack(side = Tkinter.LEFT, fill=Tkinter.X)
  258. #self.canvas.create_text(10, 10, text=self._mines.minecount, fill='#FF3333')
  259. #
  260. self.powerbutton = Tkinter.Button(self.funcframe, image=self.__WIN__, text='w')
  261. self.powerbutton.bind(__LBEVENT__, self.power)
  262. self.powerbutton.pack(side=Tkinter.RIGHT)
  263. #
  264. self.funcframe.pack(side=Tkinter.TOP)
  265. #
  266. self.mainframe = None
  267.         _m = self._mines
  268. self.restart(_m._minecount, _m._width, _m._height, False)#use restart to init
  269. def buildmain (self, mf):
  270.         width  = self._mines._width
  271.         height = self._mines._height
  272. #mf     = self.mainframe
  273.         r = 0
  274. #print "R=%d, C=%d" % (height, width)
  275. while r < height:
  276.             c = 0
  277. while c < width:
  278.                 button = Tkinter.Button(mf,  text='', width=2, height=1)#width=2, height=1
  279.                 button.grid(row=r,column=c,rowspan=1, columnspan=1)
  280.                 button.bind(__LBEVENT__, self.lbclick)
  281.                 button.bind(__RBEVENT__, self.lbclick)
  282. #print "c = %d" % c
  283.                 c += 1
  284.             r += 1
  285. #print "r = %d" % r
  286. def lbclick (self, event):#handle click the chessboard
  287.         w = event.widget
  288.         g = w.grid_info()
  289.         r = int(g['row'])   #for row
  290.         c = int(g['column'])#for column
  291.         mnum = event.num    #mouse button
  292. #print u"滑鼠num = ", mnum
  293. if mnum == 1:       #left button
  294. self.clickleft(w, r, c)
  295. elif mnum == 3:     #right button
  296. self.clickright(w, r, c)
  297. #if self._mines.ismine(r, c):
  298. def clickright (self, w, r, c):
  299.         t = w.cget('text')
  300. if t=='?':
  301.             w.config(text='')
  302.             w.bind(__LBEVENT__, self.lbclick)
  303. elif t=='$':
  304.             w.config(text='?')
  305.             w.unbind(__LBEVENT__)
  306.             _m = self._mines
  307.             ml = r*_m._width + c    #mine location
  308. ifnot _m.ismine(r,c):
  309. self._errormines.remove(ml)
  310. self._allcount += 1#allcount + 1
  311. self.countlabel.config(text=self._allcount, font=self._font, fg='#FA3CAD')
  312. else:
  313.                 _m.mines.append(ml)
  314. else:
  315.             w.config(text='$')
  316.             w.unbind(__LBEVENT__)
  317.             _m = self._mines
  318.             ml = r*_m._width + c    #mine location
  319. ifnot _m.ismine(r,c):
  320. self._errormines.append(ml)
  321. self._allcount -= 1#allcount - 1
  322. self.countlabel.config(text=self._allcount, font=self._font, fg='#FA3CAD')
  323. else:
  324.                 _m.mines.remove(ml)
  325. self._allcount -= 1#allcount - 1
  326. self.countlabel.config(text=self._allcount, font=self._font, fg='#FA3CAD')
  327. ifself._allcount==0:   #all found
  328. if len(self._errormines)>0#some error
  329. self._showresult(r, c)
  330. else:
  331. self._showresult(r, c, 'WIN')
  332. def _showresult (self, r, c, result = 'LOSE'):
  333. if result == 'WIN':
  334.             tkMessageBox.showinfo(message=u'Congratulations!!/nYou Winned.', title=u'Win')
  335. return;
  336. self.powerbutton.config(text='l', image=self.__LOSE__)
  337. for m inself._errormines:
  338.             x = m//self._mines._width
  339.             y = m%self._mines._width
  340. self._forbutton(x, y, True, text='X', state=Tkinter.DISABLED, disabledforeground='#FFAA00', relief=Tkinter.FLAT)   #show all error mines
  341. for m inself._mines.mines:      
  342.             x = m//self._mines._width
  343.             y = m%self._mines._width
  344. self._forbutton(x, y, True, text='#', state=Tkinter.DISABLED, disabledforeground='#FFAFCD', relief=Tkinter.FLAT)   #show all mines, and unbind all action
  345.         tkMessageBox.showinfo(message=u'Sorry!!/nBut you Failed.', title=u'Fail')    #third, tell the result 'LOSE'
  346. def clickleft (self, w, r, c):
  347. ifself._mines.ismine(r, c):    #click a mine, game over
  348.             ml = r*self._mines._width + c
  349. self._mines.mines.remove(ml)
  350. self._forbutton(r, c, True, text='#', state=Tkinter.DISABLED, disabledforeground='#FF0000', relief=Tkinter.FLAT)
  351. #for m in self._mines.mines:      
  352. #    x = m//self._mines._width
  353. #    y = m%self._mines._width
  354. #    self._forbutton(x, y, True, text='#', state=Tkinter.DISABLED, disabledforeground='#FF0000', relief=Tkinter.FLAT)   #show all mines, and unbind all action
  355. #self.powerbutton.config(text='l', image=self.__LOSE__)   
  356. #tkMessageBox.showinfo(message=u'踩到雷了!!', title=u'失敗')    #third, tell the result 'LOSE'
  357. self._showresult(r, c)
  358. else:
  359.             m = self._mines
  360.             t = ''
  361.             n = m.chessboard[r][c]
  362. if n>0:
  363.                 t = '%s' % n
  364.             w.config(relief = Tkinter.FLAT, state = Tkinter.DISABLED, text = t)
  365. self.unbind(w)      #disable, and unbind
  366. if n==0:    #none around
  367. self._showaround(r, c)
  368. 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
  369.         bt = self.mainframe.grid_slaves(row=r, column=c)[0]
  370.         state = bt.cget('state')
  371. if state == Tkinter.DISABLED:
  372. return'done'
  373. #bt.config(relief = Tkinter.FLAT, state = st, text = t, disabledforeground = dfg)
  374. if kw:
  375. #print 'cnf = ', cnf, ' ; kw = ', kw    #cnf is alwayes {}
  376.             cnf = _cnfmerge((cnf, kw))
  377. #print 'CNF = ', cnf
  378.         bt.config(cnf)
  379. if ub:
  380. self.unbind (bt)
  381. returnNone
  382. def _decide (self, r, c):
  383.         n = self._mines.chessboard[r][c]
  384. if n==0:
  385.             st = self._forbutton(r, c, True, state=Tkinter.DISABLED, relief=Tkinter.FLAT)
  386. #print 'st = ', st
  387. if st == None#hasn't handled
  388. self._showaround(r, c)
  389. else:
  390. self._forbutton(r, c, True, text = '%s' % n, state=Tkinter.DISABLED, relief=Tkinter.FLAT)
  391. def _showaround (self, r, c):
  392.         cb = self._mines.chessboard
  393. if r>0:     
  394.             _r = r-1#north
  395.             _c = c
  396. #self._forbutton(_r, _c, '')
  397. self._decide(_r, _c)
  398. if c>0:     #northwest
  399.                 _r = r-1
  400.                 _c = c-1
  401. #self._forbutton(_r, _c, '')
  402. self._decide(_r, _c)
  403. if c<self._mines._width-1#northeast
  404.                 _r = r-1
  405.                 _c = c+1
  406. #self._forbutton(_r, _c, '')
  407. self._decide(_r, _c)
  408. if r<self._mines._height-1:
  409.             _r = r+1#south
  410.             _c = c
  411. #self._forbutton(_r, _c, '')
  412. self._decide(_r, _c)
  413. if c>0:     #southwest
  414.                 _r = r+1
  415.                 _c = c-1
  416. #self._forbutton(_r, _c, '')
  417. self._decide(_r, _c)
  418. if c<self._mines._width-1#southeast
  419.                 _r = r+1
  420.                 _c = c+1
  421. #self._forbutton(_r, _c, '')
  422. self._decide(_r, _c)
  423. if c>0:     #west
  424.             _r = r
  425.             _c = c-1
  426. #self._forbutton(_r, _c, '')
  427. self._decide(_r, _c)
  428. if c<self._mines._width-1:  #east
  429.             _r = r
  430.             _c = c+1
  431. #self._forbutton(_r, _c, '')
  432. self._decide(_r, _c)
  433. def unbind (self, w):
  434.         w.unbind(__LBEVENT__)
  435.         w.unbind(__RBEVENT__)
  436. #main class
  437. class MineGame:
  438. def __init__ (self):
  439. self.mine = Mines(401616)   #預設:中級
  440. self.root = Tkinter.Tk()
  441. self.root.title(__TITLE__)
  442. self.game = Game(self.root, self.mine)
  443. self._addmenu(self.root)
  444. self.root.wm_resizable(FalseFalse)    #can not resize
  445. self.root.mainloop()
  446. def _addmenu (self, _master):
  447.         bar = Tkinter.Menu()
  448.         fil = Tkinter.Menu()
  449. for x in u'初級', u'中級', u'高階', u'自定義''-', u'退出':
  450. if x=='-':
  451.                 fil.add_separator()
  452. else:
  453.                 fil.add_command(label=x, command = lambda x=x: self.newgame(x))
  454.         bar.add_cascade(label=u'遊戲', menu=fil)
  455.         _master.config(menu = bar)
  456. def newgame (self, x):
  457. if x==u'初級':
  458. self.game.restart(1099)
  459. elif x==u'中級':
  460. self.game.restart(401616)
  461. elif x==u'高階':
  462. self.game.restart(993016)
  463. elif x==u'自定義':
  464. def _okb ():
  465. try:
  466.                     wd = int(wfunc.get())
  467.                     hg = int(hfunc.get())
  468.                     mc = int(cfunc.get())
  469. except TypeError:
  470.                     tkMessageBox.showwarning(message=u'請輸入正確的數字', title='警告')
  471. return
  472.                 m = wd * hg
  473. if m//mc>10:
  474.                     tkMessageBox.showwarning(message=u'棋盤太大了,而雷太少了。/n請確保地雷數目不少於棋盤大小的1/10。', title='警告')
  475. return
  476. elif m//mc<3:
  477.                     tkMessageBox.showwarning(message=u'棋盤太小了,而雷太多了。/n請確保地雷數目不超過棋盤大小的1/4。', title='警告')
  478. return
  479.                 root.destroy()
  480. self.root.wm_resizable(TrueTrue)
  481. self.game.restart(mc, wd, hg)
  482. self.root.wm_resizable(FalseFalse)
  483. def _ccb ():
  484.                 root.destroy()
  485.             root = Tkinter.Tk()
  486.             fram = Tkinter.Frame(root)
  487.             Tkinter.Label(fram, text=u'寬度(6~30  ): ').pack(side=Tkinter.LEFT)
  488.             wfunc = Tkinter.Entry(fram)
  489.             wfunc.pack(side=Tkinter.RIGHT, fill=Tkinter.BOTH, expand=1)
  490.             fram.pack()
  491.             fram = Tkinter.Frame(root)
  492.             Tkinter.Label(fram, text=u'高度(6~24  ): ').pack(side=Tkinter.LEFT)
  493.             hfunc = Tkinter.Entry(fram)
  494.             hfunc.pack(side=Tkinter.RIGHT, fill=Tkinter.BOTH, expand=1)
  495.             fram.pack()
  496.             fram = Tkinter.Frame(root)
  497.             Tkinter.Label(fram, text=u'雷數(6~180): ').pack(side=Tkinter.LEFT)
  498.             cfunc = Tkinter.Entry(fram)
  499.             cfunc.pack(side=Tkinter.RIGHT, fill=Tkinter.BOTH, expand=1)
  500.             fram.pack()
  501.             fram = Tkinter.Frame(root)
  502.             Tkinter.Button(fram,text=u'確定', command=_okb).pack(side= Tkinter.LEFT)
  503.             Tkinter.Button(fram,text=u'取消', command=_ccb).pack(side= Tkinter.RIGHT)
  504.             fram.pack()
  505.             root.mainloop()
  506. elif x==u'退出':
  507.             sys.exit()
  508. #main start function
  509. def start (arg):
  510. if arg=='c':
  511.         mines = Mines(169)
  512. print Mines.__doc__
  513.         _l = len(mines.chessboard)
  514.         l = 0
  515. while l<_l:
  516. for i in mines.chessboard[:][l]:
  517. if i==-1:
  518.                     i