先添上最終完成效果圖
Github鏈接
https://github.com/happyte/2048, 這個是2048的代碼倉庫,希望大家指出不足之處。
2048設(shè)計思想
1.大家都玩過2048,我們可以認(rèn)為
4*4的方塊是個矩陣,開始是4*4的零矩陣。游戲開始在任意地方出現(xiàn)2或4,以后每次出現(xiàn)的數(shù)字都是2或者4。然后我們可以上下左右移動,移動的規(guī)則是例如向左動,某一行(左移只需要考慮每一行)的數(shù)比如是[2,4,0,2]向左移動,移動后變成[2,4,2,0],移動后不允許(每行或者每列,與移動方向有關(guān))兩個非0數(shù)字之間有0的存在。移動前相鄰兩個數(shù)相同的話會合并,例如[2,2,4,4]會合并成[4,8,0,0]。2.移動合并完后,會在所有為0的位置隨機(jī)挑選出一個位置填上2或者4,記住是先移動合并完后才會隨機(jī)填上2或者4。
-
3.有些時候我們發(fā)現(xiàn)往某個方向無法移動,例如下面這張情況:
向左和向下都無法移動,向左移動的話每一行移動后還是原來老樣子,因為每一行任意相鄰兩個非0數(shù)之間不存在間隔且無法合并。向下移動的話每一列還是原來老樣子,因為每一列任意相鄰兩個非0數(shù)之間不存在間隔且無法合并。在無法移動(即移動后還是老樣子的情況下)不會在隨機(jī)0位置處添加隨機(jī)數(shù)2或4。只有移動后改變了矩陣的原來樣子且矩陣最小值為0才會在隨機(jī)0位置出添加隨機(jī)數(shù)2或4。
4.有兩種情況移動后不會添加隨機(jī)數(shù)2或4,第一種情況是上面這種情況,往一個方向移動沒有效果。另外一種是矩陣都為非0數(shù),沒有位置添加隨機(jī)數(shù)2或4。
5.游戲結(jié)束的情況,當(dāng)矩陣沒有0而且每行每列任意兩個相鄰數(shù)無法合并。
基礎(chǔ)類
-
1.我們將上下左右移動都封裝成一個類,繼承一個基礎(chǔ)類,基礎(chǔ)類的對象應(yīng)該擁有
matrix矩陣屬性,一個列表用于存儲0元素位置zerolist和紀(jì)錄當(dāng)前分?jǐn)?shù)score。
2.將矩陣移動并返回處理完的矩陣,這里我們將矩陣上下左右移動統(tǒng)一轉(zhuǎn)換為矩陣向左移動,這個我在繼承類中會寫出。
- 先獲得矩陣的行、列數(shù)
lastmatrix = matrix.copy()
m,n = matrix.shape
- 再對每一行進(jìn)行處理,每行都是轉(zhuǎn)化成
list對象,矩陣是array類型的對象,在初始化時使用numpy模塊構(gòu)造出來。
將每一行非0元素之間的0刪除。不斷刪除第一個0元素再往最后 添 加0,直到非0元素之間沒有0。例如list(matrix[i])[2,4,0,4]經(jīng)過處理后變成[2,4,4,0]。
會掉用下面這個for i in range(m): newList = self.removeZero(list(matrix[i]))removeZero函數(shù)
- 將非0元素處理完成之后要對
list進(jìn)行合并,例如上面的[2,4,4,0]變成[2,8,0,0],只要對比相鄰兩個元素是否相同,返回處理完的list。
- 每行的
list都處理完了,把處理完的每行的list賦給老矩陣每行的list,接下來我們要找到所有0元素的位置,并且把位置添加到zerolist這個鏈表中來。
從每一行的最后一個元素向前找,因為0元素一定在最后!!!matrix[i] = newList for k in range(Size-1,Size-newList.count(0)-1,-1): self.zerolist.append((i,k))Size在文件初始化時設(shè)置為4,代表4*4矩陣。 - 如果移動后的矩陣和函數(shù)進(jìn)來時復(fù)制的那個矩陣相同說明已經(jīng)無法移動(也稱為移動沒有效果),不需要在隨機(jī)的一個0位置添加隨機(jī)數(shù)。還有一種情況矩陣都為非0數(shù),沒有0元素的位置了。
if matrix.min() == 0 and (matrix!=lastmatrix).any():
#往矩陣添加隨機(jī)數(shù)2或者4
下面貼上整個函數(shù)的代碼
繼承類
-
1.繼承類只有兩個函數(shù),一個是初始化函數(shù),另外一個是處理矩陣函數(shù)。因為是繼承基礎(chǔ)類,要拿到基礎(chǔ)類對象屬性
matrix、zerolist、score要調(diào)用基類的__init__函數(shù)。以左移類為例
-
2.處理數(shù)據(jù)函數(shù),左移類不需要對矩陣先變化,因為基礎(chǔ)類的
Sequence函數(shù)就是根據(jù)左移寫的。
-
3.右移類,右移?是左移?的逆向排列。先對矩陣每行的數(shù)進(jìn)行一個逆序,例如
[2,4,4,0]轉(zhuǎn)換成[0,4,4,2],再掉用Sequence進(jìn)行處理,例如上面這行處理完為[8,2,0,0],處理完后再對每行進(jìn)行逆序,得到[0,0,2,8],就是[2,4,4,0]右移的結(jié)果。其實(shí)我們的思路就是無論如何移動,我們都先轉(zhuǎn)換成左移,處理完后再還原成應(yīng)該移動方向的樣子。
[:,::-1]就是對矩陣每行進(jìn)行逆序 -
4.向上類,本來上移?就是左移?的轉(zhuǎn)置。道理和右移動一樣。
-
5.向下類,下移可以有多種方法變成左移,比如下移先變成上移,上移轉(zhuǎn)置變成左移?;蛘呦乱葡绒D(zhuǎn)置成右移,右移再逆序成左移。這里采用下移先變成上移,上移轉(zhuǎn)置變成左移。
游戲初始化
-
1.該類封裝了一系列函數(shù),都是類方法和靜態(tài)方法,可以直接拿該類調(diào)用函數(shù)。其中?最重要函數(shù)的是設(shè)置數(shù)據(jù)函數(shù),游戲開始時在任意位置設(shè)置2或者4,移動后在處理完的矩陣的任意0元素位置添加2或者4。
該函數(shù)有兩個默認(rèn)參數(shù)matrix和zerolist,初始化矩陣時調(diào)用這個函數(shù)不需要傳入這兩個參數(shù)。移動完成后的矩陣會調(diào)用這個函數(shù),傳入matrix和zerolist參數(shù)。 -
2.獲得隨機(jī)數(shù)的位置,同樣也有一個默認(rèn)參數(shù)為
zerolist=None
因為初始化時沒有傳入zerolist參數(shù),因此a,b為(0,3)之間的隨機(jī)整數(shù)。移動后的矩陣傳入zerolist參數(shù),記住之前我們往zerolist添加的都是tuple對象。sample函數(shù)取出存在zerolist中的任意一個元素,取出仍然是個list對象,例如取出就是[(0,3)],因此加后面的[0]取出的結(jié)果就是(0,3)。 -
3.返回隨機(jī)數(shù)2或者4
-
4.描繪標(biāo)題和描繪方塊數(shù)字函數(shù)。我給每種不同數(shù)字的方塊定義了不同的顏色,放在一個字典中。
描繪標(biāo)題函數(shù),內(nèi)部調(diào)用了描繪方塊函數(shù)
描繪方塊函數(shù)
-
5.主函數(shù)的鍵盤按下會調(diào)用這個函數(shù),根據(jù)移動方向創(chuàng)建不同對象,然后通過這個對象調(diào)用這個類的
handleData函數(shù)。
-
6.判斷游戲是否結(jié)束函數(shù),矩陣最小值不為0且每行每列相鄰數(shù)不相同
主函數(shù)
- 1.主函數(shù)比較簡單,做完一些初始化工作進(jìn)入
While循環(huán),不斷判斷什么事件發(fā)生,只有退出程序事件和鍵盤事件。