下面是我们要达到的效果,在命令行里面玩2048,是不是很酷?
写出这个小程序其实用的知识很少,主要是用了嵌套列表,循环,if 判断,还有一点点 random 和copy 模块。作为练习是个很好的项目。
初步分析游戏的流程
首先来分析一下这个游戏。
1 . 游戏界面为一个 4 X 4 一共 16 的方格,开始时在随机的两个格子里面生成两个数字 2 。
2 . 可以上下左右滑动界面,数字会向着滑动的方向移动,途中若遇到相同的数字,则合并成一个数,值为二者的和,直到移动不了为止。同时在每次移动的时候,会在空白的地方再次生成两个数字 2 。
3 . 若没有空格子且相邻没有相同的数字,那么游戏结束。
游戏第一步
第一步 当然是先写一个游戏界面,为了方便后面的操作和计算,需要先来装这些数据,因为要涉及到计算和修改,那么 python 内置的只有列表和字典可以考虑,但是字典是无序的,在定位上可能会问题,所以用列表比较合适。可以用一个嵌套列表来表示位置和值,写这个界面的方法很像写九九乘法表。
import random
# 开始界面
def start():
list_2048 = [[' ', ' ', ' ', ' '], [' ', ' ', ' ', ' '], [' ', ' ', ' ', ' '], [' ', ' ', ' ', ' ']]
i = 0
while i < 2:
x = random.randint(0,3)
y = random.randint(0,3)
if list_2048[y][x] == ' ':
list_2048[y][x] = '%4d'%2
i += 1
return list_2048
我开始生成一个空的嵌套列表,里面存放的全是 4 个空格组成的字符串,是因为我玩的时候尝试过,很难玩超过四位数(实际上我只玩到过三位数....),是拿来占位用,以免在游戏玩的过程中,界面出现错位。所以我在后面往里面添加元素的时候,添加的也是长度为 4 的字符串,而不是单纯的数字 2 。
实际上,我们写的游戏的界面就已经完成了,后面全是在此列表中的转换。列表的框架就这个样子了,放了后面方便调用,写一个关于打印的函数。
# 将列表打印出游戏界面的效果
def print_number(li):
for row in li:
for number in row:
print(number,end='|')
print()
就是如下效果:
游戏过程
这个游戏操作还是很简单的,只能上下左右四个方向移动,故在玩的过程中只需要把这四个过程都写一遍,后面只是一直循环调用这个四个方法而已。
先来分析向下移动,游戏的效果是,所有的数字向下移动,如果碰到相同的数字,那么合并成一个数字。
初看比较复杂,又要移动,又要合并,而且因为数字在的位置不同,移动的单位也不相同,同时每次移动还要随机在空白区域产生两个数字。
简化过程:
步骤 1 :只移动,不合并。直到数字中间没有空隙(垂直方向),移动过程就结束了。因为中间没有空隙,那说明所有的数字都移动到位了,不能再继续移动了。
步骤 2 :只合并不移动。用循环在判断的方式来验证相应方向上有没有可以合并的。
步骤 3 :综合两个过程,先移动,然后判断合并,因为在合并的过程中,可能会产生空隙,故还需要调用一次移动,最后在两个空白区域在产生两个数字,当然到游戏的中后段,可能只会有一个空格或者没有空格,也要做相应的判断。在我写这篇文章的时候,才发现,我在这一步的判断中的逻辑很有问题,虽然最后呈现的效果一样,但是增加了很多不必要的计算量。(由于现在深夜了,我也懒得改了,给大家留个小作业,读者自己改改吧。)
# down_step1 是将游戏界面里面所有的元素堆在下面,使中间没有空隙
def down_step1(old_list):
while True:
for x in range(3):
for y in range(4):
if old_list[x][y] != ' ' and old_list[x+1][y] == ' ':
old_list[x][y],old_list[x+1][y] = old_list[x+1][y],old_list[x][y]
count = 0
for y in range(4):
judge_list = []
for x in range(4):
judge_list.append(str(old_list[x][y]))
judge_str = ''.join(judge_list)
judge_str = judge_str.strip()
if ' ' in judge_str:
break
else:
count += 1
if count == 4:
break
return old_list
# down_step2 是将上下相邻且相同的数字合并,因为是向下移动,故判断的时候是从下像上判断,这点很重要。
def down_step2(old_list):
global score
for x in range(4): # 将前面处理好,挨着紧凑的数据来判断最后两项是否相同,若相同就将两项合并
if old_list[3][x] != ' ' and old_list[3][x] == old_list[2][x]:
old_list[3][x] = '%4d'%(int(old_list[3][x]) * 2)
old_list[2][x] = ' '
score += int(old_list[3][x])
if old_list[1][x] != ' ' and old_list[1][x] == old_list[0][x]:
old_list[1][x] = '%4d'%(int(old_list[1][x]) * 2)
old_list[0][x] = ' '
score += int(old_list[1][x])
elif old_list[2][x] != ' ' and old_list[2][x] == old_list[1][x]:
old_list[2][x] = '%4d'%(int(old_list[2][x]) * 2)
old_list[1][x] = ' '
score += int(old_list[2][x])
elif old_list[1][x] != ' ' and old_list[1][x] == old_list[0][x]:
old_list[1][x] = '%4d'%(int(old_list[1][x]) * 2)
old_list[0][x] = ' '
score += int(old_list[1][x])
return old_list
'''
down 是中和两个步骤,然后在把处理完的元素堆在下面。
这里没有用循环处理,是因为要让玩家每操作一次,也只变动一次,
操作一次,而变动多次会使玩家捉摸不到游戏的规律性
'''
def down(old_list):
judge_list = copy.deepcopy(old_list)
old_list = down_step1(old_list)
old_list = down_step2(old_list)
old_list = down_step1(old_list)
if judge_list == old_list:
return old_list
if old_list[0].count(' ') + old_list[1].count(' ') + old_list[2].count(' ') + old_list[3].count(' ') >= 2:
k = 0
while k < 2:
x = random.randint(0,3)
y = random.randint(0,3)
if old_list[y][x] == ' ':
old_list[y][x] = '%4d'%2
k += 1
elif old_list[0].count(' ') + old_list[1].count(' ') + old_list[2].count(' ') + old_list[3].count(' ') == 1:
k = 0
while k < 1:
x = random.randint(0, 3)
y = random.randint(0, 3)
if old_list[y][x] == ' ':
old_list[y][x] = '%4d'%2
k += 1
else:
pass
return old_list
相同的逻辑,其他几个方向都可以写出来了,其中 score 是游戏的分数,这段代码是我从程序里面复制出来的,就不改了。在这个片段中没有实际作用。
将四个方向写完后,那就是重复的调用了,因为是在命令行里面玩,就需要玩家通过输入命令来操作游戏。
我设定的是通过 wsad 四个键来控制 上下左右四个方向。
最后就是判断游戏结束的条件,此游戏的结束条件是,所有的空格都被数字填满,而且每个数字相邻的位置的数都不相同。
为了增加游戏的趣味性,也做了一个记分规则,每次移动合并后的数字之和为本次移动所获得的分数。比如:2和2合并为4,那么分数为4。最后把所有的分数累加起来就是最后的玩家的得分。
# 启动游戏
old_list = start()
print_number(old_list)
while True:
# 根据用户输入做出相应的移动
print()
user_input = input('请输入你要移动的方向[上(w)下(s)左(a)右(d)]:')
print()
if user_input == 'w':
old_list = up(old_list)
print_number(old_list)
print()
print('目前得分:%d 分'%score)
elif user_input == 's':
old_list = down(old_list)
print_number(old_list)
print()
print('目前得分:%d 分' % score)
elif user_input == 'a':
old_list = left(old_list)
print_number(old_list)
print()
print('目前得分:%d 分' % score)
elif user_input == 'd':
old_list = right(old_list)
print_number(old_list)
print()
print('目前得分:%d 分' % score)
else:
print()
print('输入错误!请输入正确的方向!')
print_number(old_list)
# 游戏的终止条件
# 判断界面是否还有空位
if old_list[0].count(' ') + old_list[1].count(' ') + old_list[2].count(' ') + old_list[3].count(' ') == 0:
# 判断每一个数相邻是否还有相同的数字
count = 0
for i in range(4): # 判断横排
for j in range(3):
if old_list[i][j] == old_list[i][j+1]:
count += 1
for x in range(4):
for y in range(3):
if old_list[y][x] == old_list[y+1][x]:
count += 1
if count == 0:
print('很遗憾,游戏结束!')
print('您的最终得分:%d 分' % score)
break
至此结束。如果有什么更好的做法,欢迎交流,互相学习。
若要代码的小伙伴,可以通过下面地址去拿。
代码 : https://gitee.com/MiltonL/codes/zhuf495dgb7loeavstyc068