【Python学习笔记】——2048小游戏实现

实验楼项目:https://www.shiyanlou.com/courses/368/labs/1172/document

# _*_coding:utf-8_*_


import curses
from random import randrange,choice
from collections import defaultdict

actions=['Up','Left','Down','Right','Restart','Exit']
# ord()函数,返回输入字符对应的ASCII数值
letter_codes=[ord(ch) for ch in 'WASDRQwasdrq']
# zip()函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组
# 然后返回这些元组组成到列表
action_dict=dict(zip(letter_codes,actions * 2))


# 获取用户到有效输入
def get_user_action(keyboard):
    char='N'
    while char not in action_dict:
        char=keyboard.getch()
    return action_dict[char]


# 矩阵转置
def transport(field):
    return [list(row) for row in zip(*field)]


# 矩阵逆转
def invert(field):
    return [row[::-1] for row in field]

# 创建一个棋盘
class Gamefield(object):
    def __init__(self,height=4,width=4,win=2048):
        self.height=height
        self.width=width
        self.win_value=win
        self.score=0
        self.highscore=0
        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()

    # 棋盘操作,随机生成一个2或者4
    def spawn(self):
        new_element = 4 if randrange(100) > 89 else 2
        (i, j) = choice([(i, j) for i in range(self.width) for j in range(self.height) if self.field[i][j] == 0])
        self.field[i][j] = new_element


    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]
                        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)))

        # 通过对矩阵进行逆转与转置,可以直接从左移得到其余三个方向到移动操作
        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:transport(moves['Left'](transport(field)))
        moves['Down']=lambda field:transport(moves['Right'](transport(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 move_is_possible(self,direction):
        def row_is_left_moveable(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))

        check={}
        check['Left']=lambda field: any(row_is_left_moveable(row) for row in field)
        check['Right']=lambda field: check['Left'](invert(field))
        check['Up']=lambda field: check['Left'](transport(field))
        check['Down']=lambda field: check['Right'](transport(field))

        if direction in check:
            return check[direction](self.field)
        else:
            return False

    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')

        # 绘制水平分割线
        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))
        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)





# 有限状态机
def main(stdscr):

    def init():
        game_field.reset()
        return 'Game'

    def not_game(state):
        # 画出gameover或者Win的界面
        game_field.draw(stdscr)
        # 读取出用户输入得到action,判断是重启游戏还是结束游戏
        action=get_user_action(stdscr)
        responses=defaultdict(lambda :state)
        # 对应不同到行为转换到 不同到状态
        responses['Restart'],responses['Exit']='Init','Exit'
        return responses[action]

    def game():
        # 画出当前期盼状态
        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'

    # 状态机开始循环
    while state!='Exit':
        state=state_actions[state]()

curses.wrapper(main)

'''
总结:
1、###陌生函数—ord():返回输入字符对应的ASCII数值
2、###陌生函数—zip():用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回这些元组组成到列表中,如下:
    >>>a = [1,2,3]
    >>> b = [4,5,6]
    >>> c = [4,5,6,7,8]
    >>> zipped = zip(a,b)     # 打包为元组的列表
    输出:[(1, 4), (2, 5), (3, 6)]
3、###陌生函数—format():格式化字符串的函数,str.format(),实例如下:

    print("网站名:{name}, 地址 {url}".format(name="菜鸟教程", url="www.runoob.com"))

    # 通过字典设置参数
    site = {"name": "菜鸟教程", "url": "www.runoob.com"}
    print("网站名:{name}, 地址 {url}".format(**site))

    # 通过列表索引设置参数
    my_list = ['菜鸟教程', 'www.runoob.com']
    print("网站名:{0[0]}, 地址 {0[1]}".format(my_list))  # "0" 是必须的

    输出:三行“网站名:菜鸟教程, 地址 www.runoob.com”
3、学习怎么找错,排错!
4、curses:python中curses封装了c语言的curses,把c中复制部分简单化,比如addstr(),mvaddstr(),mvwaddstr()合并成了一个addstr()方法
5、assert:断言,python assert断言是声明其布尔值必须为真的判定,如果发生异常就说明表达示为假。可以理解assert断言语句为raise-if-not,用来测试表示式,其返回值为假,就会触发异常(应该是可以使程序直接退出)。
'''

猜你喜欢

转载自blog.csdn.net/laon_chan/article/details/79794109