前言:
Life is short ,you need python.
--Bruce Eckel
我與2048的緣,不是緣於一個玩家。而是一次,一次,重新的ACM比賽.四月份校賽初賽,第一次碰到2048,兩週後決賽再次遇到2048,後來五月份的廣東省賽,又出現了2048。
在這三次比賽過程中,我一次2048都沒玩過..全靠隊友的解釋,直到昨天。我突然想起寫個2048吧,於是下了個2048玩了幾盤,之後就開始用python來寫了,心想就不寫介面了,為了簡潔。
我對python並不熟悉,可是我在之前的博文就提過我希望成為一個python的玩家,所以我選擇了python,同一時候,也希望大家也能夠用很easy的程式碼取實現2048。
首先,假設你沒有玩過2048,沒關係,你僅僅要隨便在網上或電子市場一搜索肯定會有各種版本號,之後玩幾把就會了。
來到這裡,我如果大家都玩過2048了,於是我們先分析一下怎麼來寫一個簡單的2048?回憶一下。遊戲的過程是非常easy的:
開始===>( 進行操作(上下左右)===>矩陣移動===>數字合併===>計分 )===>退出
括號內就是遊戲的進行過程了。我們退出大概就是兩個:1.出現2048,勝利退出 2.無法合併同一時候無法出現新數(是且的關係)
懂得這個執行過程後,我們就一步步來寫,整個過程也不會難。哪怕你跟我一樣基本是新手。
先看看簡略圖,看起來儘管真的非常樸素,可是愛美的你。能夠在看完我的程式後自己美化。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMTA0NDc1OQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" height="474" width="804" alt="">
儲存結構:列表,當中包含二維矩陣列表,即所謂的mtr[4][4],以及棧,棧是用於退回。假設大家不想要這個功能能夠直接忽略。
我們的變數定義:
declare = "←:a/h ↓: s/j ↑: w/k →: d/l ,q(uit),b(ack)"
illegal = "Illegal operation!"
noefficient = "This move has no efficient"
score = 0
step = 0
mtr = init() # init the matrix
mtr_stk = [] # use step for back
scr_stk = []
略微解釋一下: declare是解釋操作提示,自己喜歡怎麼寫就怎麼寫~
illegal是我們輸入非正常操作提示,。能夠省略
noefficient是我們往這個方向沒效果。也能夠省略~
score是得分,step是走了多少步,
mtr是我們的二維方陣,
mtr_stk,scr_stk是儲存棧,用於後退功能,假設不要後退。還是能夠省略~
演算法實現:
·「注:事實上沒有演算法可言....更準確叫函式實現。」
(1).在我們這個沒有介面的2048中。我們僅僅能用製表符啦。所以我們須要一個display函式。
這是一個很easy的事情。跟C語言的printf基本一致, 像例如以下程式碼。基本就能繪製出一個固定數字的表格了。
print "┌"+("─"*5+"┬")*3+"─"*5+"┐"
print "│%4d │%4d │%4d │%4d │"%(1,2048,25,4)
print "├"+("─"*5+"┼")*3+"─"*5+"┤"
print "│%4d │%4d │%4d │%4d │"%(1,2048,25,4)
print "├"+("─"*5+"┼")*3+"─"*5+"┤"
print "│%4d │%4d │%4d │%4d │"%(1,2048,25,4)
print "├"+("─"*5+"┼")*3+"─"*5+"┤"
print "│%4d │%4d │%4d │%4d │"%(1,2048,25,4)
print "└"+("─"*5+"┴")*3+"─"*5+"┘"
可是我們須要的是將固定的數字變成我們矩陣mtr的數字。
另外能夠。注意到一點,數字輸出的形式是固定的,表格邊線。僅僅有交叉點是不同。其它是0。而交叉點有五個,我們能夠走迴圈(認為麻煩能夠直接忽略)
為了美觀,我們不將0輸出,所以我們輸出時候選擇的是字串即%s,這樣。我們遇到0,就能夠輸出' ',其它直接輸出。
我們能夠用python的三目運算子號
print "%4s " %(mtr[i][j] if mtr[i][j] else ' ')
所以我們的繪製表格寫成這樣子:最直接的方法!
def T(a):
return a if a else ' '
def display(mtr):
print "┌"+("─"*5+"┬")*3+"─"*5+"┐"
print "│%4s │%4s │%4s │%4s │"%(T(mtr[0][0]),T(mtr[0][1]),T(mtr[0][2]),T(mtr[0][3]))
print "├"+("─"*5+"┼")*3+"─"*5+"┤"
print "│%4s │%4s │%4s │%4s │"%(T(mtr[1][0]),T(mtr[1][1]),T(mtr[1][2]),T(mtr[1][3]))
print "├"+("─"*5+"┼")*3+"─"*5+"┤"
print "│%4s │%4s │%4s │%4s │"%(T(mtr[2][0]),T(mtr[2][1]),T(mtr[2][2]),T(mtr[2][3]))
print "├"+("─"*5+"┼")*3+"─"*5+"┤"
print "│%4s │%4s │%4s │%4s │"%(T(mtr[3][0]),T(mtr[3][1]),T(mtr[3][2]),T(mtr[3][3]))
print "└"+("─"*5+"┴")*3+"─"*5+"┘"
假設走迴圈能夠這樣寫:就是列舉交叉點情況。
def display(mtrx): #output function
a = ("┌", "├", "├", "├", "└")
b = ("┬", "┼", "┼", "┼", "┴")
c = ("┐", "┤", "┤", "┤", "┘")
for i in range(4):
print a[i] + ("─" * 5 + b[i]) * 3 + ("─" * 5 + c[i])
for j in range(4):
print "│%4s" % (mtrx[i][j] if mtrx[i][j] else ' '),
print "│"
print a[4] + ("─" * 5 + b[4]) * 3 + ("─" * 5 + c[4])
(2)有了表格雛形,我們須要對矩陣進行初始化。then,我們就能夠看到一個像模像樣的2048開始啦!
初始化矩陣是很easy的,我們在矩陣中選兩個不同位置,設定為2其它為0就可以。
先設定為0(沒有memset,╮(╯▽╰)╭)
可是這樣就能夠啦:(看不慣?沒關係。很easy理解,慢慢習慣吧!)
mtr = [[0 for i in range(4)] for j in range(4)]
我們選的位置是採取隨機的,要保證不同,方法非常多,可是我們python的random.sample就非常棒了!
我們用簡單的一句
ran_pos = random.sample(range(16), 2)
便能夠得到一個由兩個不同組成的片段
之後我們通過 4*i+j 進行二維到一維的對映,相同也用( num/4 。num%4)進行一維到二維的對映
所以我們初始話的過程也是十分簡單。(要是偷懶。也能夠直接忽略隨機過程,自己指定吧!哈哈)
def init(): #initial of matrix
mtr = [[0 for i in range(4)] for j in range(4)]
ran_pos = random.sample(range(16), 2)
mtr[ran_pos[0]/4][ran_pos[0]%4] = mtr[ran_pos[1]/4][ran_pos[1]%4] = 2
return mtr
怎麼樣?2048有2個2啦!!!
O(∩_∩)O哈哈~
(3)動起來!。讓我們的2048動起來!
老實說。這個過程是全個程式最核心的部分。也是最難過的~只是理解了也就不難了。
我們先看向左移動吧。
記得我們玩的時候,合併過的格子是不會再在這次合併的,比方 某一行為 2 2 2 2 最後移動到的結果是4 4 0 0 而不是 8 0 0 0。
我們要理解這個過程:
我們對於每一行的操作是一樣的,即行間無關,所以我們如今要討論第i行,當我們向左動起來的時候。我們從最左開始確定。我們一開始合併前兩個,再合併前三個,再合併前4個。
比方
2 2 4 4 => 4 0 4 4 => 4 4 0 4 =>4 4 4 0 => 4 8 0 0
注意兩點: 1.合併過的不再參與合併,所以我們須要一個visit列表,用到前面所講的對映
2.合併的情況=>相鄰的相等,移動的情況=>當前元素為0
python程式碼
visit=[]
score=0
for i in range(4):
for j in range(1, 4):
for k in range(j,0,-1):
if mtr[i][k - 1] == 0 :
mtr[i][k - 1] = mtr[i][k]
mtr[i][k] = 0
elif mtr[i][k - 1] == mtr[i][k] and 4 * i + k - 1 not in visit and 4 * i + k not in visit:
mtr[i][k - 1] *= 2
mtr[i][k] = 0
score += mtr[i][k - 1]
visit.append(4 * i + k)
visit.append(4 * i + k - 1)
當你想明確這個過程後,整個move過程就自然清晰起來了。這個是本文第一個,最後一個比較長的函數了!
def move(mtr, dirct):#the core code!move by the four direction
score = 0
visit = []
if dirct == 0: # left
for i in range(4):
for j in range(1, 4):
for k in range(j,0,-1):
if mtr[i][k - 1] == 0 :
mtr[i][k - 1] = mtr[i][k]
mtr[i][k] = 0
elif mtr[i][k - 1] == mtr[i][k] and 4 * i + k - 1 not in visit and 4 * i + k not in visit:
mtr[i][k - 1] *= 2
mtr[i][k] = 0
score += mtr[i][k - 1]
visit.append(4 * i + k)
visit.append(4 * i + k - 1)
elif dirct == 1: # down
for j in range(4):
for i in range(3, 0, -1):
for k in range(0,i):
if mtr[k+1][j] == 0:
mtr[k+1][j] = mtr[k][j]
mtr[k][j]=0
elif mtr[k+1][j]==mtr[k][j] and (4 *(k+1)+j) not in visit and (4*k+j) not in visit:
mtr[k+1][j]*=2
mtr[k][j]=0
score=mtr[k+1][j]
visit.append(4*(k)+j)
visit.append(4*(k+1)+j)
elif dirct == 2: # up
for j in range(4):
for i in range(1,4):
for k in range(i,0,-1):
if mtr[k-1][j]==0:
mtr[k-1][j]=mtr[k][j]
mtr[k][j]=0
elif mtr[k-1][j]==mtr[k][j] and (4 *(k-1)+j) not in visit and (4*k+j) not in visit:
mtr[k-1][j]*=2
mtr[k][j]=0
score += mtr[k-1][j]
visit.append(4*(k)+j)
visit.append(4*(k-1)+j)
elif dirct == 3: # right
for i in range(4):
for j in range(3, 0, -1):
for k in range(j):
if mtr[i][k+1] == 0:
mtr[i][k+1] = mtr[i][k]
mtr[i][k]=0
elif mtr[i][k] ==mtr[i][k+1] and 4 * i + k + 1 not in visit and 4 * i + k not in visit:
mtr[i][k+1]*=2
mtr[i][k]=0
score+=mtr[i][k+1]
visit.append(4*i+k+1)
visit.append(4*i+k)
return score
(4)當你來到這步,已經進入輕鬆的狀態了,讓控制檯讀入你的操作。短短兩行,當中一行是轉成小寫
dirct = raw_input("Step :%d Score :%d (%s):" % (step, score, declare))
dirct = dirct.lower()
(5)推斷讀入操作並處理
if dirct == "q":
break
elif dirct == "a" or dirct == "h":
dirct = 0
elif dirct == "s" or dirct == "j":
dirct = 1
elif dirct == "w" or dirct == "k":
dirct = 2
elif dirct == "d" or dirct == "l":
dirct = 3
elif dirct == "b":
if len(mtr_stk) == 1:
print "Can't Back.."
else:
mtr_stk.pop()
scr_stk.pop()
mtr = copy.deepcopy(mtr_stk[-1])
score = scr_stk[-1]
step -= 1
continue
else:
print illegal
continue
我是vim 的腦殘粉,所以還支援了hjkl。假設你沒有習慣vim什麼的。直接asdw就能夠了。
上面最麻煩最值得注意的就是要用深複製.這個bug我調了非常久啊親,用"="傷不起啊。
上面的處理意思就是: 對映方向。和b,q的處理(有continue是由於我們將巢狀於while中)。
(6)移動的處理
在上面處理後。篩選出移動的操作,之後我們呼叫移動函式,進行加分。和比較。
tmp = copy.deepcopy(mtr)
op_scr = move(mtr, dirct)
if tmp != mtr:
score = score + op_scr
update(mtr) #更新
display(mtr)
tmp = copy.deepcopy(mtr)
mtr_stk.append(tmp) # 插入後退佇列
scr_stk.append(int(score))
step = step + 1 # 步數加1
else:
print noefficient
注意:不能只用加分來推斷!
由於我們非常多時候都是沒有碰撞合併。等待下一個2,4出現
我們用深複製,將老的矩陣放在tmp上。再移動,將老新矩陣比較,沒改變的時候就輸出 noefficient,否則就加分,插入新數字。並顯示。最後將矩陣壓入棧中。由於要後退!
(7)插入新數字
我們遍歷0-16,並對映後。將相應0的位置放入random列表,我們用random.choice選出一個,同理我們生成一個ran_num=[2,4],選取加分
之後相應位置加分咯。可是注意,當我們沒有空位時候不要插數字。
def update(mtr):
ran_pos=[]
ran_num=[2,4] for i in range(4):
for j in range(4):
if mtr[i][j]==0:
ran_pos.append(4*i+j)
if len(ran_pos)>0:
k=random.choice(ran_pos)
n=random.choice(ran_num)
mtr[k/4][k%4]=n
(8)最後一個啦!
!
!遊戲的迴圈條件
這個還是非常easy的。遊戲進行。當前僅當:沒有2048且(有0或者能夠合併)。
所以我們僅僅須要推斷2048 in mtr,有就結束,沒有 == > 0 in mtr ?有就繼續。沒有==>能夠合併?有繼續,沒有==》,就返回結束啦
def go_on(mtr, score):
if 2048 in mtr:
print "Great!You win!Your score is ", score
raw_input("Press any key to continue...")
exit()
if 0 in mtr:
return True
for i in range(4):
for j in range(4):
if i < 3 and mtr[i][j] == mtr[i + 1][j]:
return True
if j < 3 and mtr[i][j] == mtr[i][j + 1]:
return True
print "Gameover!"
return False
(9)一些其它的東西
注意一開始要畫出開始的畫面(迴圈外),一開始要將初始矩陣,初始分數。壓入棧。
最後來個終於版本號的
#!/usr/bin/env python
# coding=utf-8
#********************************************************
# > OS : Linux 3.2.0-60-generic #91-Ubuntu
# > Author : yaolong
# > Mail : [email protected]
# > Time : 2014年06月01日 星期日 13:13:39
#********************************************************
import random
import copy def T(a):
return a if a else ' '
def display(mtr):
print "┌"+("─"*5+"┬")*3+"─"*5+"┐"
print "│%4s │%4s │%4s │%4s │"%(T(mtr[0][0]),T(mtr[0][1]),T(mtr[0][2]),T(mtr[0][3]))
print "├"+("─"*5+"┼")*3+"─"*5+"┤"
print "│%4s │%4s │%4s │%4s │"%(T(mtr[1][0]),T(mtr[1][1]),T(mtr[1][2]),T(mtr[1][3]))
print "├"+("─"*5+"┼")*3+"─"*5+"┤"
print "│%4s │%4s │%4s │%4s │"%(T(mtr[2][0]),T(mtr[2][1]),T(mtr[2][2]),T(mtr[2][3]))
print "├"+("─"*5+"┼")*3+"─"*5+"┤"
print "│%4s │%4s │%4s │%4s │"%(T(mtr[3][0]),T(mtr[3][1]),T(mtr[3][2]),T(mtr[3][3]))
print "└"+("─"*5+"┴")*3+"─"*5+"┘" def init():
mtr = [[0 for i in range(4)] for j in range(4)] # 小小蛋疼..
ran_pos = random.sample(range(16), 2)
mtr[ran_pos[0]/4][ran_pos[0]%4] = mtr[ran_pos[1]/4][ran_pos[1]%4] = 2 return mtr def go_on(mtr, score):
if 2048 in mtr:
print "Great!You win!Your score is ", score
raw_input("Press any key to continue...")
exit()
if 0 in mtr:
return True
for i in range(4):
for j in range(4):
if i < 3 and mtr[i][j] == mtr[i + 1][j]:
return True
if j < 3 and mtr[i][j] == mtr[i][j + 1]:
return True
print "Gameover!"
return False def move(mtr, dirct):
score = 0
visit = []
if dirct == 0: # left
for i in range(4):
for j in range(1, 4):
for k in range(j,0,-1):
if mtr[i][k - 1] == 0 :
mtr[i][k - 1] = mtr[i][k]
mtr[i][k] = 0
elif mtr[i][k - 1] == mtr[i][k] and 4 * i + k - 1 not in visit and 4 * i + k not in visit:
mtr[i][k - 1] *= 2
mtr[i][k] = 0
score += mtr[i][k - 1]
visit.append(4 * i + k)
visit.append(4 * i + k - 1)
# for i in range(4):
# for j in range(3): elif dirct == 1: # down
for j in range(4):
for i in range(3, 0, -1):
for k in range(0,i):
if mtr[k+1][j] == 0:
mtr[k+1][j] = mtr[k][j]
mtr[k][j]=0
elif mtr[k+1][j]==mtr[k][j] and (4 *(k+1)+j) not in visit and (4*k+j) not in visit:
mtr[k+1][j]*=2
mtr[k][j]=0
score=mtr[k+1][j]
visit.append(4*(k)+j)
visit.append(4*(k+1)+j) elif dirct == 2: # up
for j in range(4):
for i in range(1,4):
for k in range(i,0,-1):
if mtr[k-1][j]==0:
mtr[k-1][j]=mtr[k][j]
mtr[k][j]=0
elif mtr[k-1][j]==mtr[k][j] and (4 *(k-1)+j) not in visit and (4*k+j) not in visit:
mtr[k-1][j]*=2
mtr[k][j]=0
score += mtr[k-1][j]
visit.append(4*(k)+j)
visit.append(4*(k-1)+j) elif dirct == 3: # right
for i in range(4):
for j in range(3, 0, -1):
for k in range(j):
if mtr[i][k+1] == 0:
mtr[i][k+1] = mtr[i][k]
mtr[i][k]=0
elif mtr[i][k] ==mtr[i][k+1] and 4 * i + k + 1 not in visit and 4 * i + k not in visit:
mtr[i][k+1]*=2
mtr[i][k]=0
score+=mtr[i][k+1]
visit.append(4*i+k+1)
visit.append(4*i+k) return score def update(mtr):
ran_pos=[]
ran_num=[2,4] for i in range(4):
for j in range(4):
if mtr[i][j]==0:
ran_pos.append(4*i+j)
if len(ran_pos)>0:
k=random.choice(ran_pos)
n=random.choice(ran_num)
mtr[k/4][k%4]=n # map 0 left,1 down,2 up ,3 right
# a,h=> left ,s,j=>down, w,k=>up, d,l=>right
declare = "←:a/h ↓: s/j ↑: w/k →: d/l ,q(uit),b(ack)"
illegal = "Illegal operation!"
noefficient = "This move has no efficient"
if __name__ == '__main__':
score = 0
step = 0
mtr = init()
mtr_stk = [] # for back
scr_stk = []
tmp = copy.deepcopy(mtr)
mtr_stk.append(tmp)
scr_stk.append(0)
display(mtr)
while go_on(mtr, score):
dirct = raw_input("Step :%d Score :%d (%s):" % (step, score, declare))
dirct = dirct.lower()
if dirct == "q":
break
elif dirct == "a" or dirct == "h":
dirct = 0
elif dirct == "s" or dirct == "j":
dirct = 1
elif dirct == "w" or dirct == "k":
dirct = 2
elif dirct == "d" or dirct == "l":
dirct = 3
elif dirct == "b":
if len(mtr_stk) == 1:
print "Can't Back.."
else:
mtr_stk.pop()
scr_stk.pop()
mtr = copy.deepcopy(mtr_stk[-1])
score = scr_stk[-1]
step -= 1
continue
else:
print illegal
continue
tmp = copy.deepcopy(mtr)
op_scr = move(mtr, dirct)
if tmp != mtr:
score = score + op_scr
update(mtr) #更新
display(mtr)
tmp = copy.deepcopy(mtr)
mtr_stk.append(tmp) # 插入後退佇列
scr_stk.append(int(score))
step = step + 1 # 步數加1
else:
print noefficient
還有我github的版本號,大致一樣,點這裡
假設我的演算法有什麼不正確。請提出。由於寫得匆忙。我未能非常完整第測試。可是我自己玩一些或OK該,沒有上場2048╮(╯▽╰)╭。。。
版權宣告:本文部落格原創文章,部落格,未經同意,不得轉載。