先看题
在m*n的数阵中,有很多害虫(8表示),围墙(*表示),随机生成隔壁墙(1表示),随机投放药弹(0表示),药效默认第一次可以炸毁墙继续有效,以后药效持续,但不能穿墙。
擂1:如何向左、向下、向上消灭害虫?
擂2:如何设置药弹威力值,可以穿透相应的墙?
擂3:其他奇思妙想~~~比如,如何不投在墙上?如何获得最大药效?如何提高程序效果等。
王爱胜,公众号:蓝调百香果深度趣味编程(打擂台)|6.数阵之中灭害虫
看了这个题目之后,我并没有看王老师的初始代码,因为感觉对我来说初始代码实现起来就是一个挑战,需要自己想尝试搞定。
1、问题的初步分析
看到m*n的数阵,我头脑中第一反应是二维数组,这是在学习c++时已经根深蒂固的认识。那么在Python中实现二维数组的方法是什么呢?元素是列表类型的列表。这个基础认识我是有的,但从未实践过。对列表这部分内容,我的认识是它是python区别于其它的语言的一大特点,很灵活,也很复杂,非常重要。这个数阵的实现需要的就是列表的赋值初值,然后显示。再根据投药结果,修改列表元素,再次显示。如此循环,直至游戏结束。因此,我把问题分解为两个步。第一步,创建列表,赋初值,输出初始地图。第二步,投药,修改列表,输出修改地图
2、算法设计
感觉没啥好写的,就是:
1)输入行列数,列表赋初值
2)输出列表
3)输入投药点
4)修改列表
5)输出列表
3、代码实现
边学编写列表赋初值的代码,用了我一个多小时的时间,列表组成的列表赋值有点蒙圈,下面着重解释一下。我试试看能不能不看我前几天写好的代码,自己再写出来一遍。
1)数阵初始效果:二维列表(姑且这么叫吧)赋初值
m=int(input('行数'))n=int(input('列数'))map_m=[] #定义行列表for i in range(m): map_n=[] #定义列列表,map_n将是map_m的元素 for j in range(n): map_n.append(8) map_m.append(map_n)
还好,一次写成了。现在想想看,根本没那么难。和c++二维数组赋初值的思路基本相似,就是双重循环。有所区别的是第5、7、8行。解释一下:
第5行,因为map_n是要作为列表的一个元素的,所以每次都需要从0开始,如果将第5行移至循环体外,则map_n的值会一直增加下去,说的不太清楚,你试一下就知道了。
第7、8行是列表增加新元素的命令,不能像c++那样直接赋值(也许可以,只是我不知
道)。
2、设置随机墙
设置随机墙的方法有很多,我最开始用的是下面这种方法:
import randommap_e=[1,8,8,8,8,8,8,8,8,8]m=int(input('行数'))n=int(input('列数'))map_m=[] #定义行列表for i in range(m): map_n=[] #定义列列表,map_n将是map_m的元素 for j in range(n): map_n.append(map_e[random.randint(0,9)]) map_m.append(map_n)
这里第2行是存储用来生成列表元素的列表,第9行使用了随机函数,随机取1和8,这里8的数量越多,1出现的概率就越低,墙的数量就越少。
后来我发现这种方法,不利于控制1的数量,完善游戏的玩法时会增加一些麻烦,但我觉得这种用8的数量来控制1的出现的频率,并且1放在列表第一个位置,这种方法是一种小聪明,刚刚想到时还兴奋了一小下。
第2种方法是由用户输入一个难度系数,然后随机生成1。
hard=int(input('墙的数量'))while hard>0: i=random.randint(0,m-1) j=random.randint(0,n-1) if map_m[i][j]==8: map_m[i][j]=1 hard=hard-1
这种方法要注意随机数的重复可能性,所以加了一个if,确保生成足够的1。
3、输出列表
因为列表元素是列表的话,直接输出会如下图一样输出,这不符合我们需要的效果,我们需要按元素逐一输出。
for i in range(m): for j in range(n): print(map_m[i][j],end=' ') print()
效果如下图
这里end=' '表示用空格结尾不换行,当一行输出结束后,再用print()输出一个换行符进行换行。现在的效果和王老师的图形相比少了外围的边框,属于美化方面的内容。为了使后续玩起来方便,我把*换成了行列号,稍显麻烦了一些。
print(' ',end='-')for i in range(n): if i<10: print(' '+str(i),end='-') else: print(i,end='-')print()for i in range(m): if i<10: print(' '+str(i),end='| ') else: print(str(i),end='| ') for j in range(n): print(map_m[i][j],end=' ') print('|')
至此,第一步基本实现了,但是由于第二步会经常使用输出列表,所以应该将上面的输出列表代码变为函数。
def disp_map(_map): m=len(_map) #取行数 n=len(_map[0])#取列数 print(' ',end='-') for i in range(n): if i<10: print(' '+str(i),end='-') else: print(i,end='-') print() for i in range(m): if i<10: print(' '+str(i),end='| ') else: print(str(i),end='| ') for j in range(n): print(_map[i][j],end=' ') print('|')
这里参数_map是列表类型。
我们还可以将列表的赋初值和墙的数量初值,即地图的生成定义为函数。
import randomdef draw_map(): m=int(input('行数')) n=int(input('列数')) map_m=[] #定义行列表 for i in range(m): map_n=[] #定义列列表,map_n将是map_m的元素 for j in range(n): map_n.append(8) map_m.append(map_n) hard=int(input('墙的数量')) while hard>0: i=random.randint(0,m-1) j=random.randint(0,n-1) if map_m[i][j]==8: map_m[i][j]=1 hard=hard-1 return map_mdef disp_map(_map): m=len(_map) #取行数 n=len(_map[0])#取列数 print(' ',end='-') for i in range(n): if i<10: print(' '+str(i),end='-') else: print(i,end='-') print() for i in range(m): if i<10: print(' '+str(i),end='| ') else: print(str(i),end='| ') for j in range(n): print(_map[i][j],end=' ') print('|') start=draw_map()disp_map(start)
这里第19行return map_m很重要,要将生成的列表返回,否则将什么也得到。第40行map_m赋值给start,start就是以后要使用的地图,所第41行的参数是start。
4、输入投药点
这个比较简单,给出输入变量即可,这里包括坐标(行列号)即可,默认以一行从左向右为方向。
5、修改列表
这里稍微复杂一点儿,要考虑以下几点:
1)投药点是8
2)投药点是1
3)是否会再遇到1,如果遇到是第一次还是第二次,如果是第一次在穿墙继续,否则停止。
6、显示列表
直接调用disp_map即可
x=int(input('投药行'))y=int(input('投药列'))flag=0for i in range(y,len(start[0])): if start[x][i]==1 and flag==1: break elif start[x][i]==1: flag=1 start[x][i]=0 else: start[x][i]=0disp_map(start)
至此,最基本的功能就全实现了,至于那几个“擂”还没开始打。饭要一口一口的吃,骨头要一下一下的啃。
行走路上
信息技术教学的路很长, 行走路上与您一路同行! ● 感谢访问,欢迎交流 |