[实验楼] Python 实现 2048 游戏
程序员文章站
2023-08-24 17:28:15
参考:https://www.shiyanlou.com/courses/368本节实验中将学习和实践以下知识点:Python 基本知识curses 终端图形编程库random 随机数模块collections 容器数据类型库状态机的概念项目样式如下(main函数中)state 存储当前状态, state_actions 这个词典变量作为状态转换的规则,它的 key 是状态,value 是返回下一个状态的函数:与原代码相比,添加了上下左右键处理(原来只有wasd键),胜利后按任意键(除...
参考:https://www.shiyanlou.com/courses/368
本节实验中将学习和实践以下知识点:
Python 基本知识
curses 终端图形编程库
random 随机数模块
collections 容器数据类型库
状态机的概念
项目样式如下
(main函数中)state 存储当前状态, state_actions 这个词典变量作为状态转换的规则,它的 key 是状态,value 是返回下一个状态的函数:
与原代码相比,添加了上下左右键处理(原来只有wasd键),胜利后按任意键(除了r,q)继续游戏,把一部分函数改的更亲民了(比如move_is_possible,not_game,原来的好变态…不,是高深)
import curses # curses 用来在终端上显示图形界面
from random import randrange, choice # random 模块用来生成随机数
actions = ['Up', 'Left', 'Down', 'Right', 'Restart', 'Exit']
letter_codes = [ord(ch) for ch in 'WASDRQwasdrq'] + [259,260,258,261]
# ord转换为ASCII码 后面四个是上左下右
#[87, 65, 83, 68, 82, 81, 119, 97, 115, 100, 114, 113, 259, 260, 258, 261]
win_continue = False #胜利后是否继续
actions_dict = dict(zip(letter_codes, actions * 2 + actions[:4]))
#{87: 'Up', 65: 'Left', 83: 'Down', 68: 'Right', 82: 'Restart', 81: 'Exit', 119: 'Up', 97: 'Left', 115: 'Down', 100: 'Right', 114: 'Restart', 113: 'Exit', 259: 'Up', 260: 'Left', 258: 'Down', 261: 'Right'}
#对角置换
def transpose(field):
return [list(row) for row in zip(*field)]
#水平反置
def invert(field):
return [row[::-1] for row in field]
# 用户输入
def get_user_action(keybord):
char = '#'
while char not in actions_dict:
char = keybord.getch() # 返回按下键位的 ASCII 码值
return actions_dict[char]
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: transpose(moves['Left'](transpose(field)))
moves['Down'] = lambda field: transpose(moves['Right'](transpose(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):
global win_continue
if win_continue:
return False
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 draw(self, screen):
help_str1 = '(W)Up (S)Down (A)Left (D)Right'
help_str2 = ' (R)Restart (Q)Exit '
gameover_str = ' Game Over '
win_str = ' You Win '
# 绘制函数
def cast(string):
# addstr()将传入的内容展示到屏幕上
screen.addstr(string + '\n')
# 水平线
def draw_hor_separator():
line = '+------' * self.width + '+'
cast(line)
def draw_row(row):
cast(''.join('|{:^6}'.format(num) if num > 0 else '! ' for num in row) + '|')
screen.clear()
cast('SCORE: ' + str(self.score))
if self.highscore != 0:
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_str)
else:
if self.is_gameover():
cast(gameover_str)
else:
cast(help_str1)
cast(help_str2)
#判断某个方向能否移动
#有0一定可以,否则需要这个方向上有相邻且相同的数
def move_is_possible(self, direction):
if any(any(num==0 for num in row) for row in self.field):
return True
if direction == 'Left' or direction == 'Right':
for i in range(0, self.height):
for j in range(1, self.width):
if self.field[i][j] == self.field[i][j-1]:
return True
else:
for i in range(1, self.height):
for j in range(0, self.width):
if self.field[i][j] == self.field[i-1][j]:
return True
return False
def main(stdscr):
#标准屏幕 来自curses库
def init():
# 初始化棋盘
global win_continue
win_continue = False
game_field.reset()
return 'Game'
def not_game(state):
# 绘制界面
game_field.draw(stdscr)
if state == 'Win':
stdscr.addstr('press other key to continue\n')
# 获取用户键盘输入
action = get_user_action(stdscr)
if action == 'Exit':
return 'Exit'
if action == 'Restart':
return 'Init'
if state == 'Win':
global win_continue
win_continue = True
return 'Game'
return state
def game():
game_field.draw(stdscr)
action = get_user_action(stdscr)
if action == 'Exit':
return 'Exit'
if action == 'Restart':
return 'Init'
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)
"""
首先, curses.wrapper 函数会激活并初始化终端进入 'curses 模式'。
在这个模式下会禁止输入的字符显示在终端上、禁止终端程序的行缓冲(line buffering),即字符在输入时就可以使用,不需要遇到换行符或回车。
接着,curses.wrapper 函数需要传一个函数作为参数,这个传进去的函数必须满足第一个参数为主窗体(main window) stdscr。 在前面的代码里,可以看到我们给 curses.wrapper(main) 的 main 函数中传入了一个 stdscr。
最后,stdscr 作为 window.addstr(str)、window.clear() 方法的调用需要窗体对象(window object),在 game_field.draw(stdscr) 中传入 draw 方法中。
"""
本文地址:https://blog.csdn.net/qq_33831360/article/details/107550841
上一篇: 茶树菇用处你了解吗