200行python實現(xiàn)2048

import curses #控制字符界面
from random import  randrange, choice
#random.randrange([start], stop[, step]),
# 從指定范圍內(nèi)拳喻,按指定基數(shù)遞增的集合中 獲取一個隨機數(shù)即碗。
#random.choice從序列中獲取一個隨機元素耀怜。
from collections import  defaultdict#默認字典



letter_codes = [ord(ch) for ch in 'WASDRQwasdrq']#ord函數(shù)以一個字符為參數(shù)贡茅,返回對應(yīng)的ASCII數(shù)值锐秦,或者Unicode數(shù)值
actions = ['Up', 'Left', 'Down', 'Right', 'Restart', 'Exit']
actions_dict = dict(zip(letter_codes,actions*2)) #zip() 函數(shù)用于將可迭代的對象作為參數(shù),將對象中對應(yīng)的元素打包成一個個元組辅鲸,然后返回由這些元組組成的對象,這樣做的好處是節(jié)約了不少的內(nèi)存腹殿。
#[(W,Up),(A,Left),..(q,Exit)]-->{W:Up, A:Left,...q:Exit}

#用戶輸入
def get_user_action(keyboard):
    char = 'N'
    while char not in actions_dict:
        char = keyboard.getch()
    return  actions_dict[char]
#矩陣轉(zhuǎn)置
def transpose(field):#進行一個二維列表的轉(zhuǎn)換
    return [list(row) for row in zip(*field)]

#矩陣逆轉(zhuǎn)
def invert(field):
    return [row[::-1] for row in field]

#創(chuàng)建棋盤
class GameField(object):#class:類的關(guān)鍵字独悴;GameField:自定義類的標識符;object锣尉;要繼承的類名(是一個更大的類刻炒,可以從這個父類中繼承一些功能和特性),object是默認值自沧。
    def __init__(self, height=4, width=4, win=2048):#__init__是超類object下的一個初始化對象數(shù)據(jù)的函數(shù)
        self.height = height    #高
        self.width = width      #寬
        self.win_value = win    #過關(guān)分數(shù)
        self.score = 0          #當前分數(shù)
        self.highscore = 0      #最高分數(shù)
        self.reset()            #棋盤重置
    #重置棋盤
    def reset(self):
        if self.score > self.highscore:
            self.highscore = self.score
        self.score = 0
        self.field = [[0 for i in range(self.width)] for j in range(self.height)]#嵌套的列表坟奥,四行四列
        self.spawn()
        self.spawn()#在嵌套的列表里隨機的生成元素

    def move(self,direction):#合并
        #一行向左合并
        def move_row_left(row):
            def tighten(row): #把零散的非零單元擠到一起
                new_row = [i for i in row if i !=0]
                new_row += [0 for i in range(len(row)-len(new_row))]
                return new_row
            def merge(row): #對鄰近元素進行合并
                pair = False
                new_row = []
                for i in range(len(row)):
                    if pair:
                        new_row.append(2 * row[i])#合并
                        self.score += 2 * row[i]#記錄分數(shù)
                        pair = False
                    else:
                        if i +1 < len(row) and row[i] == row[i + 1]:
                            pair = True
                            new_row.append(0)
                        else:
                            new_row.append(row[i])
                assert  len(new_row) == len(row)
                return  new_row
            #先擠到一塊再合并再擠到一起
            return tighten(merge(tighten(row)))
        #通過對矩陣進行轉(zhuǎn)置和逆轉(zhuǎn),可以直接從左移得到其余三個方向的移動操作
        moves = {}
        moves['Left'] = lambda field:                   \
            [move_row_left(row) for row in field]
        moves['Right'] = lambda field:                   \
            invert(moves['Left'](invert(field)))
        moves['Up'] = lambda field:                 \
            transpose(moves['Left'](transpose(field)))
        moves['Down'] = lambda field:                   \
            transpose(moves['Right'](transpose(field)))

        if direction in  moves:
            if self.move_is_possible(direction):
                self.field = moves[direction](self.field)
                self.spawn()
                return True
            else:
                return False
    #判斷輸贏
    def is_win(self):
        return any(any(i >= self.win_value for i in row) for row in self.field)

    def is_gameover(self):
        return not any(self.move_is_possible(move) for move in actions)

    #繪制游戲界面
    def draw(self,screen):
        help_string1 = '(W)Up (S)Down (A)Left (D)Right'
        help_string2 = '      (R)Restart (Q)Exit'
        gameover_string = '            GAME OVER'
        win_string = '                YOU WIN!'
        def cast(string):
            screen.addstr(string + '\n')#調(diào)用方法把字符串打印到屏幕

        #繪制水平分割線
        def draw_hor_separator():
            line = '+' + ('+------' * self.width + '+')[1:]
            separator = defaultdict(lambda: line)
            if not hasattr(draw_hor_separator, 'counter'):
                draw_hor_separator.counter = 0
            cast(separator[draw_hor_separator.counter])
            draw_hor_separator.counter += 1
        def draw_row(row):
            cast(''.join('|{: ^5} '.format(num) if num > 0 else '|      ' for num in row)+ '|')

        screen.clear()
        cast('SCORE: ' + str(self.score))#繪制當前分數(shù)和最高分
        if 0 != self.highscore:
            cast('HIGHSCORE:' + str(self.highscore))
        for row in self.field:
            draw_hor_separator()#繪制分割線
            draw_row(row)#繪制行
        draw_hor_separator()#繪制底邊的線
        if self.is_win():
            cast(win_string)
        else:
            if self.is_gameover():
                cast(gameover_string)
            else:
                cast(help_string1)
        cast(help_string2)

    #棋盤操作
    #隨機生成一個2或者4
    def spawn(self):
        new_element = 4 if randrange(100) > 89 else 2#9:1的比例生成4和2
        (i,j) = choice([(i,j) for i in range(self.width) for j in range(self.height) if self.field[i][j] ==0])#通過choice選擇一個未被占領(lǐng)的位置
        self.field[i][j] = new_element

    #判斷能否移動
    def move_is_possible(self,diretion):
            def row_is_left_movable(row):#判斷是否可以向左移動
                def change(i):
                    if row[i] == 0 and row[i + 1] !=0:
                        return True
                    if row[i] !=0 and row[i + 1] == row[i]:
                        return  True
                    return False
                return any(change(i) for i in range(len(row) - 1))#any,只要有一個是ture結(jié)果就是ture
            check = {}
            check['Left'] = lambda field:                   \
                any(row_is_left_movable(row) for row in field)
            check['Right'] = lambda field:                  \
                check['Left'](invert(field))
            check['Up']     =lambda  field:                 \
                check['Left'](transpose(field))
            check['Down'] =lambda field:                    \
                check['Right'](transpose(field))
            if diretion in check:
                return check[diretion](self.field)
            else:
                return False
#主邏輯
def main(stdscr):#標準屏幕,curses模塊
    def init():
        #重置游戲棋盤
        game_field.reset()
        return  'Game'
    def not_game(state):
        #畫出 GameOver 或者 Win 的界面
        game_field.draw(stdscr)
        #讀取用戶輸入得到的action爱谁,判斷是重啟游戲還是結(jié)束游戲
        action = get_user_action(stdscr)
        responses = defaultdict(lambda : state)#默認是當前狀態(tài)晒喷,沒有行為就會一直在當前界面循環(huán)
        #collections.defaultdict可以接受一個函數(shù)作為參數(shù)來初始化
        responses['Restart'],responses['Exit']='Init','Exit'#對應(yīng)不同的行為轉(zhuǎn)換到不同的狀態(tài)
        return responses[action]
    def game():
        #畫出當前棋盤的狀態(tài)
        game_field.draw(stdscr)
        #讀取用戶輸入得到的action
        action = get_user_action(stdscr)
        if action == 'Restart':
            return  'Init'
        if action == 'Exit':
            return  'Exit'
        if game_field.move(action):
            if game_field.is_win():
                return 'Win'
            if game_field.is_gameover():
                return 'Gameover'
        return 'Game'
    state_actions = {
        'Init': init,
        'Win': lambda: not_game('Win'),
        'Gameover': lambda : not_game('Gameover'),
        'Game': game
    }
    curses.use_default_colors()

    game_field = GameField(win=2048)


    state = 'Init'
    #狀態(tài)機開始循環(huán)
    while state != 'Exit':
        state = state_actions[state]()
curses.wrapper(main)#把stdscr對象傳入main函數(shù)里,wrapper接口
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末访敌,一起剝皮案震驚了整個濱河市凉敲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寺旺,老刑警劉巖爷抓,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異迅涮,居然都是意外死亡废赞,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門叮姑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來唉地,“玉大人,你說我怎么就攤上這事传透≡耪樱” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵朱盐,是天一觀的道長群嗤。 經(jīng)常有香客問我,道長兵琳,這世上最難降的妖魔是什么狂秘? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮躯肌,結(jié)果婚禮上者春,老公的妹妹穿的比我還像新娘。我一直安慰自己清女,他們只是感情好钱烟,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著嫡丙,像睡著了一般拴袭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上曙博,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天拥刻,我揣著相機與錄音,去河邊找鬼羊瘩。 笑死泰佳,一個胖子當著我的面吹牛盼砍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播逝她,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼浇坐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了黔宛?” 一聲冷哼從身側(cè)響起近刘,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎臀晃,沒想到半個月后觉渴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡徽惋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年案淋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片险绘。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡踢京,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宦棺,到底是詐尸還是另有隱情瓣距,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布代咸,位于F島的核電站蹈丸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏呐芥。R本人自食惡果不足惜逻杖,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望思瘟。 院中可真熱鬧弧腥,春花似錦、人聲如沸潮太。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铡买。三九已至,卻和暖如春霎箍,著一層夾襖步出監(jiān)牢的瞬間奇钞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工漂坏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留景埃,地道東北人媒至。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像谷徙,于是被迫代替她去往敵國和親拒啰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容