命令行2048
2015-07-27 10:28
615 查看
命令行2048
By 白熊花田(http://blog.csdn.net/whiterbear) 转载需注明出处,谢谢。
写了一个基于命令行的2048游戏,python写的,难度不是很大,关键是要理清游戏的逻辑。这篇博客将介绍下游戏实现的逻辑和简单的实现,以及美化游戏界面的细节。先贴一张游戏运行的截图。
1 逻辑
介绍该游戏的逻辑。1.1 游戏的逻辑
玩过2048的童鞋应该知道,该游戏是由4*4的方格组成,启动时方格里会随机出现两个数字,一般为2,偶尔也会出现4. 游戏在达到2048时显示胜利,当然也可以继续玩下去,在方格填满并且四个方向均无法移动时游戏失败。1.2 移动的逻辑
可以通过上下左右进行方格数字的移动,以向左为例,当玩家选择向左时,每一行从最左边的数字开始,先(1)向左移动,如果该数字左边有0格,移动后位置数字的值从0变成该数字的值,一直到该行所有的数字均尝试了移动为止;(2)合并,如果该数字与其右边的数字等值且不等于0时,它自身值变为以前的2倍,其右边的值变为0,继续合并,直到该行的所有数字均尝试了该合并操作;(3)向左移动,规则同(1)。以向左为例,可能会出现的情况有: [0, 0, 0, 2] -> [2, 0, 0, 0] [2, 2, 2, 0] -> [4, 2, 0, 0] [2, 2, 2, 2] -> [4, 4, 0, 0] [4, 2, 0, 2] -> [4, 4, 0, 0]
1.3 分数的逻辑
每次移动有合并时,游戏总分加上合并后该数字的数值。如某行从[2, 2, 2, 2] -> [4, 4, 0, 0],出现了两次合并,则总分应该加上4+4分。
2 实现
介绍该游戏的编程实现细节。2.1 方格的表示和显示
使用二维数组来表示游戏方格。_SIZE = 4 cells = [[0]*_SIZE for _ in xrange(_SIZE)]
使用format来显示游戏方格。
for i in xrange(_SIZE): print('{0:4} {1:4} {2:4} {3:4}'.format(cells[i][0], cells[i][1], cells[i][2], cells[i][3]))
2.2 初始化游戏方格
初始化游戏方格时,需要随机选择两个方格放置随机的数字(2或4)。利用random模块,从[2]*9 + [4]中随机选择一个数值。在所有空的方格中随机选择一个方格,放入选择的数值,重复两次。
2.3 获得玩家输入
以asdw键分别代表左,下,左,上操作。使用raw_input获得玩家输入即可。2.4 移动方格
获得到玩家输入的方向时,即可利用该方向移动方格。按照1.2中给定的移动的逻辑,在移动之前需要进行方格的位置移动,即在不进行合并操作的情况下进行数字移动,这样能够便于之后的合并操作。以向左为例:for line in xrange(_SIZE): # 从该行最左边开始 for row in xrange(0, _SIZE - 1, 1): # 如果该方格为0值 if not cells[line][row]: # 将其右边的方格值传递给它,即进行移动 cells[line][row] = cells[line][row + 1] cells[line][row + 1] = 0
当然会出现一次操作不能使方格移动完整,即会出现[0, 0, 0, 2]->[0, 0, 2, 0]的情况,所以需要多移动几次才会变成[2, 0, 0, 0]。
在移动方格后,开始合并方格。以向左为例:
for line in xrange(_SIZE): # 从该行最左边开始 for row in xrange(0, _SIZE - 1, 1): # 如果该方格和其右边方格值相同,且两者都不为0时进行合并 if cells[line][row] == cells[line][row + 1] != 0: # 合并,即它自身值变成之前的2倍,而它右边的方格值变成0 cells[line][row], cells[line][row + 1] = 2*cells[line][row], 0 # 分数加上合并后的方格值 score += cells[line][row]
然而,这种移动的方式会出现[2, 2, 2, 2]->[4, 0, 4, 0]的情况,而我们需要的是变成[4, 4, 0, 0]。所以,在这样移动后,我们仍需要调用上面的位置移动代码,进行方格值的移动达到我们要的结果。
2.5 运行游戏
有了上面的基础,该游戏就可以简单的运行起来了。# 循环处理 while True: # 显示游戏方格和分数 display() # 接受玩家输入,并进行移动方格操作 operation() # 选择空格区域添加一个随机分值 addCell()
然而,这样运行会出现游戏方格在玩家输入某方向上已经不能移动时,游戏仍然会选择空格区域添加随机分值,这样,我们需要判断玩家输入某方向后,该方向是否移动过,如果移动了,那么才可以添加分值。改进如下:
while True: display() # 必须使用深复制,否则两者变化会一致 old_cells = copy.deepcopy(cells) operation() # 如果方格有移动,则选择一个方格添加一个值 if old_cells != cells: addCell()
使用了深复制,复制了一份移动之前的游戏方格状态,将该状态与玩家输入移动后的状态进行比较,如果两者不一致即说明移动成功了,可以添加方格。
游戏截图:
3 美化
介绍该游戏界面的美化。3.1 添加颜色
前面的游戏运行所有分数都是以默认的黑色进行显示的,我们尝试着让不同的分值显示不同的颜色。在github上找到了一位大神的2048代码,他使用了colorama模块,然后他代码里的其他特性也被我用上了。利用了他的代码,我也将我的游戏添上了色彩。colorama是一个能够让python在命令行中输出彩色字体的模块。安装很简单:pip install colorama。下面给出一个简单的使用范例。
from colorama import init, Fore, Style # autoreset能够让变色效果只对当前输出有效果 init(autoreset=True) # Fore.RED可以让后面的输出变为红色,Style.RESET_ALL能够清除变色效果 print(Fore.RED + 'color font' + Style.RESET_ALL + 'no color') # 该段代码输出红色的‘color font’和默认黑色的‘no color’
使用colorama到游戏中。首先定义一个颜色字典,让每个数值都对应一个颜色。如:
COLORS = { 2: Fore.GREEN, 4: Fore.BLUE + Style.BRIGHT, 8: Fore.CYAN, ...}
利用该字典,我们在每次显示游戏方格时,对每个数字进行变色处理。
# 获取给定的一个游戏方格值 cell = getCell(x, y) # 如果值为0,则表示为一个点 . if cell == 0: return ' .' # 如果是数字,那么默认每个数字占3个空格长度 # 在字典中找到该数值对应的颜色,修饰后恢复默认颜色(这样不会影响下一个数字输出的颜色) else: str_cell = '%3d' % cell return str(COLORS.get(cell, Fore.GREEN) + str_cell + Style.RESET_ALL)
这样游戏的运行的效果如:
3.2 支持VIM键和箭头操作
作为程序员,还是有必要支持一下vim键的,另外,如果能够支持箭头,那么操作起来也会更方便(我好像暴露了)。利用termios模块(只在Unix下运行,Windows下请使用msvcrt),可以方便的获得键盘输入。学习一下termios模块,获取字母的大小写输入并不是难事,但是箭头的获取稍微麻烦点。你需要按照下面的方式来获取箭头输入:k = __getKey() if k == 27: k = __getKey() if k == 91: k = __getKey()
其中__getKey()函数能够获取键盘输入,并返回输入的ascii码值。普通的键,比如‘A’,输入后得到65,而输入箭头时得到的k值等于27,接着再调用该方法,得到91,再调用一次,得到104或106或107或108,这四个值分别是输入左,下,上,右箭头。
3.3 支持刷新
通过前面的游戏界面,我们能看到每次玩家输入,游戏界面总是会在命令行重新绘制,增加了行数,降低了游戏的娱乐程度。所以我们使用清屏操作,每次输入后,清屏,然后在同样的位置绘制游戏界面,这样给玩家的感觉游戏时连贯的。另外,消除掉命令行的光标也有助于增加游戏体验。清屏操作:
os.system('clear')
隐藏光标操作:
sys.stdout.write('\033[?25l')
显示光标操作:
sys.stdout.write('\033[?25h')
修改运行代码:
# 首先注册显示光标函数,这样游戏运行完成退出后能够运行该函数显示光标 atexit.register(showCursor) # 隐藏光标 hideCursor() while True: # 每次循环先清屏,然后重新绘制游戏界面 clearScreen() display() old_cells = copy.deepcopy(cells) operation() if old_cells != cells: addCell()
最后再贴一张运行截图:
代码链接:
githubcsdn
相关文章推荐
- Python动态类型的学习---引用的理解
- Python3写爬虫(四)多线程实现数据爬取
- 垃圾邮件过滤器 python简单实现
- 下载并遍历 names.txt 文件,输出长度最长的回文人名。
- install and upgrade scrapy
- Scrapy的架构介绍
- Centos6 编译安装Python
- 使用Python生成Excel格式的图片
- 让Python文件也可以当bat文件运行
- [Python]推算数独
- [Android]在代码里运行另一个程序的方法
- Python中zip()函数用法举例
- Python中map()函数浅析
- 我是运营,我没有假期
- Python在CAM软件Genesis2000中的应用
- 使用Shiboken为C++和Qt库创建Python绑定
- FREEBASIC 编译可被python调用的dll函数示例