一、基础版本
流程图:
设计思路:
游戏分为<初始化init><游戏中game><胜利win><失败gameover>四个状态;
main函数中主要有init函数,game函数和not_game函数,函数的返回值作为字典的key值,
函数作为value值生成字典,循环捕获key值,通过key值的变化来调用函数,使整个程序循环运转!
1).init函数
初始化创建数据的函数,在捕获用户操作restart时返回此函数,函数返回值为key值game,去进行游戏操作
2).game函数
该函数主要分为,画表格(将初始化的数据按一定格式画出来),不湖欧用户操作进行移动,移动完进行结束
游戏条件(胜利|失败)判断,不满足条件返回key值game,继续进行游戏操作,满足则返回key值win|gameover
进行游戏外设置(重置|退出)操作
3).not_game函数
主要进行游戏结束用户选择操作,捕获用户操作,restart返回key值init,重新游戏。exit退出游戏
import curses from itertools import chain from random import choice, randint class GameField(object): def __init__(self, width=4, height=4, win_value=2048): self.width = width self.height = height self.win_value = win_value self.score = 0 self.highscore = 0 self.reset() def reset(self): """重置棋盘""" if self.score > self.win_value: self.highscore = self.score 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): """初始化棋盘时, 在随机位置生成2或者4, 2的可能性大, 4的可能性少""" # 可能出现的问题: 随机生成的i,j位置原本已经有值。 解决方法: while True: i, j = choice(range(self.width)), choice(range(self.height)) if self.field[i][j] == 0: self.field[i][j] = 4 if randint(1, 100) > 80 else 2 break def draw(self, stdscr): def draw_sep(): line = '+' + '----+' * self.width stdscr.addstr(line + '\n') def draw_row(row): # [2,0,2,0] draw_one_row = "".join(['|{:^4}'.format(num) if num != 0 else '| ' for num in 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 row in self.field: draw_sep() draw_row(row) draw_sep() 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']]) @staticmethod def invert(field): # 对于列表每一行进行反转 return [row[::-1] for row in field] @staticmethod def tranpose(field): """对于列表转置, 可以间接求向上移动的可能性""" return [list(row) for row in zip(*field)] def move_is_possible(self, direction): def move_left_possible(row): """判断列表中的一行是否可以移动""" # 0 2, 0 4, 2 2, 4 2 # 0 0 # # 1. 判断两个元素是否可以移动? # 4,2,2,2 def is_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 # len(row)-1 =4-1 = 3 # i= 0,1,2 # 依次遍历每一行的每一个元素, 判断是否可以移动, 只要有一个时可以移动的, 返回True return any([is_change(i) for i in range(len(row) - 1)]) check = {} check['Left'] = lambda field: any([move_left_possible(row) for row in field]) # check['Right'] = lambda field: any([ move_left_possible(row) for row in invert(field)]) # 判断每行内容反转后的field能否向左移动, 即原field能否向右移动; check['Right'] = lambda field: check['Left'](self.invert(field)) # 判断转置后的field能否向左移动, 即原field能否向上移动; check['Up'] = lambda field: check['Left'](self.tranpose(field)) # 判断转置后的field能否向右移动, 即原field能否向下移动; check['Down'] = lambda field: check['Right'](self.tranpose(field)) if direction in check: return check[direction](self.field) else: return False def move(self, direction): def move_row_left(row): def tight(row): """把所有非0的聚集在最左边""" 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, for i in range(len(row) - 1): # 0,1,2,3 if row[i] == row[i + 1]: row[i] *= 2 row[i + 1] = 0 self.score += row[i] return row return tight(merge(tight(row))) moves = {} moves['Left'] = lambda field: [move_row_left(row) for row in field] moves['Right'] = lambda field: self.invert([move_row_left(row) for row in self.invert(field)]) moves['Up'] = lambda field: self.tranpose([move_row_left(row) for row in self.tranpose(field)]) moves['Down'] = lambda field: self.tranpose(moves['Right'](self.tranpose(field))) if direction in moves: if self.move_is_possible(direction): self.field = moves[direction](self.field) self.random_create() return True else: return False 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.reset() return 'Game' def game(): # 画棋盘 game_field.draw(stdscr) # 获取用户的操作 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' def not_game(state): game_field.draw(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'), # ‘Init’, 'Exit' 'GameOver': lambda: not_game('GameOver') } game_field = GameField(win_value=2048) state = 'Init' while state != 'Exit': state = state_actions[state]() curses.wrapper(main)
二、双人模式
import curses from itertools import chain from random import choice , randint class GameFiled(object): def __init__(self,wight=4,hight=4,win_value=2048): """创建游戏的属性""" self.wight = wight self.hight = hight self.win_value = win_value self.score1 = 0 self.score2 = 0 self.file1 = [] self.file2 = [] def recreate_file(self): """重置棋盘""" self.file1 = [[0 for i in range(self.wight)] for j in range(self.hight)] self.file2 = [[0 for i in range(self.wight)] for j in range(self.hight)] self.create_randem(self.file1) self.create_randem(self.file1) self.create_randem(self.file2) self.create_randem(self.file2) def create_randem(self,file): """在棋盘中最初的两个数字""" while True: i,j = choice(range(self.wight)) , choice(range(self.hight)) if file[i][j] == 0: file[i][j] =4 if randint(0,100)>90 else 2 break def draw(self,stdscr): def draw_sep(): """画横线""" line = '+'+'----+'*self.wight stdscr.addstr(line +'\n') def draw_row(row): # [2,0,2,0] draw_one_row = "".join(['|{:^4}'.format(num) if num != 0 else '| ' for num in row]) + '|' stdscr.addstr(draw_one_row + '\n') stdscr.clear() stdscr.addstr('Player1_score:'+str(self.score1)+ ' \n') """画棋盘1""" for row in self.file1: draw_sep() draw_row(row) draw_sep() stdscr.addstr('Player2_score:' + str(self.score2) + ' \n') """画棋盘2""" for row in self.file1: draw_sep() draw_row(row) draw_sep() if self.is_win(): if self.file1 > self.file2: stdscr.addstr('Player1 Win' + '\n') elif self.file1 < self.file2: stdscr.addstr('Player2 Win' + '\n') if self.is_gameover(): stdscr.addstr('All GameOver' + '\n') stdscr.addstr('1P:Up Down Left Right\n2P:W A S D\n(R)Restart(Q)Exit\n') def is_win(self): """定义判断输赢的函数<chain将多个列表内的值链成一个新列表>""" return max(chain(*self.file1)) >= self.win_value or \ max(chain(*self.file2)) >= self.win_value def is_gameover(self): return (not any([self.move_possible(direction) for direction in ['Up', 'Down', 'Right', 'Left']])) and\ (not any([self.move_possible1(direction) for direction in ['Up1', 'Down1', 'Right1', 'Left1']])) @staticmethod def invert(filed): """对每一行进行反转""" return (row[::-1] for row in filed) @staticmethod def tranpose(field): """对于列表转置, 可以间接求向上移动的可能性""" return [list(row) for row in zip(*field)] def move_possible(self, direction): def leftrow_move_possible(row): def is_move(i): if row[i] == 0 and row[i+1] != 0: return True if row[i] == row[i+1] !=0: return True return False return any([is_move(i) for i in range(len(row) - 1)]) possible = {} possible['Left'] = lambda filed : any([leftrow_move_possible(row) for row in filed]) possible['Right'] = lambda filed: possible['Left'](self.invert(filed)) possible['Up'] = lambda filed:possible['Left'](self.tranpose(filed)) possible['Down'] = lambda filed :possible['Right'](self.tranpose(filed)) if direction in possible: return possible[direction](self.file1) else: return False def move_possible1(self, direction): def leftrow_move_possible(row): def is_move(i): if row[i] == 0 and row[i+1] != 0: return True if row[i] == row[i+1] !=0: return True return False return any([is_move(i) for i in range(len(row) - 1)]) possible = {} possible['Left1'] = lambda filed : any([leftrow_move_possible(row) for row in filed]) possible['Right1'] = lambda filed: possible['Left1'](self.invert(filed)) possible['Up1'] = lambda filed:possible['Left1'](self.tranpose(filed)) possible['Down1'] = lambda filed :possible['Right1'](self.tranpose(filed)) if direction in possible: return possible[direction](self.file2) else: return False def move(self,direction): def leftrow_move(row): def tight(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): for i in range(len(row)-1): if row[i] == row[i+1]: row[i] = row[i]*2 row[i+1] = 0 self.score1 += row[i] return row return tight(merge(tight(row))) def leftrow_move1(row): def tight(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): for i in range(len(row)-1): if row[i] == row[i+1]: row[i] = row[i]*2 row[i+1] = 0 self.score2 += row[i] return row return tight(merge(tight(row))) moves = {} moves['Left'] = lambda filed: [leftrow_move(row) for row in filed] moves['Right'] = lambda filed: self.invert([leftrow_move(row) for row in self.invert(filed)]) moves['Up'] = lambda filed: self.tranpose([leftrow_move(row) for row in self.tranpose(filed)]) moves['Down'] = lambda filed: self.tranpose(moves['Right'](self.tranpose(filed))) moves1 = {} moves1['Left1'] = lambda filed: [leftrow_move1(row) for row in filed] moves1['Right1'] = lambda filed: self.invert([leftrow_move1(row) for row in self.invert(filed)]) moves1['Up1'] = lambda filed: self.tranpose([leftrow_move1(row) for row in self.tranpose(filed)]) moves1['Down1'] = lambda filed: self.tranpose(moves1['Right1'](self.tranpose(filed))) if self.move_possible(direction): self.file1 = moves[direction](self.file1) self.create_randem(self.file1) return True elif self.move_possible1(direction): self.file2 = moves[direction](self.file2) self.create_randem(self.file2) return True else: return False #捕获玩家的操作 def get_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 'Up1' if action == ord('s'): return 'Down1' if action == ord('a'): return 'Left1' if action == ord('d'): return 'Right1' if action == ord('r'): return 'Restart' if action == ord('q'): return 'Exit' def main(stdscr): """调用游戏类""" def init(): game_filed.recreate_file() return 'Game' def game(): # 游戏内函数 game_filed.draw(stdscr) # 画棋盘 action = get_action(stdscr) # 获取用户操作 if action == 'Restart': # 重置游戏 return 'Init' if action == 'Exit': return 'Exit' if game_filed.move(action): # 其他操作让其移动 # 每次移动后判断输或者赢 if game_filed.is_win(): return 'Win' return 'Game' # 再次返回游戏函数 def not_game(): # 定义游戏外函数 game_filed.draw(stdscr) # 画棋盘 while True: action = get_action(stdscr) if action == 'Restart': # 重置游戏 return 'Init' if action == 'Exit': # 退出 return 'Exit' status_filed = {'Init': init, 'Game': game, 'Win': not_game, 'GameOver': not_game } game_filed = GameFiled(win_value=2048) status = 'Init' while status != "Exit": status = status_filed[status]() curses.wrapper(main)