上一篇文章中挠轴,我們梳理了實現(xiàn)簡易版 2048 游戲的基本知識势篡,這篇文章將介紹如何實現(xiàn)各個模塊翩肌。換句話說,上一次我們確定了旅行的目的地禁悠,這一次就讓我們自由暢行在山間田野念祭。
游戲主程序,即 game 函數(shù)按部就班地向下執(zhí)行碍侦,該判斷就判斷粱坤,然后執(zhí)行相應函數(shù)隶糕。
首先讀取用戶輸入,第一個判斷:是否移動數(shù)字站玄,顯然要移動數(shù)字要滿足以下條件:
- 用戶輸入小寫的 w s a d 對應上下左右
- 該移動方向上允許移動
具體來說枚驻,移動方向最前面有空間或者有連續(xù)相同的數(shù)字≈昕酰可以移動則執(zhí)行 move 函數(shù)再登,并在棋盤上生成隨機數(shù)字,否則原樣輸出晾剖。
其次判斷:棋盤是否被填滿锉矢。被填滿時執(zhí)行 fail 函數(shù)。
最后判斷:是否勝利齿尽。如果獲勝沽损,打印獲勝提示。
def game(board, stdscr, rscore):
global score
global change
# curses.noecho()
# 屏幕不顯示用戶輸入的字符
curses.noecho()
while 1:
# stdscr.getch()
# 讀取用戶輸入的字符
order = stdscr.getch()
# move()對用戶移動的響應
current_board, change = move(order, board)
# change 為 1 隨機產(chǎn)生 2 或 4
if change:
current_board = choice(board)
# 打印棋盤
print_board(stdscr, current_board, rscore)
# 當棋盤被填滿循头,判斷是否游戲結(jié)束
if (current_board != 0).all():
fail(current_board)
# win 為 1 打印獲勝提示
if win:
stdscr.addstr('You win')
以上便是游戲主程序的基本邏輯绵估,接下來我們看具體的函數(shù)模塊。
首先是移動模塊:
basic 函數(shù)用來執(zhí)行移動與碰撞的操作卡骂。move_{up,down,right,left} 函數(shù)用來實現(xiàn)各個方向上的 basic 函數(shù)操作壹士。move 函數(shù)用來響應用戶指令,實現(xiàn)各個方向上的移動偿警。
棋盤由 44 矩陣組成,0 代表該位置上沒有數(shù)字唯笙。basic 函數(shù)就是基于矩陣的運算螟蒸,且以右移為基礎(chǔ)移動。
4 4 矩陣:
向右滑動:
每一周期分為 4 輪崩掘,每一輪操作一行(共 4 行)七嫌,從最左面的元素開始執(zhí)行。設(shè)置 flag 用于提示這一輪是否發(fā)生了改變苞慢,如果發(fā)生了改變诵原,這一輪就再進行一次循環(huán),直到 flag 保持為 0 不變挽放。對于循環(huán)的每一個元素绍赛,如果該元素不為 0 ,若下個元素為 0辑畦,就交換當前值與下個元素的值吗蚌。若下個元素與當前元素相同,則當前元素置 0 纯出,且下一個元素增加一倍蚯妇,分數(shù)還要增加 100 分敷燎。
舉個例子:對于第一行 [2 2 0 4]
第一輪:
- 4 與 0 不交換 [2 2 0 4]
- 0 與 2 交換 [2 0 2 4]
- 0 與 2 交換 [0 2 2 4]
- flag = 1 且 score + = 0
第二輪:
- 4 與 2 不交換 [0 2 2 4]
-
雙倍
置 0 [0 0 4 4]
- 0 不變 [0 0 4 4]
- flag = 1 且 score += 100
第三輪:
-
雙倍
置 0 [0 0 0 8]
- 不變 [0 0 0 8]
- 不變 [0 0 0 8]
- flag = 1 且 score += 100
第四輪:
- 不變
- 不變
- 不變
- flag = 0 且 score += 0
即第一輪最后輸出結(jié)果 [0 0 0 8]。
以上就是向右移動的操作箩言,而對于其他方向上的移動其實就是在此基礎(chǔ)上進行矩陣的轉(zhuǎn)置與逆置操作硬贯。
# A 為 4*4 的矩陣
# 轉(zhuǎn)置操作
A.T
# 逆置操作
A[::-1,::-1]
下圖為原矩陣:
向下滑動:
將原矩陣轉(zhuǎn)置得到新矩陣,新矩陣向右滑動陨收,相當于原矩陣向下滑動饭豹,再轉(zhuǎn)置變回原矩陣。
向左滑動:
將原矩陣逆置得到新矩陣畏吓,新矩陣向右滑動墨状,相當于原矩陣向左滑動,再逆置變回原矩陣菲饼。
向上滑動:
將原矩陣轉(zhuǎn)置加逆置得到新矩陣肾砂,新矩陣向右滑動,相當于原矩陣向上滑動宏悦,再通過轉(zhuǎn)置加逆置變回原矩陣镐确。
# 基礎(chǔ)移動
def basic(board):
global score
global win
# 以右移為基礎(chǔ)移動
for i in range(4):
flag = 1
while flag:
flag = 0
j = 2
while j >= 0:
if board[i, j] != 0:
if board[i, j + 1] == board[i, j]:
board[i, j + 1] = 2 * board[i, j]
if board[i, j + 1] == 2048:
win = 1
board[i, j] = 0
score += 100
flag = 1
elif board[i, j + 1] == 0:
temp = board[i, j]
board[i, j] = board[i, j + 1]
board[i, j + 1] = temp
flag = 1
j -= 1
return board
# 右移
def move_right(board):
return basic(board)
# 上移
def move_up(board):
# 逆置 + 轉(zhuǎn)置
board = board[::-1, ::-1].T
board = basic(board)
board = board[::-1, ::-1].T
return board
# 左移
def move_left(board):
# 逆置
board = board[::-1, ::-1]
board = basic(board)
board = board[::-1, ::-1]
return board
# 下移
def move_down(board):
# 轉(zhuǎn)置
board = board.T
board = basic(board)
board = board.T
return board
# 移動
def move(order, board):
# ord 求碼值
global score
global win
change = 1
tempboard = copy.deepcopy(board)
# 退出游戲
if order == ord('q'):
save_score(score)
exit()
# 重置游戲
elif order == ord('r'):
win = 0
save_score(score)
score = 0
stdscr.clear()
wrapper(main)
# 勝利后,只有退出和重置游戲
elif win:
change = 0
newboard = tempboard
return newboard, change
# 上下左右移動
elif order == ord('w'):
newboard = move_up(board)
elif order == ord('s'):
newboard = move_down(board)
elif order == ord('a'):
newboard = move_left(board)
elif order == ord('d'):
newboard = move_right(board)
# 按其他鍵程序不響應
else:
newboard = board
if (newboard == tempboard).all():
change = 0
return newboard, change
接下來饼煞,我們講 choice 模塊:首先獲取值為 0 的矩陣元素的位置源葫,并儲存在字典里,以序號( 最大值為 count ) 為索引砖瞧。其次產(chǎn)生 [0,count) 范圍內(nèi)的隨機數(shù)(隨機抽取值為 0 的元素)息堂,并且產(chǎn)生隨機數(shù) 2 或 4 (概率為 75% 與 25%)。最后將隨機抽取的元素更改為生成的隨機數(shù)(2 或 4)块促。
# 隨機產(chǎn)生 2 或 4
def choice(board):
udict = {}
# 統(tǒng)計0的個數(shù)
count = 0
for i in range(4):
for j in range(4):
# board[i,j] 為 0
# eg. {0:(1,3),1:(2,1),3:(3,2)}
# 根據(jù) key 可以獲得元素 0 在棋盤上的位置
if not board[i, j]:
udict[count] = (i, j)
count += 1
# np.random.randint(0, count)
# 產(chǎn)生 [0,count) 范圍內(nèi)的隨機數(shù)
random_number = np.random.randint(0, count)
# np.random.choice([2,2,2,4])
# 隨機選取列表 [2,2,2,4] 中的元素
two_or_four = np.random.choice([2, 2, 2, 4])
# 更改棋盤上 0 元素為隨機數(shù)
board[udict[random_number]] = two_or_four
return board
然后是生成分數(shù):
首先游戲開始時加載一次分數(shù)(歷史最高分)荣堰,游戲結(jié)束時保存最高分。每次打印棋盤前竭翠,都比較當前分數(shù)與當前最高分振坚,并更改當前最高分數(shù)。
# 加載最高分
def load_score():
rank_score = np.load(FILENAME)
return rank_score
# 保存最高分
def save_score(score):
rscore = load_score()
if score > rscore:
np.save(FILENAME, score)
# 比較當前分數(shù)與當前最高分
def compare_score(score, rscore):
if score > rscore:
rscore = score
return rscore
其次是打印模塊:
只打印非零值斋扰。
# 打印棋盤
def print_board(stdscr, board, rscore):
global score
rscore = compare_score(score, rscore)
# stdscr.clear()
# 清除屏幕
# stdsscr.addstr()
# 打印字符串
stdscr.clear()
stdscr.addstr('得分:' + str(score) + '\n')
stdscr.addstr('歷史最高:' + str(rscore) + '\n')
for i in range(4):
stdscr.addstr('-' * 22 + '\n')
for j in range(4):
stdscr.addstr('|')
if board[i, j]:
stdscr.addstr('{:^4d}'.format(board[i, j]))
else:
stdscr.addstr(' '.format())
stdscr.addstr('|')
stdscr.addstr('\n')
stdscr.addstr('-' * 22 + '\n')
最后是一些零碎的知識點:
首先我們要初始化程序渡八,初次運行游戲會在當前目錄生成 ‘out.npy’ 文件,并且儲存 0 在文本中传货。其次初始化棋盤屎鳍,最后就可以愉快地開始游戲了。
import numpy as np
import curses
import copy
import os
from curses import wrapper
stdscr = curses.initscr()
# 分數(shù)
score = 0
# 判斷是否獲勝
win = 0
#
FILENAME = 'out.npy'
# 初始化
def init():
# 初始化棋盤
# 初始棋盤 2 或 4 的隨機數(shù)字
if FILENAME not in os.listdir():
np.save(FILENAME, 0)
init_board = choice(np.zeros((4, 4), dtype=np.int))
return init_board
# 主程序
def main(stdscr):
# 初始化程序
init_board = init()
rscore = load_score()
# 打印棋盤
print_board(stdscr, init_board, rscore)
# 游戲主進程
game(init_board, stdscr, rscore)
if __name__ == "__main__":
wrapper(main)
以上便是 python 實現(xiàn) 2048 游戲的完結(jié)版问裕,如果想獲取源代碼哥艇,在微信后臺回復 2048 。
想了解更多請關(guān)注: