用python編寫的45行精簡版俄羅斯方塊遊戲

我以前用js寫過一個60行代碼的俄羅斯方塊,現在python比較流行,決定再寫一個python版本的。
遊戲算法與之前的基本一致,也是用二進制作爲點陣圖數據,用位運算的“與”來判斷方塊與場地是否重疊,用位運算的“或”來把方塊與場地合併。
這次重新優化了程序,並改良了之前不那麼優雅的語法。
本想用python編寫會超過60行,結果最終優化後的程序不算註釋和空行只有45行。

遊戲操作:上鍵是旋轉,左右下鍵是移動,空格鍵是暫停,遊戲得分顯示在標題欄。
由於是精簡版,沒有下一個方塊的提示,也沒有級別,方塊下落速度隨分數加快。

#-*- coding:utf-8 -*-
import random, tkinter, tkinter.messagebox

# 7種方塊圖案(以4*4的16位二進制點陣存儲)
tetris = (0x66,),(0x2222,0xf0),(0xc60,0x264),(0x6c0,0x462),(0x446,0x2e,0x622,0x74),(0x226,0xe2,0x644,0x470),(0x262,0x72,0x232,0x270)
# 遊戲場地數據 (以每行12位二進制點陣存儲,底線設置兩行是爲了防止計算時列表索引越界)
fie = [0x801]*25+[0x7fe]*2
# 遊戲運行需要的數據
p = {"pause":True,"lines":0}
# 將012字符映射成空格,實心方塊與空心方塊的轉換表
mak = str.maketrans("012","\u3000\u25a0\u25a1")

# 初始化新方塊函數
def init():
    # 爲了方便在標題欄顯示分數
    win.title("【分數:%d】俄羅斯方塊 作者:jslang" % (p["lines"]*100))
    # 初始化新方塊的數據 dia是隨機選擇出的一種方塊圖案,r是方塊圖案旋轉的次數,x和y是方塊位置座標
    p.update(dia=random.choice(tetris),r=random.randrange(4),y=0,x=8,pause=False)

# 動作函數 e是操作的屬性名,v是對屬性增減的數值
def act(e, v):
    # 如果是暫停狀態則退出函數
    if p["pause"]: return
    # 對操作的屬性增減
    p[e] += v
    # 取得當前旋轉的方塊圖案
    h = p['dia'][p['r']%len(p['dia'])]
    # 將4*4的16位二進制點陣轉換成4行每行12位二進制點陣
    f = {i:(h<<i*4&0xf000)>>p['x'] for i in range(4)}
    # 用位運算的“與”來判斷方塊與場地是否重疊
    if all(map(lambda i: f[i]&fie[p['y']+i]==0, f)):
        # 將二進制數據轉換成方塊字符並顯示
        box['text'] = '\n'.join(str(int(bin(fie[i])[2:])+int(bin(f.get(i-p['y'],0))[2:])*2)[1:-1] for i in range(4,25)).translate(mak)
        return True
    # 解決方塊處於左右邊緣無法旋轉的問題
    if e!='r' or not(act('x',-1) or act('x',1) or act('x',2)):
        # 對之前的屬性增減反向操作,等於撤銷之前的操作
        p[e] -= v
    # 判斷方塊已到底部
    if e=='y':
        for i in f:
            # 用位運算的“或”來把方塊與場地合併
            fie[p['y']+i] |= f[i]
            # 如果一行已填滿,則刪除這一行並在列表頭添加新行
            if fie[p['y']+i]==0xfff:
                del fie[p['y']+i]
                fie.insert(0,0x801)
                p["lines"] += 1
        # 判斷遊戲結束
        if fie[3]!=0x801:
            p.update(pause=True,lines=0)
            fie[:25] = [0x801]*25
            if not tkinter.messagebox.askyesno("遊戲結束","方塊到頂了,要重新再來一局嗎"):
                win.quit()
        init()

# 定時器函數
def timeout():
    # 方塊下落一行
    act('y',1)
    # 間隔一定的時間再次調用本函數
    win.after(300-p["lines"]*3,timeout)

# 創建遊戲窗口和組件
win = tkinter.Tk()
box = tkinter.Label(win,font=("\u5B8B\u4F53",28),fg="#fc9",bg="#024")
box.pack()
# 綁定鍵盤事件,上鍵是旋轉,左右下鍵是移動,空格鍵是暫停
win.bind('<Up>', lambda e: act('r',1))
win.bind('<Down>', lambda e: act('y',1))
win.bind('<Left>', lambda e: act('x',-1))
win.bind('<Right>', lambda e: act('x',1))
win.bind('<space>', lambda e: p.update(pause=not p["pause"]))

init()
timeout()
# 進入窗口消息循環
win.mainloop()

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章