在前一篇裡面,我們實作了move和merge兩個函式,接下來我們就把兩個它們組合在一起,變成一個完整的動作。
function moveMerge(line,direction)
line=move(line,direction)
line=merge(line)
line=move(line,direction)
end
如前一篇所解釋的,當遊戲玩家決定了方向之後,每一列就進行三個動作:移動、合併相同的數字、再合併一次。
當然,為了維持凡事必定檢驗的好習慣,一小段unit test是不可少的:
function testmovemerge()
println("testing moveMerge()")
assert(moveMerge([0,2,0,2],1)==[4,0,0,0])
assert(moveMerge([0,2,0,0],-1)==[0,0,0,2])
println("test pass")
end
有心的讀者我會建議自己再多加幾個test case試試看。
到目前為止,處理2048每一列移動的工具都已經齊備,接下來就是往二維方向的矩陣前進。
前面有提過,我們會用一個矩陣來儲放2048盤面的值。假設盤面是:
4 . 2 2
. . . 8
16 4 . .
16 . . .
current score: xxxx
Enter the next direction:
那麼我們可以建立一個矩陣來表示:
board=[4 0 2 2; 0 0 0 8; 16 4 0 0; 16 0 0 0]
和Matlab一樣,julia用分號來分隔每一列,用空格分格每一行。
取出每一行或是每一列的語法也和matlab/python相同,和matlab的不同之處只在一個是用[]一個是()來作indexing。
亦即board[1,:]會得到board的第一列:[4,0,2,2],而board[:,1]會得到的第一行:[4;0;16;16]
要特別注意的是,board[1,:]傳回的是一個一維陣列,但是board[:,1]傳回的會是一個二維陣列,有時候需要透過reshape()
這個內建函式把它轉為一維陣列。
因此,我們需要自訂兩個函式來存取2048矩陣每一行或是每一列的值,我的作法如下:
function getLine(board,dim,index)
if dim==1 #處理每一列
return board[index,:]
elseif dim==2 #處理每一行
return reshape(board[:,index],size(board,dim))
end
end
function setLine(board,dim,index,line)
if dim==1 #處理每一列
board[index,:]=line
elseif dim==2 #處理每一行
board[:,index]=reshape(line,1,size(board,dim))
end
return board
end
getLine用來把矩陣的每一行或是每一列抓出來,然後轉成一個一維陣列,而setLine則用把我們之前用moveMerge這個函式產生的值傳回去。在這兩個函式中,我們用dim這個引數來決定要處理矩陣的行或是列。列方向是1,行的方向是2。處理每一樣的時候,就用reshape來轉換矩陣的維度(dimension)。
有了這兩個小工具之後,盤面移動的函式也就順勢完成了:
function boardMove(board,direction)
newBoard=copy(board)
for rowIdx=1:size(newBoard,abs(direction))
# 取出行
line=getLine(newBoard,abs(direction),rowIdx)
newLine=moveMerge(line,direction)
# 把moveMerge傳回的值再放到新的矩陣
newBoard=setLine(newBoard,abs(direction),rowIdx,newLine)
end
return newBoard
end
direction是使用者決定移動的方向,direction=2或-2是上下移動,direction=1或-1左右移動,算是配合julia內建的定義讓程式碼變得簡單。
我的實作並不是執行起來最有效率的寫法,但這種寫法可以充分利用2048遊戲本身的對稱性,還有julia的語言特性。犧牲一點小小的效率,就可以省下很多時間在跟一堆for loop或是if戰鬥。
走筆至此,2048的核心幾乎已經完成了,接下來就要進入到使用者介面的部份了。