一、游戏逻辑分析
对于一个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)六、总结
这个游戏的难点就在于怎样去实现棋盘类的方法,其次注意我们的程序的几种状态,和状态之间的转换。