python学习笔记之2048游戏项目练习

一、游戏逻辑分析

      对于一个2048游戏,我们现在不使用图形界面,使用curses模块将其实现(只有内部逻辑结构,没有图形渲染)。

     2048游戏的状态运行图如下。

     

二 、curses 模块的使用

     我们在curese窗口(控制字符界面)下设计这个棋盘。

def main(stdscr):  
    while():  
        ......  
curses.wrapper(main)

     curses.wrapper()函数会将界面变量传给main函数,一旦main函数执行完毕,则自动退出该控制字符界面。相当于实现了下面代码

def main(stdscr):
    pass:
stdscr = curses.initscr()  # 在任何代码执行前都先要初始化curses。初始化操作就是调用initscr()函数
main(stdscr)
    我们在进行窗口绘制的时候使用stdscr. addstr('字符串'+'\n') 来绘制,其中stdscr是一个curses对象。

三、分步去实现

     对于新手第一次看到这个逻辑图是不是有点懵逼呢,不要紧,我们先来实现下面几个关键的步骤:

    1、画棋盘
    import curses
    import random
    width = height = 4
    #定义一个嵌套列表,其中外层长度4,每一个元素的长度为4,
    field = [[0 for i in range(width)] for j in range(height)]
    def random_create():
        """
        将棋盘数据中field列表中任意两个元素变为2或者4,2出现的概率比4大
        :return:
        """
        while True:
            i, j = [random.choice(range(4)), random.choice(range(4))]
            if field[i][j] == 0:
                field[i][j] = 4 if random.randint(1,100)>80 else 2
                break

    def draw_sep(stdscr):
        """
        该函数用于画棋盘的横向边界
        :return:
        """
        line = '+'+'----+'*width
        stdscr.addstr(line+'\n')

    def draw_row(list_row,stdscr):
        """
        该函数用于画棋盘的纵向边界
        :param list_row:
        :return:
        """
           draw_one_row= ''.join(['|{:^4}'.format(num)\
                  if num!=0 else '|    ' for num in list_row])+'|'
        stdscr.addstr(draw_one_row+'\n')

    def draw_filed(field,stdscr):
        """
        该函数用于画棋盘和数据
        :param field:  数据
        :return:
        """
        for list_row in field:
            draw_sep(stdscr)
            draw_row(list_row,stdscr)
        draw_sep(stdscr)
    

    def main(stdscr):
            random_create()
            random_create()
            draw_filed(field,stdscr)
            #这里因为main函数执行太快,看不出来是否在字符窗口进行画棋盘,因此我们加入输入函数让程序在这里等待。
            ch = stdscr.getch()

    curses.wrapper(main)
        

    2、判断棋盘是否可以移动

            棋盘移动的条件是:

                1、我们的列表中的元素的值都至少有一个是0

                2、列表中有相邻的连个元素的值相等。

            判断棋盘是否可以移动,我们从四个方向出发:

                1、棋盘是否可以向左移动

                2、棋盘是否可以向右移动

                3、棋盘是否可以向下移动

                4、棋盘是否可以向上移动

            我们以判断棋盘是否可以向左移动为例:将4*4的棋盘我们拆成四个列表,我们对每一个列表使用条件1

     和条件2来判断这个列表是否可以移动,并将四个列表逐一判断完毕。如果有一个列表可以向左移动,那么整

     个棋盘都已向左移动,如果四个列表都不能向左移动那么,我们的棋盘就不能向左移动。

            对于期盼是否可以进行右移、上移或下移,我们可以通过将棋盘逆置或转置后的棋盘来判断是否可以左

     移,间接求出是否可以向其他方向移动,如果我们棋盘不能向任何一个方向移动且没有达到赢得游戏的条件,

     那么游戏结束。

field = [
    [1,2,3,0],
    [3,4,1,2],
    [2,1,3,1],
    [1,4,2,3]
]


def is_change(row):
    """
    :param row: 棋盘中的某一行
    :return: 该行可以向左移动返回Ture,否则返回False
    """
    def move(i):
        if row[i] == 0 and row[i + 1] != 0:
            return True
        elif row[i] != 0 and row[i + 1] == row[i]:
            return True
        else:
            return False
    return any([move(i) for i in range(len(row)-1)])

def move_left_possible(field):
    """

    :param field: 棋盘
    :return:  棋盘是否可以向左移动
    """
    return any([is_change(row) for row in field])

def move_right_possible(field):
    """
    判断将列表的每一行反转后的列表是否可以向左移动,如果可以,则元列表可以向右移动
    :return:
    """
    return move_left_possible(invert(field))

def move_up_possible(field):
    """
    判断逆置后的列表是否可以向左移动,若可以,则原列表可以向上移动
    :return:
    """
    return move_left_possible(tranpose(field))

def move_dowm_possible(field):
    """
    判断向逆置后的列表是否可以向右移动,若可以,则原列表就可以向下移动
    :return:
    """
    return move_right_possible(tranpose(field))

def invert(field):
    """
    对列表的每一行进行反转
    :return:
    """
    return [row[::-1] for row in field]

def tranpose(field):
    """
    对于列表进行转置
    :return:
    """
    return [list(row) for row in zip(*field)]
#一个存有棋盘操作的字典,其key值为上下左右,value值对应是否可以移动的函数。
check ={}
check['Left'] = move_left_possible(field)
print(check['Left'])
check['Right'] = move_right_possible(field)
print(check['Right'])
check['Up'] = move_up_possible(field)
print(check['Up'])
check['Down'] = move_dowm_possible(field)
print(check['Down'])
    3、移动棋盘

        移动棋盘时,我们同样以一行为移动的最小单位,根据棋盘移动的两个条件对其盘进行移动。以棋盘向左移

 动为例。

score = 0
def tight(row):
    """
    将棋盘的一行向左移动,将一行中不等于0的数移到最前面
    :param row:棋盘的一行移动
    :return:
    """
    new_row = [item for item in row if item!=0]  
    new_row += [0 for item in range(len(row)-len(new_row))]
    return new_row


def merge(row):
    """
    将两个相等的数进行相加,将索引小的数变为原来的2倍,而索引大的数变为0
    :param row: 棋盘的一行移动
    :return: 
    """
    for i in range(len(row)-1):
        if row[i] == row[i+1]:
            row[i] *= 2
            row[i+1] = 0
            global score
            score += row[i]
    return row

print(merge([2,2,0,0]))
print(tight(merge([2,2,2,0])))
print(tight(merge([2,2,2,2])))

        对于棋盘向右移动,我们可以先对棋盘的行进行翻转后得到的棋盘进行向左移动,之后再将棋盘翻转回去,

 得到向右移动的棋盘。向上移动我们可以,对逆置后的棋盘向左移动,之后再次逆转得到向上移动后的棋盘;向

下移动棋盘,对逆置后的的棋盘进行向有移动,再次逆置后的到向下移动的棋盘。

三、思路代码整理

    1、棋盘类

      我们需要创建一个棋盘类,这个类的属性有:

             棋盘的列表,该局的得分,历史最高得分,获胜的值,棋盘的长和宽

        其方法有:判断棋盘是否可以移动、移动棋盘、按行将棋盘翻转、将棋盘逆置、判断是不是赢了、

                        判断gameOver、将棋盘画在字符窗口上

import curses
import random
from itertools import chain
class field_class(object):
    def __init__(self,width=4,height=4,win_value=2048):
        """
        初始化棋盘的所有变量
        :param width: 棋盘的宽
        :param height: 棋盘的高
        :param win_value: 游戏获胜的值
        """
        self.width = width
        self.height = height
        self.win_value = win_value
        self.score = 0 #当前一局的分数
        self.highscore = 0 #历史最高分数
        self.field = []  #棋盘数据生成
        self.Init_field()   #在棋盘初始化完成之后,将随机改变两个元素的值

    def Init_field(self):
        """
        初始化棋盘
        :return: 无
        """
        if self.score > self.highscore:
            self.highscore = self.highscore
        self.score = 0
        self.field = [[0 for i in range(self.width)] for j in range(self.height)]
        self.random_create()
        self.random_create()

    def random_create(self):
        """
        将棋盘数据中slef.field列表中任意两个元素变为2或者4,2出现的概率比4大
        :return:
        """
        while True:
            i, j = [random.choice(range(4)), random.choice(range(4))]
            if self.field[i][j] == 0:
                self.field[i][j] = 4 if random.randint(1, 100) > 80 else 2
                break
    def draw_field(self,stdscr):

        def draw_sep(stdscr):
            """
            该函数用于画棋盘的横向边界
            :return:
            """
            line = '+' + '----+' * self.width
            stdscr.addstr(line + '\n')

        def draw_row(list_row, stdscr):
            """
            该函数用于画棋盘的纵向边界
            :param list_row:
            :return:
            """
            draw_one_row = ''.join(['|{:^4}'.format(num) \
                            if num != 0 else '|    ' for num in list_row]) + '|'
            stdscr.addstr(draw_one_row + '\n')

        # 清屏
        stdscr.clear()
        stdscr.addstr('SCORE:' + str(self.score) + '\n')
        if self.highscore != 0:
            stdscr.addstr("HIGHSCORE:" + str(self.highscore) + '\n\n')
        for list_row in self.field:
            draw_sep(stdscr)
            draw_row(list_row, stdscr)
        draw_sep(stdscr)
        if self.is_win():
            stdscr.addstr('You win!' + '\n')
        if self.is_gameover():
            stdscr.addstr('Game Over!' + '\n')
        stdscr.addstr("                上下左右键" + '\n')
        stdscr.addstr("       (R)Restart (Q)Exit" + '\n')
        
    def is_win(self):
        return max(chain(*self.field)) >= self.win_value

    def is_gameover(self):
        """任何方向都不能移动时"""
        return not any([self.move_is_possible(direction)
                        for direction in ['Up', 'Down', 'Right', 'Left']])

    def move_field(self,direction):
        """对棋盘进行移动"""
        dict_move=dict()
        def move_left(field):
            """
            向左移动棋盘
            :return: 移动成功返回移动后的棋盘,失败返回False
            """
            def tight(row):
                """
                将棋盘的一行向左移动,将一行中不等于0的数移到最前面
                :param row:棋盘的一行移动
                :return:
                """
                new_row = [item for item in row if item != 0]
                new_row += [0 for item in range(len(row) - len(new_row))]
                return new_row

            def merge(row):
                """
                将两个相等的数进行相加,将索引小的数变为原来的2倍,而索引大的数变为0
                :param row: 棋盘的一行移动
                :return:
                """
                for i in range(len(row) - 1):
                    if row[i] == row[i + 1]:
                        row[i] *= 2
                        row[i + 1] = 0
                        global score
                        score += row[i]
                return row
            new_field = [ tight(merge(row))for row in field ]
            return move_left(self.field)

        def move_right(field):
            """
            向右移动棋盘
            棋盘向右移动,先对棋盘的行进行翻转后得到的棋盘进行
            向左移动,之后再将棋盘翻转回去,得到向右移动的棋盘
            :return: 移动成功返回移动后的棋盘,失败返回False
            """
            return self.invert(move_left(self.invert(field)))


        def move_up(field):
            """
            向上移动棋盘,对逆置后的棋盘向左移动,之后再次逆转得到向上移动后的棋盘
            :return: 移动成功返回移动后的棋盘,失败返回False
            """
            return self.tranpose(move_left(self.tranpose(field)))

        def move_down(field):
            """
            向下移动棋盘,对逆置后的的棋盘进行向有移动,再次逆置后的到向下移动的棋盘。
            :return:  移动成功返回移动后的棋盘,失败返回False
            """
            return self.tranpose(move_right(self.tranpose(field)))


        dict_move['Left'] = move_left(self.field)
        dict_move['Right'] = move_right(self.field)
        dict_move['Down'] = move_down(self.field)
        dict_move['Up'] = move_up(self.field)

        if direction in dict_move.keys():
            if self.move_is_possible(direction):
                return dict_move[direction]
            else:
                return False

    def move_is_possible(self,direction):

        def move_left_possible(field):
            """
            :param field: 棋盘
            :return:  棋盘是否可以向左移动
            """
            def is_change(row):
                """
                :param row: 棋盘中的某一行
                :return: 该行可以向左移动返回Ture,否则返回False
                """
                def move(i):
                    if row[i] == 0 and row[i + 1] != 0:
                        return True
                    elif row[i] != 0 and row[i + 1] == row[i]:
                        return True
                    else:
                        return False
                return any([move(i) for i in range(len(row) - 1)])
            return any([is_change(row) for row in field])

        def move_right_possible(field):
            """
            判断将列表的每一行反转后的列表是否可以向左移动,如果可以,则元列表可以向右移动
            :param field: 棋盘
            :return:  棋盘可以向右移动 True 反之 False
            """
            return move_left_possible(self.invert(field))

        def move_up_possible(field):
            """
            判断逆置后的列表是否可以向左移动,若可以,则原列表可以向上移动
            :param field:棋盘
            :return: 棋盘可以向上移动 True 反之 False
            """
            return move_left_possible(self.tranpose(field))

        def move_down_possible(field):
            """
            判断向逆置后的列表是否可以向右移动,若可以,则原列表就可以向下移动
            :param field:棋盘
            :return: 棋盘可以向下移动 True 反之 False
            """
            return move_right_possible(self.tranpose(field))
        move_possible=dict()
        move_possible['Left'] = move_left_possible(self.field)
        move_possible['Right'] = move_right_possible(self.field)
        move_possible['Up'] = move_up_possible(self.field)
        move_possible['Down'] = move_down_possible(self.field)
        if direction in move_possible.keys():
            return move_possible[direction]
        else:
            return False

    @staticmethod
    def invert(field):
        """
        对列表的每一行进行反转
        :return:
        """
        return [row[::-1] for row in field]
    @staticmethod
    def tranpose(field):
        """
        对于列表进行转置
        :return:
        """
        return [list(row) for row in zip(*field)]
    2、获取用户键盘输入接口

         我们使用curses模块来获取用户从键盘输入的值

def get_user_action(stdscr):
    action = stdscr.getch()
    if action == curses.KEY_UP:
        return 'Up'
    if action == curses.KEY_DOWN:
        return 'Down'
    if action == curses.KEY_LEFT:
        return 'Left'
    if action == curses.KEY_RIGHT:
        return 'Right'
    if action == ord('r'):
        return 'Restart'
    if action == ord('q'):
        return 'Exit'


def main(stdscr):
    def init():
        # 初始化棋盘;
        game_field.Init_field()
        return 'Game'

    def game():
        # 画棋盘
        game_field.draw_field(stdscr)
        # 获取用户的操作
        action = get_user_action(stdscr)
        if action == 'Restart':
            return 'Init'
        if action == 'Exit':
            return 'Exit'
        if game_field.move_field(action):
            if game_field.is_win():
                return 'Win'
            if game_field.is_gameover():
                return 'GameOver'
        return 'Game'

    def not_game(state):
        game_field.draw_field(stdscr)
        while True:
            action = get_user_action(stdscr)
            if action == 'Restart':
                return 'Init'
            if action == 'Exit':
                return 'Exit'

    state_actions = {
        'Init': init,
        'Game': game,
        'Win': lambda: not_game('Win'),
        'GameOver': lambda: not_game('GameOver')
    }
    game_field = field_class(win_value=128)
    state = 'Init'
    while state != 'Exit':
        state = state_actions[state]()

四、全部代码

import curses
import random
from itertools import chain

class field_class(object):
    def __init__(self,width=4,height=4,win_value=2048):
        """
        初始化棋盘的所有变量
        :param width: 棋盘的宽
        :param height: 棋盘的高
        :param win_value: 游戏获胜的值
        """
        self.width = width
        self.height = height
        self.win_value = win_value
        self.score = 0 #当前一局的分数
        self.highscore = 0 #历史最高分数
        self.field = []  #棋盘数据生成
        self.Init_field()   #在棋盘初始化完成之后,将随机改变两个元素的值

    def Init_field(self):
        """
        初始化棋盘
        :return: 无
        """
        if self.score > self.highscore:
            self.highscore = self.highscore
        self.score = 0
        self.field = [[0 for i in range(self.width)] for j in range(self.height)]
        self.random_create()
        self.random_create()

    def random_create(self):
        """
        将棋盘数据中slef.field列表中任意两个元素变为2或者4,2出现的概率比4大
        :return:
        """
        while True:
            i, j = [random.choice(range(4)), random.choice(range(4))]
            if self.field[i][j] == 0:
                self.field[i][j] = 4 if random.randint(1, 100) > 80 else 2
                break
    def draw_field(self,stdscr):

        def draw_sep(stdscr):
            """
            该函数用于画棋盘的横向边界
            :return:
            """
            line = '+' + '----+' * self.width
            stdscr.addstr(line + '\n')

        def draw_row(list_row, stdscr):
            """
            该函数用于画棋盘的纵向边界
            :param list_row:
            :return:
            """
            draw_one_row = ''.join(['|{:^4}'.format(num) \
                            if num != 0 else '|    ' for num in list_row]) + '|'
            stdscr.addstr(draw_one_row + '\n')

        # 清屏
        stdscr.clear()
        stdscr.addstr('SCORE:' + str(self.score) + '\n')
        if self.highscore != 0:
            stdscr.addstr("HIGHSCORE:" + str(self.highscore) + '\n\n')
        for list_row in self.field:
            draw_sep(stdscr)
            draw_row(list_row, stdscr)
        draw_sep(stdscr)
        if self.is_win():
            stdscr.addstr('You win!' + '\n')
        if self.is_gameover():
            stdscr.addstr('Game Over!' + '\n')
        stdscr.addstr("                上下左右键" + '\n')
        stdscr.addstr("       (R)Restart (Q)Exit" + '\n')

    def is_win(self):
        return max(chain(*self.field)) >= self.win_value

    def is_gameover(self):
        """任何方向都不能移动时"""
        return not any([self.move_is_possible(direction)
                        for direction in ['Up', 'Down', 'Right', 'Left']])


    def move_field(self,direction):
        """对棋盘进行移动"""
        dict_move=dict()
        def move_left(field):
            """
            向左移动棋盘
            :return: 移动成功返回移动后的棋盘,失败返回False
            """
            def tight(row):
                """
                将棋盘的一行向左移动,将一行中不等于0的数移到最前面
                :param row:棋盘的一行移动
                :return:
                """
                new_row = [item for item in row if item != 0]
                new_row += [0 for item in range(len(row) - len(new_row))]
                return new_row

            def merge(row):
                """
                将两个相等的数进行相加,将索引小的数变为原来的2倍,而索引大的数变为0
                :param row: 棋盘的一行移动
                :return:
                """
                for i in range(len(row) - 1):
                    if row[i] == row[i + 1]:
                        row[i] *= 2
                        row[i + 1] = 0
                        self.score += row[i]
                return row
            new_field = [ tight(merge(row))for row in field ]
            return new_field

        def move_right(field):
            """
            向右移动棋盘
            棋盘向右移动,先对棋盘的行进行翻转后得到的棋盘进行
            向左移动,之后再将棋盘翻转回去,得到向右移动的棋盘
            :return: 移动成功返回移动后的棋盘,失败返回False
            """
            return self.invert(move_left(self.invert(field)))


        def move_up(field):
            """
            向上移动棋盘,对逆置后的棋盘向左移动,之后再次逆转得到向上移动后的棋盘
            :return: 移动成功返回移动后的棋盘,失败返回False
            """
            return self.tranpose(move_left(self.tranpose(field)))

        def move_down(field):
            """
            向下移动棋盘,对逆置后的的棋盘进行向有移动,再次逆置后的到向下移动的棋盘。
            :return:  移动成功返回移动后的棋盘,失败返回False
            """
            return self.tranpose(move_right(self.tranpose(field)))


        dict_move['Left'] = move_left
        dict_move['Right'] = move_right
        dict_move['Down'] = move_down
        dict_move['Up'] = move_up

        if direction in dict_move.keys():
            if self.move_is_possible(direction):
                self.field = dict_move[direction](self.field)
                self.random_create()
                return True
            else:
                return False


    def move_is_possible(self,direction):

        def move_left_possible(field):
            """
            :param field: 棋盘
            :return:  棋盘是否可以向左移动
            """
            def is_change(row):
                """
                :param row: 棋盘中的某一行
                :return: 该行可以向左移动返回Ture,否则返回False
                """
                def move(i):
                    if row[i] == 0 and row[i + 1] != 0:
                        return True
                    elif row[i] != 0 and row[i + 1] == row[i]:
                        return True
                    else:
                        return False
                return any([move(i) for i in range(len(row) - 1)])
            return any([is_change(row) for row in field])

        def move_right_possible(field):
            """
            判断将列表的每一行反转后的列表是否可以向左移动,如果可以,则元列表可以向右移动
            :param field: 棋盘
            :return:  棋盘可以向右移动 True 反之 False
            """
            return move_left_possible(self.invert(field))

        def move_up_possible(field):
            """
            判断逆置后的列表是否可以向左移动,若可以,则原列表可以向上移动
            :param field:棋盘
            :return: 棋盘可以向上移动 True 反之 False
            """
            return move_left_possible(self.tranpose(field))

        def move_down_possible(field):
            """
            判断向逆置后的列表是否可以向右移动,若可以,则原列表就可以向下移动
            :param field:棋盘
            :return: 棋盘可以向下移动 True 反之 False
            """
            return move_right_possible(self.tranpose(field))

        move_possible=dict()
        move_possible['Left'] = move_left_possible
        move_possible['Right'] = move_right_possible
        move_possible['Up'] = move_up_possible
        move_possible['Down'] = move_down_possible
        if direction in move_possible.keys():
            return move_possible[direction](self.field)
        else:
            return False

    @staticmethod
    def invert(field):
        """
        对列表的每一行进行反转
        :return:
        """
        return [row[::-1] for row in field]

    @staticmethod
    def tranpose(field):
        """
        对于列表进行转置
        :return:
        """
        return [list(row) for row in zip(*field)]

def get_user_action(stdscr):
    action = stdscr.getch()
    if action == curses.KEY_UP:
        return 'Up'
    if action == curses.KEY_DOWN:
        return 'Down'
    if action == curses.KEY_LEFT:
        return 'Left'
    if action == curses.KEY_RIGHT:
        return 'Right'
    if action == ord('r'):
        return 'Restart'
    if action == ord('q'):
        return 'Exit'
def main(stdscr):
    def init():
        # 初始化棋盘;
        game_field.Init_field()
        return 'Game'

    def game():
        # 画棋盘
        game_field.draw_field(stdscr)
        # 获取用户的操作
        action = get_user_action(stdscr)
        if action == 'Restart':
            return 'Init'
        if action == 'Exit':
            return 'Exit'
        if game_field.move_field(action):
            if game_field.is_win():
                return 'Win'
            if game_field.is_gameover():
                return 'GameOver'
        return 'Game'

    def not_game(state):
        game_field.draw_field(stdscr)
        while True:
            action = get_user_action(stdscr)
            if action == 'Restart':
                return 'Init'
            if action == 'Exit':
                return 'Exit'

    state_actions = {
        'Init': init,
        'Game': game,
        'Win': lambda: not_game('Win'),
        'GameOver': lambda: not_game('GameOver')
    }
    game_field = field_class(win_value=128)
    state = 'Init'
    while state != 'Exit':
        state = state_actions[state]()

curses.wrapper(main)

五、巩固练习

       写一个双人版的2048,一个人使用上下左右键,一个人使用W、A、S、D进行控制

import curses
import random
from itertools import chain

class field_class(object):
    def __init__(self, x, y, colorpair=2,width=4,height=4,win_value=2048):
        """
        初始化棋盘的所有变量
        :param width: 棋盘的宽
        :param height: 棋盘的高
        :param win_value: 游戏获胜的值
        """
        self.x = x
        self.y = y
        self.colorpair = colorpair
        self.width = width
        self.height = height
        self.win_value = win_value
        self.score = 0 #当前一局的分数
        self.highscore = 0 #历史最高分数
        self.field = []  #棋盘数据生成
        self.Init_field()   #在棋盘初始化完成之后,将随机改变两个元素的值

    def Init_field(self):
        """
        初始化棋盘
        :return: 无
        """
        if self.score > self.highscore:
            self.highscore = self.highscore
        self.score = 0
        self.field = [[0 for i in range(self.width)] for j in range(self.height)]
        self.random_create()
        self.random_create()

    def random_create(self):
        """
        将棋盘数据中slef.field列表中任意两个元素变为2或者4,2出现的概率比4大
        :return:
        """
        while True:
            i, j = [random.choice(range(4)), random.choice(range(4))]
            if self.field[i][j] == 0:
                self.field[i][j] = 4 if random.randint(1, 100) > 80 else 2
                break
    def draw_field(self,stdscr):

        def draw_sep(stdscr):
            """
            该函数用于画棋盘的横向边界
            :return:
            """
            line = '+' + '----+' * self.width
            stdscr.addstr(line + '\n')

        def draw_row(list_row, stdscr):
            """
            该函数用于画棋盘的纵向边界
            :param list_row:
            :return:
            """
            draw_one_row = ''.join(['|{:^4}'.format(num) \
                            if num != 0 else '|    ' for num in list_row]) + '|'
            stdscr.addstr(draw_one_row + '\n')

        # 清屏
        # stdscr.clear()
        stdscr.addstr(self.x,self.y,'SCORE:' + str(self.score) + '\n',self.colorpair)
        if self.highscore != 0:
            stdscr.addstr("HIGHSCORE:" + str(self.highscore) + '\n\n')
        for list_row in self.field:
            draw_sep(stdscr)
            draw_row(list_row, stdscr)
        draw_sep(stdscr)
        if self.is_win():
            stdscr.addstr('You win!' + '\n')
        if self.is_gameover():
            stdscr.addstr('Game Over!' + '\n')
        stdscr.addstr("                上下左右键" + '\n')
        stdscr.addstr("       (R)Restart (Q)Exit" + '\n')

    def is_win(self):
        return max(chain(*self.field)) >= self.win_value

    def is_gameover(self):
        """任何方向都不能移动时"""
        return not any([self.move_is_possible(direction)
                        for direction in ['Up', 'Down', 'Right', 'Left']])


    def move_field(self,direction):
        """对棋盘进行移动"""
        dict_move=dict()
        def move_left(field):
            """
            向左移动棋盘
            :return: 移动成功返回移动后的棋盘,失败返回False
            """
            def tight(row):
                """
                将棋盘的一行向左移动,将一行中不等于0的数移到最前面
                :param row:棋盘的一行移动
                :return:
                """
                new_row = [item for item in row if item != 0]
                new_row += [0 for item in range(len(row) - len(new_row))]
                return new_row

            def merge(row):
                """
                将两个相等的数进行相加,将索引小的数变为原来的2倍,而索引大的数变为0
                :param row: 棋盘的一行移动
                :return:
                """
                for i in range(len(row) - 1):
                    if row[i] == row[i + 1]:
                        row[i] *= 2
                        row[i + 1] = 0
                        self.score += row[i]
                return row
            new_field = [ tight(merge(row))for row in field ]
            return new_field

        def move_right(field):
            """
            向右移动棋盘
            棋盘向右移动,先对棋盘的行进行翻转后得到的棋盘进行
            向左移动,之后再将棋盘翻转回去,得到向右移动的棋盘
            :return: 移动成功返回移动后的棋盘,失败返回False
            """
            return self.invert(move_left(self.invert(field)))


        def move_up(field):
            """
            向上移动棋盘,对逆置后的棋盘向左移动,之后再次逆转得到向上移动后的棋盘
            :return: 移动成功返回移动后的棋盘,失败返回False
            """
            return self.tranpose(move_left(self.tranpose(field)))

        def move_down(field):
            """
            向下移动棋盘,对逆置后的的棋盘进行向有移动,再次逆置后的到向下移动的棋盘。
            :return:  移动成功返回移动后的棋盘,失败返回False
            """
            return self.tranpose(move_right(self.tranpose(field)))


        dict_move['Left'] = move_left
        dict_move['Right'] = move_right
        dict_move['Down'] = move_down
        dict_move['Up'] = move_up

        if direction in dict_move.keys():
            if self.move_is_possible(direction):
                self.field = dict_move[direction](self.field)
                self.random_create()
                return True
            else:
                return False


    def move_is_possible(self,direction):

        def move_left_possible(field):
            """
            :param field: 棋盘
            :return:  棋盘是否可以向左移动
            """
            def is_change(row):
                """
                :param row: 棋盘中的某一行
                :return: 该行可以向左移动返回Ture,否则返回False
                """
                def move(i):
                    if row[i] == 0 and row[i + 1] != 0:
                        return True
                    elif row[i] != 0 and row[i + 1] == row[i]:
                        return True
                    else:
                        return False
                return any([move(i) for i in range(len(row) - 1)])
            return any([is_change(row) for row in field])

        def move_right_possible(field):
            """
            判断将列表的每一行反转后的列表是否可以向左移动,如果可以,则元列表可以向右移动
            :param field: 棋盘
            :return:  棋盘可以向右移动 True 反之 False
            """
            return move_left_possible(self.invert(field))

        def move_up_possible(field):
            """
            判断逆置后的列表是否可以向左移动,若可以,则原列表可以向上移动
            :param field:棋盘
            :return: 棋盘可以向上移动 True 反之 False
            """
            return move_left_possible(self.tranpose(field))

        def move_down_possible(field):
            """
            判断向逆置后的列表是否可以向右移动,若可以,则原列表就可以向下移动
            :param field:棋盘
            :return: 棋盘可以向下移动 True 反之 False
            """
            return move_right_possible(self.tranpose(field))

        move_possible=dict()
        move_possible['Left'] = move_left_possible
        move_possible['Right'] = move_right_possible
        move_possible['Up'] = move_up_possible
        move_possible['Down'] = move_down_possible
        if direction in move_possible.keys():
            return move_possible[direction](self.field)
        else:
            return False

    @staticmethod
    def invert(field):
        """
        对列表的每一行进行反转
        :return:
        """
        return [row[::-1] for row in field]

    @staticmethod
    def tranpose(field):
        """
        对于列表进行转置
        :return:
        """
        return [list(row) for row in zip(*field)]

def get_user_action(stdscr):
    action = stdscr.getch()
    if action == curses.KEY_UP:
        return 'Up'
    if action == curses.KEY_DOWN:
        return 'Down'
    if action == curses.KEY_LEFT:
        return 'Left'
    if action == curses.KEY_RIGHT:
        return 'Right'
    if action == ord('w'):
        return 'Up2'
    if action == ord('s'):
        return 'Down2'
    if action == ord('a'):
        return 'Left2'
    if action == ord('d'):
        return 'Right2'
    if action == ord('r'):
        return 'Restart'
    if action == ord('q'):
        return 'Exit'
def main(stdscr):
    def init():
        # 初始化棋盘;
        game_field1.Init_field()
        game_field2.Init_field()
        return 'Game'

    def game():
        # 画棋盘
        game_field1.draw_field(stdscr)
        game_field2.draw_field(stdscr)
        # 获取用户的操作
        action = get_user_action(stdscr)
        if action == 'Restart':
            return 'Init'
        if action == 'Exit':
            return 'Exit'
        if action in ['Up', 'Down', 'Right', 'Left']:
            flage = 1
            if game_field1.move_field(action):
                if game_field1.is_win():
                    return 'Win'
                if game_field1.is_gameover():
                    return 'GameOver'

        if action in ['Up2', 'Down2', 'Right2', 'Left2']:
            flage = 2
            if game_field2.move_field(action[:-1]):
                if game_field2.is_win():
                    return 'Win'
                if game_field2.is_gameover():
                    return 'GameOver'
        return 'Game'

    def not_game(state):
            game_field1.draw_field(stdscr)
            game_field2.draw_field(stdscr)
            while True:
                action = get_user_action(stdscr)
                if action == 'Restart':
                    return 'Init'
                if action == 'Exit':
                    return 'Exit'

    state_actions = {
        'Init': init,
        'Game': game,
        'Win': lambda: not_game('Win'),
        'GameOver': lambda: not_game('GameOver')
    }

    game_field1 = field_class(2,1,win_value=2048)
    game_field2 = field_class(20, 1,win_value=2048)
    state = 'Init'
    while state != 'Exit':
        state = state_actions[state]()

curses.wrapper(main)
六、总结

        这个游戏的难点就在于怎样去实现棋盘类的方法,其次注意我们的程序的几种状态,和状态之间的转换。



猜你喜欢

转载自blog.csdn.net/m0_37717595/article/details/80504800