[python] python实现2048游戏,及代码解析。
我初学python,有不对之处望大家指教。转载请征得同意。
我在网络上也找了一些2048游戏代码的讲解,但都不是特别详细。所以我希望能够尽量详细的讲解。同时,有的地方我也不懂,希望大家能帮助补充。我会随时更新以方便后来者。
当然,需要一定的python基础再看此实例。
- #-*- coding:utf-8 -*-
- import curses
- from random import randrange, choice
- from collections import defaultdict
- # 引入3个扩展包
- letter_codes = [ord(ch) for ch in 'WASDRQwasdrq']
- actions = ['Up', 'Left', 'Down', 'Right', 'Restart', 'Exit']
- actions_dict = dict(zip(letter_codes, actions * 2))
- # 创建我们将要用的键盘输入字典,这个字典将在后边通过第18行的
- # keyboard.getch()
- # 而这个方法被封装成一个函数,调用函数以实现该方法。
- 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]
- # 矩阵逆转,同上
- 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()
- # 定义类的__init__方法,为初始化方法
- 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()
- # 重置方法,虽然命名为reset,但是初始化也同样使用该方法。
- # 如果你觉得命名为set更合适,请改为set。
- # 这个方法中使用了spawn()函数,这个函数放在了后边,
- # spawn()函数的功能是生成新的数字,reset()需要生成两次。
- def move(self, direction):
- # 最重要的3个函数之一
- 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)))
- #这里可以有不同的写法,就是tighten一次,merge一次。在merge的时候没必要加0了。
- # 欢迎大家把好的方法发给我,谢谢。http://www.cnblogs.com/danjawwi/
- # 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
- # else:
- # new_row.append(row[i])
- # new_row += [0 for j in range(len(row) - len(new_row))]
- # return new_row
- #
- # return 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)))
- # 这里把row的迭代放在了方法外边,在对应字典值这里实现了。也可以放在方法里边
- 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 draw(self, screen):
- # 最重要的3个函数之一
- 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:]
- #不明白为什么这里要这样写
- #直接line = '+------' * self.width + '+' 不行吗?
- #http://www.cnblogs.com/danjawwi/
- 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
- #这里我也不明白,直接根据self.height输出不就行了?
- def draw_row(row):
- cast(''.join('|{: ^5} '.format(num) if num > 0 else '| ' for num in row) + '|')
- #用到了join 和 format这两种方法。
- 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 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_is_possible(self, direction):
- # 最重要的3个函数之一
- def row_is_left_movable(row):
- def change(i):
- if row[i] == 0 and row[i + 1] != 0:
- # 这里是不是在说None != 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 main(stdscr):
- def init():
- game_field.reset()
- return 'Game'
- def not_game(state):
- game_field.draw(stdscr)
- 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 = 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'
- 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)
[python] python实现2048游戏,及代码解析。的更多相关文章
- Python中sort和sorted函数代码解析
Python中sort和sorted函数代码解析 本文研究的主要是Python中sort和sorted函数的相关内容,具体如下. 一.sort函数 sort函数是序列的内部函数 函数原型: L.sor ...
- 「Python实用秘技08」一行代码解析地址信息
本文完整示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/PythonPracticalSkills 这是我的系列文章「Python实用秘技」的第8期 ...
- 用Python做2048游戏 网易云课堂配套实验课。通过GUI来体验编程的乐趣。
第1节 认识wxpython 第2节 画几个形状 第3节 再做个计算器 第4节 最后实现个2048游戏 实验1-认识wxpython 一.实验说明 1. 环境登录 无需密码自动登录,系统用户名shiy ...
- 通过游戏学python 3.6 第一季 第九章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账号--锁定次数--菜单功能'menufile
通过游戏学python 3.6 第一季 第九章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁 ...
- 通过游戏学python 3.6 第一季 第八章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账号--锁定次数
通过游戏学python 3.6 第一季 第八章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账 ...
- 通过游戏学python 3.6 第一季 第三章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码 可复制直接使用 娱乐 可封装 函数
猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码 #猜数字--核心代码--猜测次数--随机函数和屏蔽错误代码 import random secrst = random.rand ...
- 通过游戏学python 3.6 第一季 第二章 实例项目 猜数字游戏--核心代码--猜测次数 可复制直接使用 娱乐 可封装 函数
猜数字游戏--核心代码--猜测次数 #猜数字--核心代码--猜测次数 number=33 amount=3 count=0 while count<=amount: conversion ...
- python 2048游戏控制器
2048游戏控制器 1 evaluate 要用程序来处理就得对现实的问题进行量化,用数字来表示.在2048游戏中,我们的输入是一个棋局,让我们输出一个移动方向,这样我们需要对棋局进行量化,即我们要评估 ...
- Python之字符串小代码解析
本篇只是拿一段代码来对python中的字符串的一些使用做解释,来让大家更加了解python Python 3.4.0 (v3.4.0:04f714765c13, Mar 16 2014, 19:25: ...
随机推荐
- linux内核调试技巧之addr2line
addr2line工具是一个可以将指令的地址和可执行影像转换为文件名,函数名和源代码行数的工具.这在内核执行过程中出现崩溃时,可用于快速定位出出错的位置,进而找出代码的bug. 用法 addr2lin ...
- NGUI的localPosition和Position之间的关系
假设有子节点为child, 父节点为parent, 且都是Transform类型. 则: child.localPosition = (child.position - parent.position ...
- JNI支持C++与C的区别
1. C++的代码后缀是".cpp" 2. 在JNI.h 文件中有两套代码.一套是支持c的, 一套是支持JNI的. JNI.h地址:mac ▸ 用户 ▸ douj ▸ Docume ...
- su with hyphen and without - su带横杠和不带横杠
The difference between "-" and "no hyphen" is that the latter keeps your existin ...
- ie8用ajax访问不能每次都刷新的问题
最近发现,用ajax访问后台,用ie8访问,第一次可以正常返回值,后面就一直不会执行后台,总是返回第一次访问的结果. 用ie9,ie10等都没问题,chrome,等浏览器也没有问题. 测试后发现,是i ...
- 查看SQLServer最耗资源时间的SQL语句
执行最慢的SQL语句 SELECT (total_elapsed_time / execution_count)/1000 N'平均时间ms' ,total_elapsed_time/1000 N'总 ...
- Chrome 没有注册类
妈蛋这个问题折腾了好久,百度都是噼里啪啦什么什么进入注册表什么的删除啊 操 看着难受死了 这边也有这个些 没有注册类原因什么的的百度一下就有了 解决办法 管理员身份运行CMD 输入 REG DELE ...
- FIS
学习官网 http://fis.baidu.com/docs/beginning/getting-started.html 1. fis release: 编译并发布 fis releas ...
- 安装spf13-vim on Windows10
安装之前 1.系统为 windows10 Version 1607 64位 企业版 2.参考 http://www.codeweblog.com/gvim-64%E4%BD%8D-windows-7 ...
- 查看python api
以下方法可以查看python 的api,包括selenium webdriver,requests等 1.cmd进入dos命令行窗口,输入python -m pydoc -p 2345 (2345 ...