贪吃蛇“大作战”(三)
例程实操与分析
在之前的两篇博客中分别通过面向过程和面向对象的编程思想分析介绍了来自sunny开始学坏的贪吃蛇例程,今天的博客将通过运行这个贪吃蛇例程来了解各行代码的作用,同时通过“找茬”的方式深入分析了解贪吃蛇的运行机制并改善这个例程。
下面是这个贪吃蛇例程的代码:
1 import os,random
2 sw=[[5,5]]
3 #lc=[[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6]]
4 lc=[[5,i] for i in range(10)]
5 s=''
6 for x in range(500):
7 w=len(lc)-1
8 li = [(['○'] * 20) for i in range(20)]
9 a=input('请输入wasd控制:')
10 if a=='':
11 a=s
12 if a=='w':
13 lc.append([lc[w][0]-1,lc[w][1]])
14 del lc[0]
15 s='w'
16 if a=='s':
17 lc.append([lc[w][0]+1,lc[w][1]])
18 del lc[0]
19 s='s'
20 if a=='a':
21 lc.append([lc[w][0],lc[w][1]-1])
22 del lc[0]
23 s='a'
24 if a=='d':
25 lc.append([lc[w][0],lc[w][1]+1])
26 del lc[0]
27 s='d'
28 if lc[w] in sw:
29 lc.insert(0,[lc[0][0],lc[0][1]-1])
30 del sw[0]
31 sw.append([random.randint(0,19),random.randint(0,19)])
32 for i in lc:li[i[0]][i[1]]='●'
33 for w in sw:li[w[0]][w[1]]='◆'
34 os.system('clear')
35 for i in li:print(''.join(i))
然后,我们来运行这个例程代码,看看运行结果:
根据这个结果可知:例程代码在运行到第九行后停止运行,并提示我们输入“w”"s""a""d"四个字符中的一个,接下来的条件选择结构会判断我们输入的内容执行不同的语句;我们可以不输入内容执行或输入这四个字符以外的内容执行试试:
从结果可以看到,输入"w""s""a""d"四个字符之外的字符也可以开启游戏,这对一个正常的游戏逻辑来说显然是个漏洞。回到代码看看,if语句与最后的四行代码是在同一个层级的,也就是if执行完后肯定会执行最后四行语句(最后四行语句的作用就是在命令行中渲染出蛇、食物和草地三个对象),这就造成了无论输入什么指令都会“开始游戏”(渲染出地图);我们可以通过修改行号为10和11的代码来解决这个漏洞(bug)。修改后的代码如下(增加了一个else语句):
import os,random
sw=[[5,5]]
#lc=[[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6]]
lc=[[5,i] for i in range(10)]
s=''
for x in range(500):
w=len(lc)-1
li = [(['○'] * 20) for i in range(20)]
a=input('请输入wasd控制:')
# if a=='':
# a=s
if a=='w':
lc.append([lc[w][0]-1,lc[w][1]])
del lc[0]
s='w'
elif a=='s':
lc.append([lc[w][0]+1,lc[w][1]])
del lc[0]
s='s'
elif a=='a':
lc.append([lc[w][0],lc[w][1]-1])
del lc[0]
s='a'
elif a=='d':
lc.append([lc[w][0],lc[w][1]+1])
del lc[0]
s='d'
else:
print("输入指令错误!")
exit()
if lc[w] in sw:
lc.insert(0,[lc[0][0],lc[0][1]-1])
del sw[0]
sw.append([random.randint(0,19),random.randint(0,19)])
for i in lc:li[i[0]][i[1]]='●'
for w in sw:li[w[0]][w[1]]='◆'
os.system('clear')
for i in li:print(''.join(i))
这样处理后,就可以控制玩家输入的指令,消除错误指令带来的Bug,,引导玩家正确地享受游戏。
通过上面的游戏效果可以看出,在游戏的初始,食物“◆”就在贪吃蛇的腹内了,这样显然不符合正常逻辑,我们要避免这种Bug的出现,即禁止食物出现在贪吃蛇的身体中。在之后食物的随机生成时,同样禁止这种情况出现。可以通过差集的方式简单解决这个Bug。
import os,random
sw=[[5,5]]
#lc=[[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6]]
lc=[[5,i] for i in range(10)]
s=''
grass = [[x,y] for x in range(20) for y in range(20)] #将地图坐标化,以坐标点组合的形式表示地图
field_sw = [var for var in grass if var not in lc] #以差集的方式得到食物随机出现的范围(滤除贪吃蛇的坐标集)
if sw[0] in lc: #if语句检测食物是否在贪吃蛇内部
del sw[0] #删除原有坐标
sw.append(random.choice(field_sw)) #在贪吃蛇身体之外随机生成一个食物出现坐标
print("Have Changed!") #测试用
for x in range(500):
w=len(lc)-1
li = [(['○'] * 20) for i in range(20)]
a=input('请输入wasd控制:')
# if a=='':
# a=s
if a=='w':
lc.append([lc[w][0]-1,lc[w][1]])
del lc[0]
s='w'
elif a=='s':
lc.append([lc[w][0]+1,lc[w][1]])
del lc[0]
s='s'
elif a=='a':
lc.append([lc[w][0],lc[w][1]-1])
del lc[0]
s='a'
elif a=='d':
lc.append([lc[w][0],lc[w][1]+1])
del lc[0]
s='d'
else:
print("输入指令错误!")
exit()
if lc[w] in sw:
lc.insert(0,[lc[0][0],lc[0][1]-1])
field_sw = [var for var in grass if var not in lc] #以差集滤除贪吃蛇的坐标集
del sw[0]
sw.append(random.choice(field_sw)) #在不包含贪吃蛇的坐标集中随机生成食物坐标
for i in lc:li[i[0]][i[1]]='●'
for w in sw:li[w[0]][w[1]]='◆'
os.system('clear')
for i in li:print(''.join(i))
再看看运行结果:
这样看着是不是舒服多了,游戏也变得合理了。
好了,一个通过命令行简单实现的贪吃蛇游戏就是这样。上面初始化的贪吃蛇太长了,我们修改下参数再运行看看,顺便让食物离贪吃蛇近一点。
import os,random
sw=[[5,7]]
#lc=[[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6]]
lc=[[5,i] for i in range(5)]
s=''
看看效果
是不是已经很有贪吃蛇的感觉了,只是用命令行来实现这个游戏终究太过“机械”,每次贪吃蛇的前进都需要我们敲入指令然后整个地图重新“渲染”一次,这样感觉会很不“友好”。之后的博客会通过面向对象的方式来同样实现一个贪吃蛇游戏,并调用另一些相关的模块,让游戏界面更美观,人机交互方式更“友好”