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中实现对Timestamp和Datetime及UTC时间之间的转换
Apr 08 Python
Python读取网页内容的方法
Jul 30 Python
Python3实现发送QQ邮件功能(附件)
Dec 23 Python
对Python生成汉字字库文字,以及转换为文字图片的实例详解
Jan 29 Python
Python简单基础小程序的实例代码
Apr 28 Python
20行python代码实现人脸识别
May 05 Python
python的内存管理和垃圾回收机制详解
May 18 Python
Django 迁移、操作数据库的方法
Aug 02 Python
django-rest-swagger的优化使用方法
Aug 29 Python
Python 项目转化为so文件实例
Dec 23 Python
python中subplot大小的设置步骤
Jun 28 Python
彻底弄懂Python中的回调函数(callback)
Jun 25 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
全国FM电台频率大全 - 20 广西省
2020/03/11 无线电
PHP 类型转换函数intval
2009/06/20 PHP
PHP生成指定长度随机数最简洁的方法
2014/07/14 PHP
在Win7 中为php扩展配置Xcache
2014/10/08 PHP
php自动获取关键字的方法
2015/01/06 PHP
php中遍历二维数组并以表格的形式输出的方法
2017/01/03 PHP
PHP实现断点续传乱序合并文件的方法
2018/09/06 PHP
url参数中有+、空格、=、%、&amp;、#等特殊符号的问题解决
2013/05/15 Javascript
jquery实现鼠标滑过小图时显示大图的方法
2015/01/14 Javascript
javascript中DOM复选框选择用法实例
2015/05/14 Javascript
利用js编写响应式侧边栏
2016/09/17 Javascript
JS中常用的正则表达式
2016/09/29 Javascript
前端必备插件之纯原生JS的瀑布流插件Macy.js
2017/11/22 Javascript
浅谈Vue数据绑定的原理
2018/01/08 Javascript
layui实现显示数据表格、搜索和修改功能示例
2020/06/03 Javascript
nodeJs项目在阿里云的简单部署
2020/11/27 NodeJs
[53:13]2014 DOTA2国际邀请赛中国区预选赛5.21 DT VS LGD-GAMING
2014/05/22 DOTA
python ElementTree 基本读操作示例
2009/04/09 Python
使用python获取CPU和内存信息的思路与实现(linux系统)
2014/01/03 Python
Python基于scrapy采集数据时使用代理服务器的方法
2015/04/16 Python
python实现通过代理服务器访问远程url的方法
2015/04/29 Python
python数据批量写入ScrolledText的优化方法
2018/10/11 Python
解决在pycharm中显示额外的 figure 窗口问题
2019/01/15 Python
使用python进行广告点击率的预测的实现
2019/07/04 Python
Python搭建代理IP池实现检测IP的方法
2019/10/27 Python
美国战术品牌:5.11 Tactical
2019/05/01 全球购物
幼儿教师思想汇报
2014/01/10 职场文书
事业单位分类改革实施方案
2014/03/21 职场文书
祖国在我心中演讲稿200字
2014/08/28 职场文书
大学生见习报告范文
2014/11/03 职场文书
教师工作决心书
2015/02/04 职场文书
2016优秀大学生个人事迹材料范文
2016/03/01 职场文书
2016年学校爱国卫生月活动总结
2016/04/06 职场文书
开学第一周日记(三篇范文)
2019/08/23 职场文书
让文件路径提取变得更简单的Python Path库
2021/05/27 Python
Springboot/Springcloud项目集成redis进行存取的过程解析
2021/12/04 Redis