一、基础版本
流程图:
设计思路:
游戏分为<初始化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)