日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

我想自己写一个扫雷,用Python

發(fā)布時間:2023/12/10 python 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 我想自己写一个扫雷,用Python 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

今天我是小魚,今天是2021年11月17日,我想自己寫一個掃雷,用Python

我用類似日記的方式記錄我的過程,想看代碼直接拖到最后。

分享出來僅為交流與學習。

前幾天兒子說別的小朋友都有電腦,玩游戲。于是就把家里的筆記本給他玩,但那是一臺工作本,沒有游戲,我就說那你玩掃雷吧,正好鍛煉一下怎么用鼠標。

但我發(fā)現(xiàn)win11的掃雷跟XP很不同,要去appstore下,畫面動畫很累贅,很慢。重點是有廣告!!!強制看30秒,不能取消。每5分鐘就跳出來——孔子不能忍,孟子不能忍,老子也不能忍!

畢竟,XP是2001年出來的,20年過去了——“大人,時代不同了”。

因為最近小半年都在自學Python,于是有了種強烈的要自己寫的想法,雖然CSDN上已經有好多人都寫過,但我想自己寫。不為兒子,就為了練習。

我今天先記錄下我的構想:

1.只有初級,中級,高級,沒有自定義地圖大小功能,地圖大小固定;雷數(shù)先固定,預留可以自定義雷數(shù)的接口。

2.分三個層面,運算層,界面層,控制層。運算層用來生成地圖,自動布雷,自動寫出雷周邊的數(shù)字,數(shù)字要有不同顏色。預留自定義雷數(shù)功能,要增加算法,每個地圖最多能放多少個雷,再多就不合理了。界面層是窗口,窗口里的內容,包括計時,雷數(shù),emoj按鈕,有下拉的菜單。還有雷上的“土”(我管那些灰色的沒有點開的方塊叫“泥土”),還有土上的插旗,以及點擊到空土(下面沒有雷沒有數(shù)字的)時自動打開周邊土直到遇到非空土為止的算法。控制層是收集鼠標的操作,左鍵,右鍵。并執(zhí)行相關的操作,F2是開始新的游戲,F1打開幫助文檔。選擇初級,中級,高級。稍微與XP有點不同的地方,左鍵開土,右鍵插旗,沒有插問號,右鍵雙擊數(shù)字代替原來的左右鍵同擊數(shù)字。

3.先不做掃雷排行榜,待上面功能實現(xiàn)之后再做。

運算層中:根據(jù)控制層選擇的等級,定義二維數(shù)組,數(shù)組大小來自于控制層定義的等級,獲取控制層中第一次開土的坐標,第一次開土作為觸發(fā)運算層的信號,用隨機算法自動布雷,布雷的算法必須避開第一次開土的坐標。數(shù)字算法在布雷算法完成之后,用9表示雷,用數(shù)字表示周邊有多少個9. 用0表示空土(初始所有數(shù)組值都為0)

界面層中:根據(jù)控制層選擇的等級畫出地圖,窗口只有3種大小(不設計自定義地圖)。用類來定義窗口,在類中定義窗口上的各種內容,包括泥土,插旗的泥土。兩種開圖算法一種是左鍵空土,一種是右鍵雙擊開土數(shù)字。

控制層中:捕捉鼠標與鍵盤動作,鼠標動作是左鍵,右鍵,右鍵雙擊。F1,F2。退出游戲通過點擊右上角紅叉和菜單中退出游戲實現(xiàn)。

今天先寫這些。

今天是11月18日,昨天和今天將運算層的代碼寫完了。運行的結果還是比較滿意的。我先貼上運算層的代碼,如果將來發(fā)現(xiàn)我的程序構架有問題,后面再說吧。大不了這個貼就不公開了

# _*_ coding : UTF-8 _*_ # @time: 2021/11/17 13:18 # @file: yunsuan.py # @Author:yyh # @Software:PyCharmimport numpy as np from gongyong import logdef autobulei(dengj, x, y):"""用隨機算法自動布雷,布雷的算法必須避開第一次開土的坐標。數(shù)字算法在布雷算法完成之后,用9表示雷,用數(shù)字表示周邊有多少個9.用0表示空土:param dengj: 用來接收來自控制層的等級選擇:param x,y:是第一次開土時地圖上的坐標位置。:return: 返回的應該是自動布完雷的二維數(shù)組"""daxiao = { # daxiao是一個表驅動,用來儲存不同等級下二維數(shù)組的大小與總的雷數(shù)"chuj": [9, 9, 10],"zhongj": [16, 16, 40],"gaoj": [16, 30, 99]}shuzu_dx = (daxiao[dengj][0], daxiao[dengj][1]) # 獲取數(shù)組的大小shuzu = np.zeros(shuzu_dx, dtype=int) # 創(chuàng)建二維數(shù)組i = 0while i < daxiao[dengj][2]:x_shuzu = np.random.randint(0, daxiao[dengj][0])y_shuzu = np.random.randint(0, daxiao[dengj][1])if shuzu[x_shuzu][y_shuzu] == 9 or ((x_shuzu, y_shuzu) == (x, y)):continue # 如果隨機生成的位置湊巧與第一塊土相同,或者已經布過雷了,那就回到循環(huán)開頭重新隨機else: # 如果是空土shuzu[x_shuzu][y_shuzu] += 9 #這句應該直接等于9,一開始我用+=發(fā)現(xiàn)了自己沒有檢測是否已經布過雷的bug,所以我就保留了+i += 1return shuzudef autoshuzi(shuzu):""":param shuzu:自動布完雷,但是還沒有寫雷周圍的數(shù)字的數(shù)組:return: 返回在雷周圍已經寫完數(shù)字的數(shù)組"""row = len(shuzu)col = len(shuzu[0])sz = shuzu # 換個短一點的名字for i in range(0, row):for j in range(0, col): # 用兩個for循環(huán)對數(shù)組逐行逐列遍歷if sz[i][j] == 9: # 如果是雷,那么我們對雷周圍的8個格子都加上1for n in range(i - 1, i + 2):for m in range(j - 1, j + 2): # 再用一個雙for循環(huán),對雷附近的3x3的區(qū)域遍歷,每個格子+1if n >= 0 and m >= 0 and row - n >= 1 and col - m >= 1 and sz[n][m] < 9:sz[n][m] += 1 # 這句if是判斷當前操作的元素是否在地圖范圍內,并且這個元素他不是雷,然后給這個單元格+1else: # 這句else可以不寫,但為了邏輯完整continueelse: # 這句也是為了邏輯完整。另外,我是遍歷整個數(shù)組中的雷,并不是遍歷整個數(shù)組中每個位置,并且對每個位置統(tǒng)計周邊雷數(shù)。# 所以,只要不是雷我都會跳過,我認為這樣執(zhí)行的語句會比較少。continuereturn szif __name__ == '__main__':ditu = autoshuzi(autobulei('chuj', 3, 6))log("地圖\n", ditu)

但是這個運算速度實在是感人,以前XP上我點下第一格之后馬上就打開了,打開就顯示周邊雷數(shù)了。說明當時地圖都已經布局好了,我的運算過程大概得有1~2秒鐘。哎,先實現(xiàn)功能再說吧。?

今天先到這里吧。今天還有不少事要做。

今天是11月19日,昨天后來找了一個做窗口的庫,叫tkinter,簡稱TK。之前做自學的時候做過一個“小蜜蜂”,當時用的是pygame,對于圖形界面的庫,我用且只用過pygame。但是感覺pygame有點復雜,掃雷這個小游戲使用標準的一些窗口與控件就可以了。既然是學習,就學習使用一個新的庫,而且TK是屬于標準庫,不需要再另外安裝。另外,昨天在網上找了圖片,就是雷的圖片,旗子的圖片。今天不忙的話就好好看一下TK的說明文檔。明天又要上一天的課,只能下周再繼續(xù)了。

現(xiàn)在已經是下午了,今天還是看了不少TK的說明文檔,另外我將yunsuan.py稍微修改了一下,布完雷之后順便再輸出一個列表,里面包含了所有地雷的坐標元組。我發(fā)現(xiàn)后面界面層還有要使用這張列表,游戲失敗后要顯示出所有的雷,但今天就不貼新代碼了,等后面完成了一起貼。另外,今天也看了一部分TK中對于鍵鼠事件的內容。看完我覺得我有信心能完成這個游戲。

今天就到這里吧,不再想掃雷的事了。

今天是11月22日,今天太忙了,沒有做什么工作。

今天是11月24日,昨天休假了,沒有工作,今天學習了tk中的標簽與按鈕控件,我準備用標簽顯示地圖,用按鈕做泥土。但是按鈕的大小與標簽的大小我控制不好。CSDN上有一個叫荷蒲的博主關于tk帖子不錯,鏈接如下:

https://so.csdn.net/so/search?q=tkinter&t=blog&u=hepu8

今天就到這里,太累了,明天要弄明白按鈕的大小與標簽大小都是什么因素影響的。

今天是11月25日,今天仔細在研究按鈕和標簽的大小,我知道按鈕是會按文本的大小來變化的,所以,我弄了好多的參數(shù),有字體,字號,有內邊距,有button自身屬性的width和height。我測試了好多次,希望能找出其中的規(guī)律,但是,我失敗了。標簽的大小與字符像素的大小有很明確的公式關系,但是按鈕的我實在找不到。我把我的結果截圖放在這里,以后有時間再來研究。我用窮舉發(fā)湊出了一個可以使標簽大小與按鈕大小一致的值。

今天先這樣吧。明天將按鈕與標簽都放到frame中,自定義定義一個類。順便明天看看掃雷的數(shù)字都用什么顏色.

今天是11月29日,最近幾天是比較忙的,沒有做什么事,僅僅看了掃雷數(shù)字的顏色,但是我對標簽與按鈕大小的事,有點過于深入了,我測試了Arial3-19字號的字符大小,并且看了對應的寬度增加的步長與高度增加的步長,然后我發(fā)現(xiàn)這是一個挺奇怪的序列,需要用三元一次方程來擬合,我覺得這應該是另外一個話題了,我要開另一個帖子來記錄。明天絕對不再去想這個問題了。明天練習frame類。

傳送門

今天是11月30日,今天其實做了挺多事,我把另一個帖子寫了,另外我也弄了Frame模塊,把主窗口做了一個類,把菜單做了一個類,另外做了兩個Frame,一個用來放游戲信息,另一個用來放游戲的內容。

今天是12月1日,已經是下午四點多了,今天我把地圖上的每一個格子都做成了一個類,是繼承Label的類,每一塊泥土也做成了一個類,是繼承Button的。然后用place布局到游戲內容的框架當中去。但是,今天我遇到一個問題,應該是泥土先生成,然后點擊第一塊泥土時生成地圖,我用Label做地圖,但是后生成的組件會覆蓋在先生成的組件的上面,也就是說地圖被覆蓋到泥土的上面了。我不知道tk能不能設置層級,我在百度中沒有找到,我想如果不行的話,我不能在這上面浪費時間,我要換種思路了。比如說每開一塊泥土然后生成一個Label。明天先把上面信息欄的東西弄完,今天不再想這件事了。對了,明天記得把框架與窗口的大小調一下,比生成的泥土來的小了一點。

今天是12月2日昨天幾乎沒有做什么工作,昨天在忙自己工作上的事,但是昨天我因為嫌字太細,加粗字體之后發(fā)現(xiàn)標簽Lable的大小又變化了。然后順便又測試了幾個字號的按鈕,發(fā)現(xiàn)在某些字號下加粗字體會增大按鈕,但在有些字號下又會使按鈕變小,這讓我太抓狂了。

今天在下午的時候我又用枚舉法找出了一系列的加粗后的修正,又是一串毫無規(guī)律的數(shù)字,小號一點的加不加粗都不影響大小,大號一點的有些影響,有些不影響。總的來說是pad值對加粗修正不影響,邊框粗細對加粗修正不影響,邊框形式也不影響,但是height與width的值會影響加粗的修正值,并且是成1倍的關系。見下面表格。

另外,今天還修改了窗口的大小,發(fā)現(xiàn)geometry參數(shù)好像是包含了菜單欄,標題欄的高度的。我對窗口的大小也開始有點迷茫了,明天再弄吧。

今天是12月6日,今天還是做了不少事情,但是今天有點不舒服,狀態(tài)不太好。今天終于將窗口的大小調整好了,發(fā)現(xiàn)了一個有趣的現(xiàn)象,如果我不使用菜單,窗口的高度就是所有內容加起來的高度,如果我使用了菜單,就要額外增加20。即tk.geometry中高度的參數(shù)額外加20。另外菜單的字體我是控制不了的,即我不能通過改變菜單字體來實現(xiàn)控制額外增加的高度參數(shù),這個20也是我用眼睛一點一點對比出來的,可能也不太準,菜單的字體就是windows控制面板中設置的那個主題字體。但是如果我將窗口鎖定,即我使用tk.resizable方法,將height的參數(shù)調整為False的話。我又不需要考慮菜單的那20的高度了,直接將內容的高度加起來就好,真是有趣又意外。今天另外一個收獲就是將主窗口的類好好的整理了一下,現(xiàn)在我可以將每一塊泥土單獨的調用出來了,今后點擊泥土時可以顯示泥土下的數(shù)字,我準備把生成的地圖放到主窗口的類里面,與泥土的編號對應起來。同時將地圖數(shù)字傳遞給泥土的類。明天先把信息窗口的東西收拾一下吧,我還不知道怎么做計時器,怎么實時更新。我還不知道怎么顯示剩余雷數(shù)。另外,系統(tǒng)默認的字體里面好像是沒有像計算器那樣的數(shù)字字體的。我決定先用別的字體代替一下,我先用Stencil字體來代替。

今天是12月7日今天我的問答有位高手告訴了我tkraise()函數(shù),可以將先生成的部件提升到前面來。于是今天我今天的時間都花在了修改之前的策略上了。現(xiàn)在的狀態(tài)是能出現(xiàn)泥土,點擊泥土,泥土會消失顯露出下面的數(shù)字,如果是0就什么都不顯示,如果是9,還是顯示9。我測試了一下,生成的時間實在是太感人了,能有明顯的看到碼方塊的一個過程。今天我累了,今天狀態(tài)還是不太好,我想明天再按我原來的策略試試,即生成的地圖在背后,點擊某一塊泥土的時候才生成那一塊地圖。這個版本保存為view0.2,試玩之后再弄時間和剩余雷數(shù)那兩個標簽吧。后面再把9換成雷的圖片。等把這兩個弄好了,我就貼一張圖片上來,我覺得可以算是把界面層的工作告一段落了。

今天是12月8日,今天我按我昨天的想法把地圖生成在背后,點擊泥土時再生成這一小塊地圖,初始化的過程并沒有變快,還是能明顯看到碼方塊的過程,但我還是想繼續(xù)按這個思路做下去,我覺得這樣更加合理一些。昨天的那個版本被保存為了view0.11.另外,今天把剩余雷數(shù)的標簽弄上去了,就是字體丑,我又換成了Consolas字體。明天弄上時間框和中間的emoj按鈕

今天是12月9日,發(fā)薪日,心情不錯,身體也覺得舒服多了,狀態(tài)也不錯。今天算是把界面層的東西都弄好了,有時間框,但時間框的時間不會動,有中間按鈕,但是按鈕背后的動作還沒做,有雷數(shù)框,但因為右鍵的動作還沒有做,所以雷數(shù)不會隨著右鍵而減少。菜單也弄好了,但后面的命令都沒有做。現(xiàn)在地圖上的數(shù)字9已經可以顯示成地雷了。我給圖片涂顏色圖了好久,涂了兩張,一張的背景色與地圖背景色一致,另一張地雷的背景色是紅色的,作為引爆的那顆雷。先上兩張圖片吧。我覺得可以算是界面層的一個小節(jié)點。

? ??

?另外,再說一下這兩天在按鈕和標簽上貼圖的發(fā)現(xiàn),我發(fā)現(xiàn)當按鈕和標簽上面是文本的時候height與width屬性會以參數(shù)的形式進入部件的大小的運算,但如果部件上面是圖片的時候,這兩個屬性好像是直接表示部件的高和寬,為什么好像要加粗,因為我沒有驗證過。等做完掃雷了有時間了我再去驗證一下,我再去補充我的另一篇文章。

?我是11月17日開始做的,到今天正好是完整的3周,做運算層只花了1、2天的時間,后面一直都在弄界面。細想一下,是我對tk不熟悉,為了控制標簽與按鈕大小我花了不少的時間,相信后面應該會越來越順利的。明天開始做控制層。

今天是12月10日,今天又開始不舒服了,今天還有一些別的事,今天不做了。下周一還要出差一趟。下周二再說吧。周二之前先不去想了。

今天是12月20日,上周因為出差,出了一個星期,什么都沒有做,感覺自己懈怠了,有了點成績而放松了。今天回看之前的代碼,我想做emoj按鈕的開始功能,也想做計時器,也想做菜單的命令。但是腦子里一團漿糊,什么都做不好。再看看之前做過的《小蜜蜂》的游戲,是不是我對窗口內的東西的構架方式不對,我應該給每件東西都設為類,包括計時器,包括emoj按鈕。之后的工作是要再梳理一下自己的代碼。我現(xiàn)在的很多功能是直接做在窗口類中的一個方法,可能這樣的構架就不行。另外,今天看到幾個帖子是關于鼠標操作的,把鏈接貼在這里,方便后面查

Python筆記之Tkinter(鼠標事件)——瀟灑哥的CSDN博客

知乎上的一篇回答“實現(xiàn)一個按鈕左右鍵各觸發(fā)一種操作”

博客園上的一篇關于鼠標與鍵盤事件的文章

今天是12月21日,冬至。今天我重新整理了一下計時器,剩余雷數(shù),emoj,把他們都弄成了單獨的類,把計時器的功能做了。我是直接搜了一段代碼。見下面的連接,雖然我能理解這段代碼,但是我無法修改,一改就錯。只能直接使用,我把這段代碼放到我控制層(gamefunction)當中去了。

計時器的代碼

另外,在騰訊云上也有一段類似的計時器代碼,我也把鏈接放在這里,供學習用。

騰訊云上的計時器代碼

明天先把菜單當中等級的內容做好,等級功能是多選一,不能多選多,等級選擇后要能改變記錄等級的變量。

今天是12月22日,今天學到了一個點,在command這個關鍵字后面只能跟函數(shù)名,不能加函數(shù)后面的括號。如果要傳遞參數(shù)怎么辦,用lambda來傳遞。這是我的截圖

?這里有一個鏈接,我是從博客園上的一篇文章中學到的

Python中的TK的Button如何在command后面函數(shù)添加參數(shù)解決方法

今天我把游戲菜單中的命令做了,現(xiàn)在點擊開始游戲可以鏈接到一個函數(shù),但是函數(shù)我還沒寫,點擊等級可以改變游戲的等級變量,但改變了之后干嘛還沒寫,點擊退出游戲可以直接退出。

明天要把初始化游戲那個函數(shù)寫好,點擊開始游戲,點擊emoj,改變等級之后都要調用這個函數(shù)。這個函數(shù)做在控制層當中去,gamefunction.

今天是12月23日,昨天做初始化游戲的函數(shù)的時候遇到了困難,我讓主窗口用方法destory,"chuangkou.destory()",但是程序報錯“AttributeError:'_tkinter.tkapp'對象沒有屬性'destoy”今天在查找解決方法的時候,看到runoob.com上的一篇tkinter的菜鳥教程,在那里面他創(chuàng)建主窗口時不是繼承Tk這個類,而是自定義的一個類,這個類的實參是在主程序中的Tk的實例,然后在主窗口的方法中去定義各種參數(shù),我覺得這是個挺好的思路,我想按它的方法來修改,嘗試一下是否可以解決我遇到的問題。但是這么修改的工作量挺大的,今天又比較忙,沒時間和精力來做,明天或者。。。。下周吧。這里先放上這篇文章的鏈接,方便以后查詢。

Python GUI編程(Tkinter)菜鳥教程

今天是12月24日,按昨天的想法,我將所有的內容都修改好了,花了一天的時間,但是現(xiàn)在初始化游戲的功能做好了,繞過了昨天的問題。現(xiàn)在可以點擊菜單中的開始游戲來開始游戲,點擊emoj開始游戲,點擊切換等級切換窗口大小并重新開始游戲。明天,,,呃,下周,要把主程序中的while true的內容做好,讓主程序循環(huán)起來。然后調試好游戲狀態(tài),根據(jù)游戲狀態(tài)再把泥土下面的地圖再回復出來。

今天是12月27日,雖然也不是什么都沒做,但今天一直在忙,也實在沒做多少,剛剛把泥土左鍵從原來的command命令改成了一個bind的綁定,綁定到另一個函數(shù)上。還沒有往下細做。但是今天遇到個有意思的事,bind的函數(shù)我并沒有形參,因為不需要。但是運行時報錯了。報錯是“takes 1 positional argument but 2 were given”,他的意思是我少一個參數(shù)。百度了一下,隨便打個形參就可以了。明天繼續(xù)吧。明天把左鍵的功能完善掉,插旗的方案我的想法是換一塊泥土,在插旗的泥土上再郵件的時候,再換一塊泥土,并且與原來的一樣。凡是點擊右鍵了,原來的泥土就destroy掉,釋放這塊內存。

今天是12月29日,昨天突然出差了,什么都沒做。昨天睡太少,今天一天腦子都是懵的。今天把左鍵的功能前進了一點。現(xiàn)在第一次點擊左鍵會生成地圖的數(shù)組,這個地圖數(shù)組以及地雷坐標數(shù)組我都放到公用里面作為一個公共變量了。第一次點擊之后可以變更游戲狀態(tài),現(xiàn)在游戲狀態(tài)是布爾值,False是游戲未開始,True是開始了,以是否生成地圖為標志。第二次點擊點擊泥土左鍵開始可以返回泥土的坐標,調用公用地雷地圖列表里的數(shù)字。今天就先到這里。明天把泥土打開后畫地板的函數(shù)做好,其實之前已經做過了,只是賦予一個函數(shù)就可以了。接下來的工作是做鼠標單擊右鍵的函數(shù),再接下來是做game over的函數(shù)。

今天是12月30日,今天我本來把左鍵的功能都做完了,遇到0自動開圖,但是突然發(fā)現(xiàn)有一個隨機出現(xiàn)的BUG,就是有些地板生產出來跟我預想的不一樣,好像自己出現(xiàn)了邊框,又好像布局的位置不是我想要的。我用日志打印了一下,又沒有看到錯誤,見下圖。于是我不停地往后退。可能退的時候出現(xiàn)了問題,我一直回退到不生成地板,現(xiàn)在我第一次生成地圖是沒問題的,但我初始化游戲后(點emoj按鈕),再點擊泥土,就會出現(xiàn)BGU,要點擊兩次才能開土,而且也是隨機出現(xiàn)的。我覺得今天的工作都白做了,我明天要回檔到昨天的存檔位置去工作了。但今天的工作還是保留一下。作為明天的參考。今天的心情以自動開圖為分界,過山車啊。幸好昨天的工作內容保存了。收拾一下心情,明天重新來過。哈哈哈

今天是12月31日,今天回檔到前天的存檔開是工作,馬上就發(fā)現(xiàn),其實前天的結果也是有BUG的,某塊泥土在第一次點擊時是正常的,重新開始游戲之后就要點擊兩次,第二次重新開始后就要點三次,下面的截圖是我重新開始了4次游戲,要點擊5次,明顯是內存沒有清空掉造成的。

后來我將泥土打印出來,如下圖,很明顯。當我點擊重新開始之后在同一個frame當中新生成了一套泥土,所以當我再次點擊泥土的時候出現(xiàn)了mud141,第二次點擊是mud60,也就是說我先把后生成的泥土給點掉了,這些泥土是重復地覆蓋在上面了。現(xiàn)在回想昨天地板出現(xiàn)的BUG,應該也是相同的問題,每次打開程序的第一次運行是正常的,但后面就不再正常了。我有個想法,再點擊重新開始游戲時,我不使用muds=[]來清空存放泥土的列表,而是用雙for循環(huán)加destory()方法來把每一塊泥土都釋放掉。

我在網上搜到一個帖子:https://www.cnpython.com/qa/479977

這個帖子里人家說了兩種方法

您可以將對每個小部件的引用存儲在一個列表中,然后在列表上迭代調用destroy()方法。您還可以將所有的條目小部件放在一個框架中。當您刪除框架時,框架內的所有小部件也將被刪除。

這兩種方法我都嘗試了一下,可以解決我的問題,但我發(fā)現(xiàn)各有優(yōu)缺點,如果用for循環(huán)來刪除每一個小部件,小部件的編號還是繼續(xù)向上編號的。見下圖左。明顯,我的frame編號還是2,但是mud的編號已經超過81了(我以初級9*9地圖做的測試)。如果我用destroy掉frame的方法的話,重新開始后frame的編號會繼續(xù)向下編,而mud的編號不再往下。見下圖右。個人感覺用右圖的方法比較簡單,而且后面地板應該也會同時被清理掉。但我不知道之前的frame是不是也存在內存里面。會不會游戲進行了一小時后內存越來越多。

今天算是把左鍵的功能全部做完了,而且我也發(fā)現(xiàn)了昨天那個BUG的原因了,就是那個“看上去好像地板自己出現(xiàn)了邊框,又好像布局的位置不是我想要的”,其實是因為直接把地板貼在泥土上面了,所以出現(xiàn)了像邊框一樣的東西,因為我的底板是比泥土要小一圈的。沒有邊框的。

今天其實做了很多事,game_over的函數(shù)我也已經寫好了,游戲失敗之后會顯示出所有地雷,踩炸的雷顏色與其他不同,并且游戲會處于停止狀態(tài),計時器停止,所有泥土點擊不再有響應。現(xiàn)在每塊地板都被儲存到了gongyong里的一個列表里面了。我在重置游戲的時候我還是用for循環(huán)迭代調用destroy()方法。而且好像并沒有出現(xiàn)我上圖的問題,我在想下一步要不我也把所有的泥土也存到gongyong里面的一個列表吧。嘗試一下會怎么樣,但是因為泥土修改的量比較大,下周(明年)再試試吧。今天的先存檔備份。做完這個嘗試之后就要接著做鼠標單擊右鍵的函數(shù),我的現(xiàn)在的想法是新建一個gongyong列表用來存放插過旗子的泥土,然后直接把這塊泥土覆蓋在原來的泥土上面,如果再次點擊郵件,就把這塊泥土destroy掉,露出原來灰色的泥土。今天先到這里,新年快樂^_^

今天是2022年1月6日,元旦我多休息了一天,昨天又忙了一天,都沒看。今天工作效率極差。每次很長時間沒看之后要回到狀態(tài)總是很難。

今天一直在做插旗子的功能,現(xiàn)在我能做出一塊插過旗子的泥土,但是他與原來的泥土是怎么一個關系還困擾著我,現(xiàn)在我在原來泥土中又新增加了一個布爾值,叫flag。因為我發(fā)現(xiàn)點到空土的時候會給周邊錯點的查過旗的泥土上再覆蓋上地板。所以自動開圖的命令里面還是要識別這個位置是否插過旗子。另外,還遇到一個問題,插旗的泥土與原來的泥土的關系,我發(fā)現(xiàn)我有幾種選擇。第一是像之前想的那樣,再建一塊新的,覆蓋上去。但是新的泥土也要放一個列表,還是字典?第二是在原來的泥土上直接修改重新設置泥土的屬性,第三是把原來的泥土刪除了,再放一塊新的上去,直接就放在原來的泥土列表當中。我現(xiàn)在在用第三種方法,原來的泥土被刪除了,再次右鍵的時候我怎么恢復出原來的泥土?我還沒想好。今天我要仔細想想這個問題,以及上面三種方案的利弊。我現(xiàn)在正在第二和第三種之間搖擺。今天做的事非常少,簡單來說,我今天就做了一塊泥土,沒了。。。。。。而且我又發(fā)現(xiàn)一個Button類的大小的問題,插了旗子的泥土必須使用height=17,width=18 才能使之與之前的泥土大小匹配,也就是12月2日日記中貼圖的25、26的那兩個大小值。這跟我在做地板時得到的結果又不一樣。這又讓我對大小的事情更加的整不明白了。以后慢慢摸索吧。

今天是1月7日,昨天安靜下來想了想,感覺這個問題根本不值得猶豫,明顯第二種方式是最好的,tk中給的config命令就是用來做這種事情的。無論是代碼量還是程序的運行邏輯來說,都是最合理的一個方案。今天我按這個方案來做,一開始出現(xiàn)了一個問題,在debug模式下一切都正常,但是正式run的時候,右鍵雖然執(zhí)行了命令,但泥土上沒有出現(xiàn)旗子。后來我把這段代碼挪到界面層中,也是一樣,直到我把調用旗子圖片的那句語句放到上面Mud的__init__里面作為一個類屬性之后,這個問題才解決。后來想想,插旗子這個動作其實也是界面層的事,我就沒再把他移回去了。但是我依然把插旗子的那些代碼修改好后放在控制層里。

今天我完成了右鍵雙擊地板的功能,可以快速開圖了,順便還做了一張圖片,就是標記錯的雷,在游戲失敗的時候要用這塊地板替換掉標記錯的泥土。本來想做一個雙擊地板后如果標記數(shù)與地板數(shù)字不同時出現(xiàn)一個按下泥土但不打開的動作,但失敗了,這大概跟我用的是bind而不是command有關,先不管他了。

今天雙擊地板應該還有個BUG,我測試的時候曾經出現(xiàn)過2個炸雷的圖片,當時我順手點了重開,沒有截圖下來。明天的工作是再測試一下這個雙擊地板的功能,找出這個BUG。然后把標記錯的泥土,在游戲失敗后替換成錯雷的地板,就是今天做好的那張圖片。明天是周六了,下周要準備python考試的東西,可能又沒時間弄了。

接下來要做的事情是對勝利的判斷,我發(fā)現(xiàn)這事兒沒這么簡單,勝利可以有兩種情況。

第一,正確標記了全部地雷,(即使還有泥土沒打開)

第二,所有未打開的泥土下面都是雷(無論是否標記過)

我曾經就喜歡玩不用鼠標右鍵標記來完成游戲。但要實現(xiàn)上面這個邏輯還是不太簡單的。我現(xiàn)在的想法是這樣的,無論左鍵單擊還是右鍵單擊都會觸發(fā)勝利,所以都需要調用勝利的判斷。右鍵點下的判斷首先判斷當前的插旗的數(shù)量是否與地圖的雷數(shù)一致,如果一致繼續(xù)判斷是否與地雷列表完全相等。左鍵點下的判斷是首先判斷剩余的沒有打開的泥土數(shù)量是否與地圖的雷數(shù)一致,如果一致繼續(xù)判斷是否與地雷列表完全相等。

還有一件事就是贏了之后要干嘛,因為我不準備做排行榜,我現(xiàn)在的想法是計時器要停止,然后跳個框出來表示恭喜,并顯示等級與勝利所用的時間。這個框下面有一個按鈕,點擊按鈕‘再來一局’。就是把游戲初始化一下。今天先到這里。

今天是1月10日,今天很不舒服,一直咳嗽。今天修正掉了上周五說的那個BUG,做好了游戲失敗后用錯雷的圖片來替代有旗子的泥土的功能。到此游戲失敗的功能已經都做好了,今天還做了一個彈出窗口,用來表示游戲勝利了之后顯示游戲的等級與時間。我還沒有做游戲勝利的判斷,今天的時間不多,還要分出時間去復習考試的東西。我本來是打算用一個窗口來做游戲勝利的提示的,小窗口都設置好了,但是當我做完chuangkou.quit的命令之后我發(fā)現(xiàn)所有的窗口都退出了,主窗口也退出了。這不是我想要的,本來想花時間找找原因,可是咳嗽讓我一點心情都沒有,就直接用了messagebox去做,這樣的缺點是:那個按鈕上的字就是確定,不能修改成‘再來一局’。我還沒找到怎么設置這個按鈕的命令。明天再說吧。希望明天能好一些。今天就這樣吧,我現(xiàn)在要去買個冰淇淋,讓我喉嚨舒服一下。

今天是1月11日,今天我完成了右鍵的勝利判斷,只差最后一步了,就是左鍵的勝利判斷。因為每一塊泥土都是作為一個Mud類的實例儲存在一個二維列表當中的,所謂二維列表就是列表中的每一行也是一個列表。我掀掉泥土的時候我并沒有刪除這個實例,我依然讓它存在并占有二維列表中的這個位置,這樣后面用坐標調用的時候不會出錯。但是我也遇到一個問題。當我做左鍵的勝利判斷的時候我需要用雙for循環(huán)去逐個讀取每一塊泥土,查看它們的打開標記,插旗標記的狀態(tài)。如果我玩的是高級的話,最后幾個格子會很麻煩。運算量會很大。我要換一種思路。當我生成地圖的同時,我再生成一個一維的列表,當中的每個元素都是每塊泥土坐標的字符串(‘m_x_y’)這種形式,當我掀掉一塊泥土的時候,我就移除這個列表里的對應的坐標元素,最后我只要用一個for循環(huán)就可以做上面的事了,并且只需要查找?guī)讐K泥土就夠了。

方法我已經想好了,這個列表我就叫泥土集(muds_list),放在gongyong里面。它的元素的格式與插旗泥土(flag_muds)的元素格式是一樣的,當泥土集等于地圖雷數(shù)的時候,我可以用兩個列表相減的方法獲得未插旗泥土的集合(noflag_muds),用一個for循環(huán)來獲取這些坐標的整數(shù)值,并去地雷列表里查找他們。要是完全吻合的話。那就是勝利了,否則就返回。

兩個列表是不能直接相減的,對于我這種不重復值的列表,可以先轉換成集合再相減,減完再轉換成列表就可以了。具體可以見下面這個帖子

python 2個列表相減的2種寫法

今天就到這里,剩下的工作我明天再做吧。

今天是1月12日,我完成了。今天先是把菜單欄中的幫助文件那個命令完善了一下,其實就是點擊幫助打開一個txt文件。但還是花了我一點時間。第二件事就是把左鍵的判斷做完了。我發(fā)現(xiàn)我昨天想太復雜了。其實當我左鍵點開泥土后,剩下的泥土(無論插沒插旗)的總數(shù)能與地雷的總數(shù)一樣,其實我就已經贏了。因為要是我沒贏的話,那就應該已經輸了。所以左鍵判斷的命令最后變成了給剩余沒有插旗的泥土插旗的命令了。插旗的動作我又是直接調用了右鍵的方法。所以等于是最后所有泥土都插上了旗子,然后是以右鍵勝利判斷的方式判斷我獲勝的。在這里我先貼上我所有的代碼:

一共分6個py文件:saolei.py是我的主程序,yunsuan.py是運算層,view.py是界面層,game_function是控制層,settings.py是一些游戲設置的東西,gongyong.py用來存放一些公共變量。

saolei.py?

# _*_ coding : UTF-8 _*_ # @time: 2021/11/30 16:34 # @file: saolei.py # @Author:yyh # @Software:PyCharm import tkinter as tkimport view from settings import Settings import gongyongdef run_game():"""這是掃雷游戲的主程序"""# 每當游戲開始的時候,先生成一個初級的地圖,沒有地圖上的數(shù)字,就是完全用泥土覆蓋的一個界面。game_st = Settings() # 給設置的東西一個實例,其實就是給設置弄個短點好記點的名字。root = tk.Tk() # 先創(chuàng)建窗口gongyong.chuangkou = view.Chuangkou(root, game_st) # 用Chuangkou這個類給主窗口賦予屬性,并給這個已賦予屬性的窗口實例化為chuangkougongyong.chuangkou.update_chuangkou() # 用Chuangkou這個類里的方法更新窗口來給窗口畫出來root.mainloop() # 保持窗口循環(huán)if __name__ == '__main__':run_game()

yunsuan.py

# _*_ coding : UTF-8 _*_ # @time: 2021/11/17 13:18 # @file: yunsuan.py # @Author:yyh # @Software:PyCharmimport numpy as npimport gongyongdef autobulei(dengj, r, c, game_st):"""用隨機算法自動布雷,布雷的算法必須避開第一次開土的坐標。數(shù)字算法在布雷算法完成之后,用9表示雷,用數(shù)字表示周邊有多少個9.用0表示空土:param dengj: 用來接收來自控制層的等級選擇:param r,c:是第一次開土時地圖上的坐標位置, r表是行,c表示列,是從0開始數(shù)的。:return: 返回的應該是自動布完雷的二維數(shù)組"""shuzu_dx = (game_st.dengji_bqd[dengj][0], game_st.dengji_bqd[dengj][1]) # 獲取數(shù)組的大小shuzu = np.zeros(shuzu_dx, dtype=int) # 創(chuàng)建二維數(shù)組i = 0dilei_s = []while i < game_st.dengji_bqd[dengj][2]:r_shuzu = np.random.randint(0, game_st.dengji_bqd[dengj][0]) # 隨機某行c_shuzu = np.random.randint(0, game_st.dengji_bqd[dengj][1]) # 隨機某列,獲得一個隨機位置if shuzu[r_shuzu][c_shuzu] == 9 or ((r_shuzu, c_shuzu) == (r, c)):continue # 如果隨機生成的位置湊巧與第一塊土相同,或者已經布過雷了,那就回到循環(huán)開頭重新隨機else: # 如果是空土shuzu[r_shuzu][c_shuzu] += 9 # 這句應該直接等于9,一開始我用+=發(fā)現(xiàn)了自己沒有檢測是否已經布過雷的bug,所以我就保留了+dilei_s.append((r_shuzu, c_shuzu))i += 1gongyong.L_ditu = shuzu # 給公用的雷地圖賦值gongyong.dilei_list = dilei_s # 給公用的地雷坐標的列表賦值def autoshuzi(ditu, dilei_lis):"""這個函數(shù)跟在自動布雷后面,為雷附近的單元格填寫數(shù)字,就叫自動數(shù)字:param ditu:自動布完雷,但是還沒有寫雷周圍的數(shù)字的數(shù)組:param dilei_lis:獲得布完雷之后的地雷的坐標列表:return: 返回在雷周圍已經寫完數(shù)字的數(shù)組"""row = len(ditu)col = len(ditu[0])dt = ditu # 換個短一點的名字for dilei in dilei_lis: # 在地雷位置的清單中循環(huán)i, j = dilei[0], dilei[1] # 把位置坐標先賦值給兩個名字短一點的變量if dt[i][j] == 9: # 如果是雷,那么我們對雷周圍的8個格子都加上1for n in range(i - 1, i + 2):for m in range(j - 1, j + 2): # 再用一個雙for循環(huán),對雷附近的3x3的區(qū)域遍歷,每個格子+1if n >= 0 and m >= 0 and row - n >= 1 and col - m >= 1 and dt[n][m] < 9:dt[n][m] += 1 # 這句if是判斷當前操作的元素是否在地圖范圍內,并且這個元素他不是雷,然后給這個單元格+1else: # 這句else可以不寫,但為了邏輯完整continueelse: # 這句也是為了邏輯完整。另外,我是遍歷整個數(shù)組中的雷,并不是遍歷整個數(shù)組中每個位置,并且對每個位置統(tǒng)計周邊雷數(shù)。# 所以,只要不是雷我都會跳過,我認為這樣執(zhí)行的語句會比較少。continuegongyong.L_ditu = dt # 吧寫過數(shù)字的地圖在賦值回給公用里的那張地雷地圖

view.py

# _*_ coding : UTF-8 _*_ # @time: 2021/11/30 11:53 # @file: view_0.1.py # @Author:yyh # @Software:PyCharm import tkinter as tk import tkinter.font as tkf from os import startfileimport gongyong import game_function as gfclass Chuangkou():"""創(chuàng)建一個類,用來‘做'主窗口,他不繼承Tk,他是自定義的一個類,下面chuangkou那個變量是在主程序中Tk類的一個實例。送到這個類里來進行操作。這個類就是操作主窗口中那個實例的類。簡單點說,這個類不是窗口,這個類是用來設置窗口里的那些東西的類。:param: chuangkou是主程序當中那個創(chuàng)建的Tk,他才是真正的窗口。:param: dengji是傳遞參數(shù),用來傳遞游戲等級,根據(jù)不同的等級做出不同大小的主窗口:param: game_st 是游戲設置,一些設置參數(shù)傳遞過來"""def __init__(self, chuangkou, game_st, ):# 先設置一些屬性,傳遞參數(shù)或者字體,開始的時候self.ck = chuangkou # 給類一個窗口名字,下面設置窗口內容的時候都用這個名字self.gst = game_st # 把settings換個短一點的名字# 下面這些內容我本來是放在設置里的,但是沒有窗口不能是用tkf,所以我先放在chuangkou的類里面。self.ck.ziti_b = tkf.Font(family='Arial', size=7, ) # b 表示button,按鈕的字體self.ck.ziti_l = tkf.Font(family='Arial', size=12, weight='bold', ) # l 表示label,標簽的字體self.ck.ziti_m = tkf.Font(family='宋體', size=12, ) # m 表示menu,菜單的字體。self.ck.ziti_i = tkf.Font(family='Consolas', size=25, ) # i 表示informa是viewinfor中兩個數(shù)字顯示框的字體# 為什么我要給這些都設置字體,因為我要存放他們的那些標簽和按鈕的高度寬度能被我控制,self.menubar = MenuBar(self.ck.ziti_m, self, ) # 給菜單欄一個實例,菜單欄與等級無關,不會隨著等級的變化而變化self.viewinfor = tk.Frame(self.ck) # 實例化一個框架,這個框架用來存放上面的游戲信息,計時器,剩余雷數(shù),emoj的開始按鈕self.viewgame = tk.Frame(self.ck) # 實例化另一個框架,這個框架用來存放游戲地圖,泥土。self.lei_label = tk.Label(self.viewinfor) # 給剩余雷數(shù)做一個標簽的實例self.time_label = tk.Label(self.viewinfor) # 給計時器也做一個標簽的實例self.leishu = Info(self.lei_label, self.ck.ziti_i, )self.jishiqi = Info(self.time_label, self.ck.ziti_i)self.emoj = tk.Button(self.viewinfor)self.image = tk.PhotoImage(file=self.gst.emoj_pic)# 下面是窗口上emoj按鈕的屬性,跟計時器和剩余雷數(shù)有點不同,我沒有再單獨設置一個Emoj的類。# 因為只有他自己用,所以我直接在這里設置他的屬性了self.emoj['font'] = self.ck.ziti_bself.emoj['image'] = self.imageself.emoj['bd'] = 3self.emoj['height'] = 30self.emoj['width'] = 30self.emoj['relief'] = 'raised'self.emoj['padx'] = 0self.emoj['pady'] = 0self.emoj['command'] = lambda: gf.init_game(self)def update_chuangkou(self, dengji='chuj', ):# 下面內容用來設置這個窗口,部分內容根據(jù)等級的變化要變化,放入一個函數(shù)中,dengji是一個帶默認值的形參,默認是初級self.ck.title("掃雷") # 窗口的標題self.ck.iconbitmap(self.gst.ico) # 標題前面的logoself.ck.geometry(self.gst.gm_bqd[dengji]) # 根據(jù)等級畫出窗口的大小self.ck.config(menu=self.menubar) # 給出窗口畫上菜單欄self.ck.resizable(width=False, height=False) # 不允許手動調整窗口的大小self.viewgame.destroy() # 把初始化游戲,把剛剛創(chuàng)建的框架先清除掉,這個操作可以同時清除框架里的東西。self.viewgame = tk.Frame(self.ck) # 再創(chuàng)建一個框架self.viewinfor['height'] = 50 # 游戲信息框高50self.viewinfor['width'] = self.gst.dengji_bqd[dengji][1] * 26 + 6 # 游戲信息框寬與等級有關,窗口多寬框多寬self.viewinfor['bd'] = 3 # 設置邊框為3self.viewinfor['relief'] = 'sunken' # 邊框形式為下沉式# self.viewinfor['contain'] = False # 默認值就是False,一開始我認為我要把東西放進去,要寫True。后來明白了True表示框架做為容器# 里面放其他的應用程序,并不是放部件的意思,放部件不要True,所以我又改為Fales,后來運行了發(fā)現(xiàn),這行必須不能寫,因為不能在Frame創(chuàng)建之后# 再給contain賦值,必須在創(chuàng)建的時候就賦值,所以最后我就把這句給注釋掉了self.viewinfor.pack(padx=4, pady=0, anchor='w') # 布置信息框,距離左邊留4個單位。不為什么就為了好看。self.viewinfor.pack_propagate(0) # 這行代碼的意思是固定frame的大小。如果不固定,frame里面放入東西后會改變frame的大小self.viewgame['height'] = self.gst.dengji_bqd[dengji][0] * 25 + 6 # 游戲框高度與等級有關,行數(shù)有多少高度就要有多少self.viewgame['width'] = self.gst.dengji_bqd[dengji][1] * 26 + 6 # 同信息框,# 好像直接等于上面那個self.viewinfor['width']也可以,但這樣思路不清晰。self.viewgame['bd'] = 3 # 同信息框一樣設置為3,保持一致self.viewgame['bg'] = '#808080' # 背景色與數(shù)字8一樣是一種淺灰色,作為每個格子的邊框顏色self.viewgame['relief'] = 'sunken' # 同信息框一樣,保持一致# self.viewgame['contain'] = False # 同上面一樣。self.viewgame.pack(padx=4, pady=4, anchor='w') # 布置游戲框,距離左邊和上邊那個框都留4個單位self.leishu.update_leishu(self.gst.dengji_bqd[dengji][2])self.jishiqi.update_jishiqi()self.emoj.pack()self.update_muds(dengji)def update_muds(self, dengji):"""用來給游戲框內畫上泥土,先別管泥土下有沒有雷:param : dengji 是游戲的等級,傳遞過來確定泥土要創(chuàng)建多少塊:return: 對實例的一個方法,不存在返回"""for i in range(self.gst.dengji_bqd[dengji][0]):gongyong.muds.append([]) # 看有多少行,就在泥土列表里在放多少個子列表for j in range(self.gst.dengji_bqd[dengji][1]): # 給列表中每一塊泥土添加上去zuobiao = 'm_{}_{}'.format(i, j)gongyong.muds[i].append(Mud( # 循環(huán)生成泥土self.viewgame,self.ck,zuobiao, # 在每一塊泥土中放置一個number表示這塊泥土的編號,否則每塊泥土自己不知道自己排老幾self.gst, # 傳遞settings))gongyong.muds_list.append(zuobiao)gongyong.muds[i][j].bind("<Button-1>", gongyong.muds[i][j].zuojian) # 綁定鼠標左鍵單擊命令gongyong.muds[i][j].bind("<Button-3>", gongyong.muds[i][j].youjian) # 綁定鼠標右鍵單擊命令gongyong.muds[i][j].place(x=j * 26, y=i * 25) # 把泥土碼好(我不是碼農,我是個疊碼仔)# place這個命令中()里的x表示的是這一行的第幾個,y表示的是第幾行,這里的含義與我在運算層中是反的。我要去把運算層中的x,y修改成r和cclass MenuBar(tk.Menu):"""主窗口上的菜單是單獨一個類,用來做所有的命令。菜單欄中的內容與等級無關,不會隨著等級的變化而變化,所以這個類直接繼承了Menu的類"""def __init__(self, ziti, chuangkou, ):self.ziti = ziti# 我設置了菜單的字體,但是我發(fā)現(xiàn)窗口菜單欄的字體不受我控制,是系統(tǒng)默認的,只有下拉出來的菜單字體才是受我的控制的。self.chuangkou = chuangkouself.dj = tk.StringVar() # 這個實例化是給菜單欄中的radio按鈕使用的,如果選中某個按鈕,同一組的其他按鈕要關閉。self.dj.set('chuj')super(MenuBar, self).__init__() # 下面這些內容必須要寫在super的下面,在這個super的屬性括號里寫font是不受控制的self.gamemenu = tk.Menu(self, tearoff=False, font=self.ziti) # 做一個游戲子菜單的實例,他將來還有子菜單self.gamemenu.add_command(label='重新開始',command=self.initgame) # 在"游戲"子菜單中添加‘重新開始’的命令,這個是初始化一下游戲的意思self.gamemenu.add_separator() # 分割線self.gamemenu.add_radiobutton(label='初級', variable=self.dj, value='chuj',command=lambda: gf.update_dj(self.dj.get(), self.chuangkou)) # 多選一按鈕,初級self.gamemenu.add_radiobutton(label='中級', variable=self.dj, value='zhongj',command=lambda: gf.update_dj(self.dj.get(), self.chuangkou)) # 多選一按鈕,self.gamemenu.add_radiobutton(label='高級', variable=self.dj, value='gaoj',command=lambda: gf.update_dj(self.dj.get(), self.chuangkou)) # 多選一按鈕,self.gamemenu.add_separator() # 分割線self.gamemenu.add_command(label='退出游戲', command=chuangkou.ck.quit) # 退出游戲的命令self.helpmenu = tk.Menu(self, tearoff=False, font=self.ziti) # 做一個幫助子菜單的實例,他將來還有子菜單self.helpmenu.add_command(label='游戲說明', command=self.openhelp) # 在"幫助"子菜單中添加‘游戲說明’命令self.add_cascade(label='游戲', menu=self.gamemenu) # 在菜單欄中添加游戲按鈕,這個按鈕調出"游戲"子菜單self.add_cascade(label='幫助', menu=self.helpmenu) # 在菜單欄中添加幫助按鈕,這個按鈕調出"幫助"子菜單def initgame(self):"""重新開始,就是把窗口按當前等級重新初始化一下,這里跟emoj不一樣的地方在與我是在菜單欄的類里面做這件事。我也可以在上面菜單欄中用command = lambda …… 的命令,但我還是想直接調用一個方法,體現(xiàn)一下command后面跟的函數(shù)、方法是不能加()的"""gf.init_game(self.chuangkou)def openhelp(self):startfile('help.txt') # 用默認程序打開一個文件。這方法我百度來的。class Info():"""給信息欄內的兩個數(shù)字顯示框做一類,繼承標簽類。用來規(guī)范標簽內的格式"""def __init__(self, infor, ziti, ):self.infor = inforself.infor['font'] = zitiself.infor['height'] = 1self.infor['width'] = 3self.infor['bd'] = 0 # 無邊框self.infor['bg'] = 'black' # 黑底self.infor['fg'] = 'red' # 紅字self.infor['relief'] = 'solid' # 這句表示邊框為實線形式,但我上面已經寫無邊框了,呵呵呵,這句應該可以省略了。self.infor['padx'] = 0 # 無橫向內邊距self.infor['pady'] = 0 # 無縱向內邊距def update_leishu(self, text=0):self.infor['text'] = "{:0>3}".format(text)gongyong.shenyuleishu = text # 把從等級中獲得的雷數(shù)賦值給一個公用變量# 這個format的用法不常用,:后面是格式要求,>是右對齊的意思,3表示用三個字符來顯示內容,0表示不足三個字符的話用0來補位。# 另外,<是左對齊,^是居中對齊。這個可以具體看python的官方說明文檔self.infor.pack(side='right') # 放在信息框的右邊, 問,是不是也可以寫成 self.infor.pack['side']='right'def update_jishiqi(self, text=0):self.infor['text'] = "{:0>3}".format(text)self.infor.pack(side='left')class Mud(tk.Button):"""寫一個泥土的類,繼承按鈕,每一塊泥土都是來自這個類的:param: number表示這塊泥土在窗口中的坐標位置,是一個字符串shuzi 表示的是這個位置上地圖上的數(shù)字,首次生成泥土的時候都寫成了0st 是傳遞過來的Settings"""def __init__(self, jiemian, chuangkou, number, game_st):self.jiemian = jiemian # 向下層傳遞,也是Button部件必須的一個參數(shù)self.chuangkou = chuangkouself.number = number # 表示這塊泥土在窗口中的坐標位置,是一個字符串self.st = game_st # 是傳遞過來的Settingsself.qizi = tk.PhotoImage(file=self.st.flag) # 從設置里調出用來做旗子的圖片self.ziti = chuangkou.ziti_b # 字體是窗口那個類里定義的的ziti_b字體self.kai = False # 做一個布爾值,用來判斷這塊泥土是否已經被挖開self.flag = False # 用一個布爾值來表示這塊泥土是不是做插旗的泥土,其實插旗的泥土是另外覆蓋在現(xiàn)有的泥土上面的。super(Mud, self).__init__(self.jiemian,font=self.ziti, # 字體height=1, width=2, # 大小bd=3, # 邊框粗細bg='#e0e0e0', # 給按鈕設置一個背景色,讓按鈕與地圖的顏色稍微有點不同relief='raised', # 邊框形式padx=2, pady=2, # 內邊距)def zuojian(self, elf=True):"""左鍵點擊泥土要做的事"""if not gongyong.game_over and elf:# 如果你還沒有gameover,那么就調用控制層中挖土(watu)的函數(shù) # 本來這個elf是用來做雙擊右鍵的一個效果的,沒做出來,先放著吧。gf.watu(self.jiemian, self.chuangkou.ziti_l, self.number, self.st)# 調用游戲功能中的挖圖的函數(shù),把這塊土的坐標傳給他,把游戲設置的實例也傳過去# 當時碼放泥土時生成的number就為了現(xiàn)在可以作為坐標使用# 調用游戲功能中的挖圖的函數(shù),把這塊土的坐標傳給他,把游戲設置的實例也傳過去.在那個函數(shù)中順便也把地板給畫了。# 所以,要把界面也傳過去,字體也傳過去# print(self) # 用來監(jiān)視當前泥土的儲存編號。正式程序注釋掉這句。else: # 如果你已經輸了,那我就啥都不干了。passif len(gongyong.muds_list) == self.st.dengji_bqd[gongyong.dengji][2]:# 如果挖完土,剩下的泥土能與地圖的雷數(shù)相同,你就已經贏了gf.win_judge_1() # 調用這個函數(shù)給所有未開泥土插上旗子。else:passdef youjian(self, elf):if not gongyong.game_over and gongyong.game_state: # 如果你還沒有gameover,并且游戲已經開始。那么# gf.flag(self.number, self.st) # 調用控制層的插旗函數(shù) # 不再調用,原因見控制層插旗函數(shù)的說明if not self.flag: # 如果沒有插過旗子self.config(image=self.qizi, height=17, width=18, ) # 重新定義這塊泥土的一些屬性self.flag = True # 把旗子的標記改為開gongyong.flag_muds.append(self.number) # 偷個懶,直接把左邊存到插旗泥土的列表里去gf.update_shenyuleishu(-1) # 插面期就給剩余雷數(shù)-1else: # 如果插過旗子了,右鍵就是拔旗。這個游戲中沒有?這個不確定狀態(tài)。self.config(image='', height=1, width=2, ) # 再重新定義這塊泥土的屬性,把他回復到之前的狀態(tài)self.flag = False # 把旗子的標記改為開gongyong.flag_muds.remove(self.number) # 把這塊泥土從插過的列表里給刪了gf.update_shenyuleishu(1) # 拔面旗就給剩余雷數(shù)+1else: # 如果你已經輸了,或者說游戲還沒開始。(與的反義是或,沒錯)pass # 那右鍵就啥都不干了。class Ditu(tk.Label):def __init__(self, jiemian, ziti, zuobiao, st, text='', image="", height=1, width=2):self.jiemian = jiemianself.ziti = zitiself.zuobiao = zuobiaoself.st = stself.text = textself.image = imageself.height = heightself.width = widthsuper(Ditu, self).__init__(self.jiemian,text=self.text,image=self.image,font=self.ziti,height=self.height, width=self.width,bd=0, # 原來邊框寬度是1.但是標簽邊框顏色是黑色的,我嫌不好看,我又改不了,# 所以我干脆取消了標簽邊框,給游戲框架(viewgame)設置底色,布置的時候按原來bd=1的邊框空開來布局,直接顯示框架底色作為邊框,relief='solid',padx=2, pady=2)def you_shuangji(self, elf): # 右鍵雙擊地板的功能,直接去調用控制層里的開圖的函數(shù)gf.kaitu(self.jiemian, self.ziti, self.zuobiao, self.st, self.text)

game_function.py

# _*_ coding : UTF-8 _*_ # @time: 2021/12/20 13:43 # @file: game_function.py # @Author:yyh # @Software:PyCharm import tkinter as tk import tkinter.messageboximport view import gongyong import yunsuandef run_counter(digit): # digit是標簽類的實例"""這段是計時器的代碼"""def counting():digit.config(text="{:0>3}".format(gongyong.counter)) # .config()的效果就是把括號里的內容加到標簽,這里還需要刷一次格式if gongyong.counter < 999 and gongyong.game_state: # 如顧用時小于999秒,并且游戲還沒有結束的話gongyong.counter += 1 # 計時器加1digit.after(1000, counting) # 在1000毫秒后執(zhí)行counting()函數(shù),即循環(huán)執(zhí)行countingelse: # 如果用時大于999秒,就不在往上數(shù)了,留點面子。passcounting()def update_shenyuleishu(c):if gongyong.shenyuleishu > -99: # 如果剩余雷數(shù)大于-99的話,gongyong.shenyuleishu += c # 加上變化量gongyong.chuangkou.lei_label.config(text="{:0>3}".format(gongyong.shenyuleishu)) # 給那個類重新賦值一下屬性,再刷一次格式if gongyong.shenyuleishu == 0: # 如果剩余雷數(shù)等于0,win_judge_2() # 調用一下勝利判斷函數(shù)else: # 如果不是0,無論正負,都說明沒有完全正確標記出所有的雷passelse: # 如果已經-99了,那就pass不執(zhí)行了pass # 為什么是-99,三位數(shù)顯從-99到999,大地圖16*30只有480格子,不會出現(xiàn)999.但會出現(xiàn)-99def update_dj(dj, chuangkou):# 用一個函數(shù)來吧公用的等級參數(shù)修改掉gongyong.dengji = dj# 通常修改了等級之后要重新開始游戲,所以下面接初始化游戲。init_game(chuangkou)def init_game(chuangkou, ):"""初始化游戲,就是根據(jù)等級畫出窗口及里面的東西。:return:"""gongyong.muds_list = [] # 將儲存泥土坐標的列表情況gongyong.L_ditu = [] # 將雷的地圖清空gongyong.dilei_list = [] # 將雷的坐標列表清空gongyong.flag_muds = [] # 把插旗泥土的列表也清空gongyong.game_state = False # 將游戲狀態(tài)改為未開始gongyong.game_over = False # 將gameover改為Fales。gongyong.counter = 0 # 初始化一下計時器for item in gongyong.dibanliebiao: # 用for語句清空掉地板列表里的所有地板item.destroy()if gongyong.muds: # 如果泥土列表不空的話for i in range(len(gongyong.muds)): # 獲取一下有幾行for item in gongyong.muds[i]:item.destroy()else: # 如果泥土列表是空的話,那啥都不用管pass # 為啥跟上面地板列表比要增加一個if語句的判斷,因為泥土是二維列表。不判斷的話第一個for就無法執(zhí)行。gongyong.muds = [] # 而且作為二維數(shù)組,必須要清空掉,如果沒有這句會報錯的。chuangkou.update_chuangkou(gongyong.dengji) # 初始化主窗口def watu(jiemian, ziti, zuobiao, st):'''用于鼠標左鍵泥土的命令,順便也把地板給畫了:return:'''# 先把獲得的坐標拆解開來,行坐標是i, 列坐標是ji = int(zuobiao.split('_')[1])j = int(zuobiao.split('_')[2])if gongyong.muds[i][j].flag or gongyong.muds[i][j].kai:# 如果這塊泥土已經插過旗子了,或者已經打開了的話。因為雙擊開土的命令,存在這種可能,已經在遞歸中把泥土打開了,但是雙擊開土的時候又要開它一次。return # 那就返回,跳出這個函數(shù),不再繼續(xù)挖了else: # 如果沒有插旗pass # 那就繼續(xù)gongyong.muds[i][j].destroy() # 先把這塊泥土刪了gongyong.muds_list.remove(zuobiao) # 從泥土坐標列表里把這塊土的坐標刪掉gongyong.muds[i][j].kai = True # 把泥土打開的標記改為開if gongyong.game_state: # 如果游戲狀態(tài)已經開始,shuzi = gongyong.L_ditu[i][j] # 獲取地圖中的數(shù)字else: # 如果游戲沒有開始,啟動開始游戲。畫地圖并且獲取地圖中的數(shù)字shuzi = start_game(i, j, st)# 到這里地圖肯定已經生成了,下面兩行獲取一下地圖的行數(shù)與列數(shù)。我沒有去等級表驅動里讀,而是直接獲取實際已生成的地圖的大小row = len(gongyong.L_ditu) # 獲取一下地圖的行數(shù)col = len(gongyong.L_ditu[0]) # 獲取一下地圖的列數(shù)if shuzi == 9: # 如果數(shù)字是9,說明是雷。那就調用游戲失敗的函數(shù)game_over(jiemian, ziti, i, j, st) # game overreturn # 跳出這個函數(shù),不再執(zhí)行后面的語句elif shuzi == 0: # 如果數(shù)字是0,就text = '' # 給text設定為空字符# 下面的雙for循環(huán)是用來獲取0周邊幾個格子(最少3個,最多8個)的坐標,然后開土。for r in range(i - 1, i + 2): # 這里跟運算層給9周邊寫數(shù)字的代碼一樣,用雙for循環(huán)來獲取周邊有效單元格的坐標for c in range(j - 1, j + 2):if r >= 0 and c >= 0 and row - r >= 1 and col - c >= 1 and (r, c) != (i, j):# 上面這句的意思就是這個當前循環(huán)到的格子是在周邊,并且也不是它自身。ls_zb = 'm_{}_{}'.format(r, c) # 創(chuàng)建一個臨時坐標傳遞給遞歸的自己。watu(jiemian, ziti, ls_zb, st) # 遞歸調用自己挖土,換成地板else:continueelse: # 如果是其他數(shù)字,就把數(shù)字類型轉換成字符類型,后面畫地板要字符類型text = str(shuzi)diban = view.Ditu(jiemian, ziti, zuobiao, st, text) # 生成一小塊地板,實例化Ditu的類,畫地板。gongyong.dibanliebiao.append(diban) # 把它存到公用的地板列表里,方便重置的時候清空掉gongyong.dibanliebiao[-1].bind('<Double-Button-3>', gongyong.dibanliebiao[-1].you_shuangji)diban.place(x=j * 26 + 1, y=i * 25 + 1) # 布局到位,# 注意,place方法中x表示的是橫向位置,y表示的是縱向位置,所以x等于的是列坐標,y等于的是行坐標。# 曾經我想用grid來布局,但是我發(fā)現(xiàn)grid布局的話是一個整體,這里用一土換一圖的方式更合適# 另外,這里布局的時候+1,是因為原來地板是有邊框的,我嫌邊框顏色不好看,我又改不了邊框顏色,于是就去除邊框了,# 布置的時候空開原來邊框的寬度,即1. 這樣會直接露出viewgame的背景色,框架的背景色我能設置diban.config(fg=st.yanse_bqd[shuzi]) # 根據(jù)數(shù)字涂不同的顏色def flag(zuobiao, st):'''用于單擊鼠標右鍵的命令我本來是想在控制層中來實現(xiàn)這個功能的,但是run后不插旗,debug才會插旗,我把他挪到界面層來做,一樣。直到我把調用旗子的圖片那句語句放到上面的Mud類的__init__中后,這個問題才解決。后來想想,插旗這事也是界面層的事,就不打算再放到控制層里去了。但是我是明白了控制層要怎么修改了,所以我還是保留了控制層中flag這個函數(shù)沒有刪除'''# 先把獲得的坐標拆解開來,行坐標是i, 列坐標是ji = int(zuobiao.split('_')[1])j = int(zuobiao.split('_')[2])if not gongyong.muds[i][j].flag: # 如果這塊土沒有被插過旗子的話。gongyong.muds[i][j].config(image=gongyong.muds[i][j].qizi, height=17, width=18, ) # 重新定義這塊泥土的一些屬性gongyong.muds[i][j].flag = True # 把旗子的標記改為開print(gongyong.muds[i][j], "插旗")else: # 如果這塊泥土已經被插過旗子了。print(gongyong.muds[i][j], '拔旗')gongyong.muds[i][j].flag = False # 先把原來泥土的旗子的標記改為關gongyong.muds[i][j].config(image='', height=1, width=2, )def kaitu(jiemian, ziti, zuobiao, st, text):'''用于鼠標右鍵雙擊數(shù)字,打開周圍8格未插旗的泥土,前提條件是插旗數(shù)與數(shù)字必須相同。這里與windows當中的稍微有所不同。windouw當中是左右鍵同時按下。:return:'''flags = 0 # 這個變量用來統(tǒng)計周圍幾個格子有幾面旗子wa = [] # 這個列表用來儲存需要被挖的圖的坐標i = int(zuobiao.split('_')[1])j = int(zuobiao.split('_')[2])row = len(gongyong.L_ditu) # 獲取一下地圖的行數(shù)col = len(gongyong.L_ditu[0]) # 獲取一下地圖的列數(shù)for r in range(i - 1, i + 2): # 這段雙for循環(huán)是直接復制了上面的watu的那段for c in range(j - 1, j + 2):if r >= 0 and c >= 0 and row - r >= 1 and col - c >= 1 and (r, c) != (i, j):if gongyong.muds[r][c].flag: # 如果插旗的話,flags += 1elif not gongyong.muds[r][c].kai: # 如果沒有插旗的話,看看這塊土有沒有被打開過wa.append('m_{}_{}'.format(r, c)) # 沒有挖過的話,把他的坐標存到一個列表里去。else: # 如果是被挖過了,那就沒什么事了passelse:continueif not wa: # 如果那個wa的列表是空的話,那就是瞎點的,直接返回了。returnif flags == int(text): # 如果周圍的旗子數(shù)等于地板上的數(shù)字的話for item in wa: # 在wa這個列表中循環(huán)去挖土。if not gongyong.game_over: # 在正式開挖挖之前再對gameover判斷一下,只有還沒gameover我才挖watu(jiemian, ziti, item, st) # 挖土else: # 要是已經gameover了,那就不用再繼續(xù)下去了break # 直接跳出循環(huán)了else: # 如果周圍的旗子數(shù)不等于地板上的數(shù)字的話,pass # 本來想用下面兩句做一個按鈕被按下但沒有動作的效果,但是沒有成功。# for item in wa: # 用這個循環(huán)把周圍沒有挖過的泥土做一個按下去但沒有打開的效果,注意,這里elf用了false# gongyong.muds[int(item.split('_')[1])][int(item.split('_')[2])].zuojian(elf=False)# print('鼠標右鍵雙擊', zuobiao) # 用來監(jiān)視雙擊的是哪塊地板。正式程序注釋掉這句。def start_game(r, c, game_st):'''開始游戲,點擊下第一塊泥土之后,要開始游戲,運算背后的地圖,游戲狀態(tài)要修改為開始狀態(tài):return: 要返回第一塊泥土的坐標。'''gongyong.game_state = True # 游戲開始了,把游戲狀態(tài)修改一下run_counter(gongyong.chuangkou.time_label) # 啟動計時器yunsuan.autobulei(gongyong.dengji, r, c, game_st) # 生成大小與等級相符的地圖并隨機雷的位置# print('m_{}_{}'.format(r, c)) # 第一塊泥土的坐標。 正式程序注釋掉這句。yunsuan.autoshuzi(gongyong.L_ditu, gongyong.dilei_list) # 給雷周圍寫上數(shù)字,并且把剛剛的地圖更新一下。print(gongyong.L_ditu) # 打印一下整個地圖。正式程序注釋掉這句。return gongyong.L_ditu[r][c] # 把第一塊土里是數(shù)字返回回去def win_judge_1():"""本來這個按鈕是用來給左鍵做判斷是否勝利的,后來發(fā)現(xiàn)能達到這一步其實已經是勝利了,這個函數(shù)就用來給沒有插旗的泥土都插上旗子。"""# 先獲取一下沒有插旗的泥土的坐標列表,因為列表不能相減,所以借用一下集合來做減法,如果列表內容有重復,就不可以這么弄了。noflag_muds = list(set(gongyong.muds_list) - set(gongyong.flag_muds))for zb in noflag_muds: # 給這些泥土逐個觸發(fā)一下右鍵的動作r, c = int(zb.split('_')[1]), int(zb.split('_')[2]) # 獲取其中的坐標值,r,c分別gongyong.muds[r][c].youjian(0) # 調用右鍵的方法,這里因為必須要有一個實參,所以隨便打了個0def win_judge_2():"""右鍵勝利判斷,當按下右鍵時,如果當前標記的雷數(shù)與地圖雷數(shù)相同則觸發(fā)這個判斷,判斷所有標記的泥土的坐標是否與所有雷的坐標相同,如相同則觸發(fā)游戲勝利函數(shù)"""linshi = gongyong.flag_muds[:] # 把當前的插旗泥土先復制到一個臨時的列表里while linshi: # 當這個列表不為空的時候item = linshi.pop() # 逐個彈出這個列表里的元素r, c = int(item.split('_')[1]), int(item.split('_')[2]) # 獲取其中的坐標值,r,c分別為行與列if (r, c) in gongyong.dilei_list: # 判斷這個(r,c)元祖是否在地雷列表里continue # 要是在,那就繼續(xù)下一個循環(huán)else: # 要是不在,那就說明有至少有一個旗子插錯了,直接返回函數(shù),不執(zhí)行while后面的語句return # 返回函數(shù)game_win() # 如果while語句全部執(zhí)行完,證明所有的旗子都插對了,就調取游戲勝利的函數(shù)def game_win():"""做游戲勝利的一些事件"""bqd = { # 我之前偷懶總是用拼音來代替等級的中文,這里放個表驅動,用來對應拼音和中文'chuj': "初級",'zhongj': "中級",'gaoj': "高級",}gongyong.game_state = False # 先把游戲狀態(tài)調停,暫停之后時間會暫停。# 下面是個簡單的文本框,用來顯示這個等級多少時間完成tk.messagebox.showinfo(title='恭喜你找出了所有地雷',message="恭喜你!\n用時 {} 秒完成 {} \n再來一局".format(gongyong.counter, bqd[gongyong.dengji]),)init_game(gongyong.chuangkou) # 這行是點擊了提示框的確定后執(zhí)行的命令,就是把游戲初始化一下。def game_over(jiemian, ziti, r, c, st):"""這個函數(shù)用來做游戲失敗的一些事件:param: jiemian是傳遞過來的viewgame游戲框,因為炸雷圖片也是要用Ditu類實例后放到這個游戲框里的:param: r,c 是傳遞過來觸發(fā)游戲失敗的那個雷的坐標,就是哪個雷觸發(fā)了:param: st 是傳遞過來的游戲設置,用來獲取游戲設置當中的雷與炸雷的圖片:return:無返回"""# print('game over') # 正式程序注釋掉這句。gongyong.game_state = False # 先把游戲狀態(tài)調停gongyong.game_over = True # gameove狀態(tài)改為真,因為你是真的輸了,哈哈lei_image = tk.PhotoImage(file=st.lei_pic) # 從設置里調出用來做雷的圖片,必須用photoimage實例,因為下面標簽的image屬性只認它。zhalei_image = tk.PhotoImage(file=st.zhalei_pic) # 從設置里調出用來做踩到炸了的那個雷的圖片。cuolei_image = tk.PhotoImage(file=st.cuolei_pic) # 從設置里調出用來做標記錯了的了的那個圖片。zuobiao = 'm_{}_{}'.format(r, c)for item in gongyong.dilei_list: # 這里在地雷列表里循環(huán)if item == (r, c): # 如果是踩到的那顆雷diban = view.Ditu(jiemian, ziti, zuobiao, st, image=zhalei_image, height=23, width=24) # 用炸雷圖片else:diban = view.Ditu(jiemian, ziti, zuobiao, st, image=lei_image, height=23, width=24) # 用普通雷圖片# 上面兩句中的height與width很有意思,好像如果標簽是圖片的話,這兩個值就直接是標簽的大小了。if not gongyong.muds[item[0]][item[1]].flag: # 如果這塊泥土沒有插過旗的,那么執(zhí)行下面這些操作gongyong.dibanliebiao.append(diban) # 把有地雷的地板也存到那個列表里。gongyong.muds[item[0]][item[1]].destroy() # 這句話是先把這塊泥土給掀了。diban.place(x=item[1] * 26 + 1, y=item[0] * 25 + 1) # 布局上有地雷的地板# print(diban) # 用來監(jiān)視當前這塊地板的儲存編號。正式程序注釋掉這句。else: # 如果是插過旗子的,那就不管他。pass# print(gongyong.flag_muds) # 用來監(jiān)視一下插旗的泥土有幾塊,正式程序注釋掉這句。for item in gongyong.flag_muds:r, c = int(item.split('_')[1]), int(item.split('_')[2])if (r, c) not in gongyong.dilei_list: # 如果插過旗的那塊泥土不在地雷列表里的話# 要做一塊插錯雷的圖片來替換掉這塊插過旗的泥土diban = view.Ditu(jiemian, ziti, zuobiao, st, image=cuolei_image, height=23, width=24)gongyong.dibanliebiao.append(diban) # 也放到那個地板列表里面去gongyong.muds[r][c].destroy() # 先把這塊無辜的泥土掀了diban.place(x=c * 26 + 1, y=r * 25 + 1) # 替換上剛剛的錯雷圖片# print(diban) # 用來監(jiān)視當前這塊地板的儲存編號。正式程序注釋掉這句。else: # 如果它在列表里,那就不要做什么了,因為已經做對了。passdef emoj_test():"""用來做一些測試性的命令,給它附在emoj按鈕上,等游戲開發(fā)完成之后,再把emoj命令改回原來的初始化游戲的命令"""pass

settings.py

# _*_ coding : UTF-8 _*_ # @time: 2021/12/8 11:24 # @file: settings.py # @Author:yyh # @Software:PyCharmclass Settings():def __init__(self):self.dengji_bqd = {'chuj': [9, 9, 10],'zhongj': [16, 16, 40],'gaoj': [16, 30, 99],}self.yanse_bqd = {0: '#000000', # 0 應該是空的,但是會搜索到0,表驅動會出錯1: '#0000FF', # 從1到8是不同的數(shù)字顯示的顏色,顏色的十六進制表示值2: '#008000',3: '#FF0000',4: '#000080',5: '#933A3A',6: '#008080',7: '#000000',8: '#808080',9: '#000000', # 9 應該是個雷的圖案,先放一個顏色否則表驅動會出錯}self.gm_bqd = { # gm是一個表驅動,用來定義主窗口的大小。'chuj': '248x289', # 比框架稍微大一點,兩邊各留4的邊框,考慮frame的bd=3,所以還要加6# 26*9+6+8=248, 上邊還要留50的信息框,再加20的菜單欄的高度,25*9+6+8+50+20=309'zhongj': '430x464', # w=26*16+6+8=430, h=25*16+6+8+50+20=484'gaoj': '794x464', # w=26*30+6+8=794, h=25*16+6+8+50+20=484# 上面高度的公式中額外+20是為了菜單的高度,這是我用眼睛“試出來”的。# 如果沒有菜單,可以不用加20.但是如果下面的resizable設置成False的話,# 即表示窗口大小不可調整,又可以不用+20了。我實際使用是要求固定窗口大小的,所以最后用的值沒有+20}self.ico = 'image\saolei.ico' # 用來做窗口左上角的那個logo的圖片,是個ico文件self.emoj_pic = 'image\emoji.gif' # 中間那個emoj按鈕上的emoj圖片self.lei_pic = 'image\lei.gif' # 正常地雷的圖片self.zhalei_pic = 'image\lei_2.gif' # 踩炸了的炸雷的圖片self.cuolei_pic = 'image\lei_3.gif' # 標記錯了的錯雷圖片self.flag = 'image\qi.gif' # 標記泥土的旗子的圖片self.dengji = 'chuj' # 存在settings里的等級

gongyong.py

# _*_ coding : UTF-8 _*_ # @time: 2021/11/17 14:07 # @file: gongyong.py # @Author:yyh # @Software:PyCharm import time''' 這個文件中放一些公用的作為全局的變量,函數(shù)等 ''' chuangkou = '' root2 = '' win_ck = '' dengji = 'chuj' counter = 0 # 這就是一個計數(shù)器的變量,用來給窗口上的時間用 shenyuleishu = 0 shenyumuds = 0 muds_list = [] # 儲存泥土坐標的列表,這個列表中的元素格式與廈門插旗泥土(flag_muds)當中的一樣。是碼放泥土的時候存入所有泥土的坐標 L_ditu = [] # 數(shù)字地圖 dilei_list = [] # 地雷列表 flag_muds = [] # 用來存放所有插過旗子的泥土,如果拔旗了,要從這個列表把他刪除。 dibanliebiao = [] # 用來存放生成的所有地板,當重置游戲的時候,這個列表要for語句destroy掉 muds = [] # 用來存放生成的所有泥土。當重置游戲的時候,這個列表要用雙for循環(huán)來destroy掉里面的所有東西,并且再清空。 game_state = False # 這里放一個游戲狀態(tài),如果游戲還沒有開始,False,如果點開了第一塊土了,就是Ture game_over = False # 這里再放一個游戲失敗的狀態(tài),如果沒有踩到雷,那就是False。如果踩到了就是True,表示游戲‘真’的結束了。 # 其實游戲有三種狀態(tài),(a) 初始狀態(tài),還沒有點開任何泥土.(b) 進行狀態(tài),點開了泥土,正在游戲。(c)失敗狀態(tài),即踩到雷了。但還沒有重新開始。 # 本來想用一個值來表示三種狀態(tài)的,后來想想還是用兩個布爾值來做。從邏輯上來說更合適一些。因為很多語句都是用的if-else語句。def log(*args, **kwargs):# time.time() 返回 unix time# 如何把 unix time 轉換為普通人類可以看懂的格式呢?time_format = '%Y/%m/%d %H:%M:%S'value = time.localtime(int(time.time()))dt = time.strftime(time_format, value)with open('log.txt', 'a', encoding='utf-8') as f:print(dt, *args, **kwargs)print(dt, *args, file=f, **kwargs)

總結

做個簡單的總結,我是從11月17日開始的,接近2個月的時間。但是中間有休假,有出差,還有忙于正式的工作。誰讓我是上班摸魚做的呢!在公開日記之前,因為是私密的,所以只有我一個人瀏覽,這個私密瀏覽量是每日更新,截止到今天是19個瀏覽量。我一般有做東西,就會做記錄。所以,我可以不要臉一點:其實我就做了19天。大概也就是一個月的工作日的樣子吧。但我每天又不是全部時間在做,所以按工時算,最多只能算一半,就當自己做了100個小時吧。

?在這里面,我花的時間最多的是在界面層,因為之前從來沒有用過tk,連聽都沒有聽說過。所以花了大量的時間在學習怎么使用tk上,本來還有很多的test.py文件,都是我用來嘗試一些操作的。以前我玩掃雷玩得還是不錯的,巔峰時高級可以80多秒。但真正深入了之后發(fā)現(xiàn)這個游戲里的邏輯性的東西還是很多的,自己基本上都是一邊思考一邊在做。缺少一份計劃,缺少大綱。

另外,在做這個程序的時候,我有過幾次大的修改,近似于將一大部分東西推倒重做的過程。這是我一個人設計,如果是多人合作的話,這樣是非常不可取的,影響團隊士氣(假如我是這個統(tǒng)籌的人)。下次做這種小的項目的東西,要先做大綱。統(tǒng)籌要有一個非常強的邏輯策略,統(tǒng)籌自己控制公共變量的文件,每個成員盡量只與統(tǒng)籌有界面,與其他成員的界面要少。

接下來要做的事,先生成一個.exe文件給兒子玩兒去。然后優(yōu)化自己的代碼,我知道我的代碼量有點大,可以有很大的優(yōu)化的空間。然后自己測試。等覺得挺滿意了之后,可以再上傳一次。

總結

以上是生活随笔為你收集整理的我想自己写一个扫雷,用Python的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。