200 行python 代码实现 2048 游戏


Posted in Python onJanuary 12, 2018

创建游戏文件 2048.py

首先导入需要的包:

import curses
from random import randrange, choice
from collections import defaultdict

主逻辑

用户行为

所有的有效输入都可以转换为"上,下,左,右,游戏重置,退出"这六种行为,用 actions 表示

actions = ['Up', 'Left', 'Down', 'Right', 'Restart', 'Exit']

有效输入键是最常见的 W(上),A(左),S(下),D(右),R(重置),Q(退出),这里要考虑到大写键开启的情况,获得有效键值列表:

letter_codes = [ord(ch) for ch in 'WASDRQwasdrq']

将输入与行为进行关联:

actionsdict = dict(zip(lettercodes, actions * 2))

状态机

处理游戏主逻辑的时候我们会用到一种十分常用的技术:状态机,或者更准确的说是有限状态机(FSM)

你会发现 2048 游戏很容易就能分解成几种状态的转换。

200 行python 代码实现 2048 游戏

state 存储当前状态, state_actions 这个词典变量作为状态转换的规则,它的 key 是状态,value 是返回下一个状态的函数:

Init: init()
Game: game()
Win: lambda: not_game('Win')
Gameover: lambda: not_game('Gameover')

Exit: 退出循环

状态机会不断循环,直到达到 Exit 终结状态结束程序。

下面是经过提取的主逻辑的代码,会在后面进行补全:

def main(stdscr):
 def init():
 #重置游戏棋盘
 return 'Game'
 def not_game(state):
 #画出 GameOver 或者 Win 的界面
 #读取用户输入得到action,判断是重启游戏还是结束游戏
 responses = defaultdict(lambda: state) #默认是当前状态,没有行为就会一直在当前界面循环
 responses['Restart'], responses['Exit'] = 'Init', 'Exit' #对应不同的行为转换到不同的状态
 return responses[action]
 def game():
 #画出当前棋盘状态
 #读取用户输入得到action
 if action == 'Restart':
  return 'Init'
 if action == 'Exit':
  return 'Exit'
 #if 成功移动了一步:
  if 游戏胜利了:
  return 'Win'
  if 游戏失败了:
  return 'Gameover'
 return 'Game'
 state_actions = {
  'Init': init,
  'Win': lambda: not_game('Win'),
  'Gameover': lambda: not_game('Gameover'),
  'Game': game
 }
 state = 'Init'
 #状态机开始循环
 while state != 'Exit':
 state = state_actions[state]()

用户输入处理

阻塞+循环,直到获得用户有效输入才返回对应行为:

def get_user_action(keyboard):
 char = "N"
 while char not in actions_dict:
 char = keyboard.getch()
 return actions_dict[char]

矩阵转置与矩阵逆转

加入这两个操作可以大大节省我们的代码量,减少重复劳动,看到后面就知道了。

矩阵转置:

def transpose(field):
 return [list(row) for row in zip(*field)]

矩阵逆转(不是逆矩阵):

def invert(field):
 return [row[::-1] for row in field]

创建棋盘

初始化棋盘的参数,可以指定棋盘的高和宽以及游戏胜利条件,默认是最经典的 4×4~2048。

class GameField(object):
 def __init__(self, height=4, width=4, win=2048):
 self.height = height #高
 self.width = width  #宽
 self.win_value = 2048 #过关分数
 self.score = 0  #当前分数
 self.highscore = 0  #最高分
 self.reset()  #棋盘重置

棋盘操作

随机生成一个 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 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()
#### 一行向左合并
(注:这一操作是在 move 内定义的,拆出来是为了方便阅读)
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)))

棋盘走一步

通过对矩阵进行转置与逆转,可以直接从左移得到其余三个方向的移动操作

def move(self, direction):
 def move_row_left(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):
 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 move_is_possible(self, direction):
 defrow_is_left_movable(row):
 def 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
 return any(change(i) for i in range(len(row) - 1))
 check = {}
 check['Left'] = lambda field: any(row_is_left_movable(row) for row in field)
 check['Right'] = lambda field: check['Left'](invert(field))
 check['Up'] = lambda field: check['Left'](transpose(field))
 check['Down'] = lambda field: check['Right'](transpose(field))
 if direction in check:
 return check[direction](self.field)
 else:
 return False

绘制游戏界面

def draw(self, screen):
 help_string1 = '(W)Up (S)Down (A)Left (D)Right'
 help_string2 = ' (R)Restart (Q)Exit'
 gameover_string = '  GAME OVER'
 win_string = '  YOU WIN!'
 def cast(string):
 screen.addstr(string + 'n')
 #绘制水平分割线
 def draw_hor_separator():
 line = '+' + ('+------' * self.width + '+')[1:]
 separator = defaultdict(lambda: line)
 if not hasattr(draw_hor_separator, "counter"):
  draw_hor_separator.counter = 0
 cast(separator[draw_hor_separator.counter])
 draw_hor_separator.counter += 1
 def draw_row(row):
 cast(''.join('|{: ^5} '.format(num) if num > 0 else '| ' for num in row) + '|')
 screen.clear()
 cast('SCORE: ' + str(self.score))
 if 0 != self.highscore:
 cast('HGHSCORE: ' + str(self.highscore))
 for row in self.field:
 draw_hor_separator()
 draw_row(row)
 draw_hor_separator()
 if self.is_win():
 cast(win_string)
 else:
 if self.is_gameover():
  cast(gameover_string)
 else:
  cast(help_string1)
 cast(help_string2)

完成主逻辑

完成以上工作后,我们就可以补完主逻辑了!

def main(stdscr):
 def init():
 #重置游戏棋盘
 game_field.reset()
 return 'Game'
 def not_game(state):
 #画出 GameOver 或者 Win 的界面
 game_field.draw(stdscr)
 #读取用户输入得到action,判断是重启游戏还是结束游戏
 action = get_user_action(stdscr)
 responses = defaultdict(lambda: state) #默认是当前状态,没有行为就会一直在当前界面循环
 responses['Restart'], responses['Exit'] = 'Init', 'Exit' #对应不同的行为转换到不同的状态
 return responses[action]
 def game():
 #画出当前棋盘状态
 game_field.draw(stdscr)
 #读取用户输入得到action
 action = get_user_action(stdscr)
 if action == 'Restart':
  return 'Init'
 if action == 'Exit':
  return 'Exit'
 if game_field.move(action): # move successful
  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=32)
 state = 'Init'
 #状态机开始循环
 while state != 'Exit':
 state = state_actions[state]()

运行

填上最后一行代码:

curses.wrapper(main)

200 行python 代码实现 2048 游戏

完整版代码地址:https://github.com/JLUNeverMore/easy_2048-in-200-lines

总结

以上所述是小编给大家介绍的200 行python 代码实现 2048 游戏,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
python 迭代器和iter()函数详解及实例
Mar 21 Python
详解Python list 与 NumPy.ndarry 切片之间的对比
Jul 24 Python
Python随机生成均匀分布在单位圆内的点代码示例
Nov 13 Python
django反向解析和正向解析的方式
Jun 05 Python
Python Matplotlib库安装与基本作图示例
Jan 09 Python
如何使用Python实现斐波那契数列
Jul 02 Python
Python操作MySQL数据库实例详解【安装、连接、增删改查等】
Jan 17 Python
Pycharm中import torch报错的快速解决方法
Mar 05 Python
Anaconda的安装及其环境变量的配置详解
Apr 22 Python
Python devel安装失败问题解决方案
Jun 09 Python
Django cookie和session的应用场景及如何使用
Apr 29 Python
Django框架模板用法详解
Jun 10 Python
一篇文章快速了解Python的GIL
Jan 12 #Python
Python获取当前公网ip并自动断开宽带连接实例代码
Jan 12 #Python
python SSH模块登录,远程机执行shell命令实例解析
Jan 12 #Python
python opencv实现任意角度的透视变换实例代码
Jan 12 #Python
Python数字图像处理之霍夫线变换实现详解
Jan 12 #Python
Python实现霍夫圆和椭圆变换代码详解
Jan 12 #Python
微信跳一跳python自动代码解读1.0
Jan 12 #Python
You might like
php 分页原理详解
2009/08/21 PHP
php+iframe实现隐藏无刷新上传文件
2012/02/10 PHP
php使用curl模拟多线程实现批处理功能示例
2019/07/25 PHP
PHP code 验证码生成类定义和简单使用示例
2020/05/27 PHP
通过百度地图获取公交线路的站点坐标的js代码
2012/05/11 Javascript
jQuery获取样式中的背景颜色属性值/颜色值
2012/12/17 Javascript
浅谈javascript面向对象程序设计
2015/01/21 Javascript
angularJS 入门基础
2015/02/09 Javascript
原生node.js案例--前后台交互
2017/02/20 Javascript
JS控件bootstrap suggest plugin使用方法详解
2017/03/25 Javascript
全面介绍vue 全家桶和项目实例
2017/12/27 Javascript
layui table设置前台过滤转义等方法
2018/08/17 Javascript
Vue.js 十五分钟入门图文教程
2018/09/12 Javascript
js中对象与对象创建方法的各种方法
2019/02/27 Javascript
使用layui前端框架弹出form表单以及提交的示例
2019/10/25 Javascript
如何管理Vue中的缓存页面
2021/02/06 Vue.js
Python threading多线程编程实例
2014/09/18 Python
Python迭代器和生成器定义与用法示例
2018/02/10 Python
使用TensorFlow实现二分类的方法示例
2019/02/05 Python
HTML5实现签到 功能
2018/10/09 HTML / CSS
Wiggle中国:英国骑行、跑步、游泳 & 铁三运动装备专卖网店
2016/08/02 全球购物
TripAdvisor德国:全球领先的旅游网站
2017/12/07 全球购物
意大利一家专营包包和配饰的网上商店:Borse Last Minute
2019/08/26 全球购物
T3官网:头发造型工具
2019/12/26 全球购物
List, Set, Map是否继承自Collection接口?
2016/05/16 面试题
C#软件工程师英语面试题
2015/06/07 面试题
毕业设计计划书
2014/01/09 职场文书
房地产融资计划书
2014/01/10 职场文书
工作自我评价怎么写
2014/01/29 职场文书
班级年度安全计划书
2014/05/01 职场文书
手术室护士节演讲稿
2014/08/27 职场文书
市场总监岗位职责
2015/02/11 职场文书
MySQL中in和exists区别详解
2021/06/03 MySQL
磁贴还没死, 微软Win11可修改注册表找回Win10开始菜单
2021/11/21 数码科技
Windows Server 2022 超融合部署(图文教程)
2022/06/25 Servers
Java多线程并发FutureTask使用详解
2022/06/28 Java/Android