马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
问题来源于python课后作业的 第037讲:类和对象:面向对象
还是再上一下图(文章最下方):
题中给的指示是:“当乌龟和鱼坐标重叠,乌龟吃掉鱼,乌龟体力增加20”
这样我们就可以理解为,不论任何时候,只要乌龟和鱼碰面,就会发生鱼被吃掉这种情况。
大家做过这个题的相信已经看过答案了,我就不再把全部的代码放上,只放部分答案了:turtle = Turtle()
fish = []
for i in range(10):
new_fish = Fish()
fish.append(new_fish)
while True:
if not len(fish):
print("鱼儿都吃完了,游戏结束!")
break
if not turtle.power:
print("乌龟体力耗尽,挂掉了!")
break
pos = turtle.move()
# 在迭代器中删除列表元素是非常危险的,经常会出现意想不到的问题,因为迭代器是直接引用列表的数据进行引用
# 这里我们把列表拷贝给迭代器,然后对原列表进行删除操作就不会有问题了^_^
for each_fish in fish[:]:
if each_fish.move() == pos:
# 鱼儿被吃掉了
turtle.eat()
fish.remove(each_fish)
print("有一条鱼儿被吃掉了...")
首先,我要说的是,“# 在迭代器中删除列表元素是非常危险的,经常会出现意想不到的问题,因为迭代器是直接引用列表的数据进行引用,# 这里我们把列表拷贝给迭代器,然后对原列表进行删除操作就不会有问题了^_^”,这两句话是宝藏,还有这个处理的方法:for each_fish in fish[:]:,一定要拿小本本记下来。
回到正题,我想说这段标准答案的代码实际上是有个漏洞在里面:
我们先跟着代码的顺序理一下思路,生成实例对象turtle之后,用for循环生成10个Fish类的实例对象,存放在列表fish中。while循环中先判断了跳出条件,紧接着“ pos = turtle.move()”,小乌龟迈出了它的第一步,再进入for循环中,小鱼儿们也开始move,判断如果坐标重合,就被吃掉,这看起来没有什么问题。但是我突然就想到一个问题,万一小鱼儿落地成盒怎么办??小鱼儿和乌龟的生成位置都是随机的,十条小鱼儿一出生就和刚刚出生的小乌龟碰上也是有可能的!
想到这里,我们再来看这段代码,实际上已经忽略了这个问题。小鱼儿落地金身,没有被直接吃掉,而是走了一步之后再被看看是不是还这么倒霉。所以,我认为严谨地来讲,确实应该考虑到小鱼儿“落地成盒”的问题(毕竟是吃鸡常有的事=_=)
我的答案是在看标准答案之前写的,所以考虑进了这一点,也顺利的解决了,代码奉上:import random as r
legal_x = [0, 10]
legal_y = [0, 10]
class Turtle():
def __init__(self):
self.hp = 100 #乌龟的生命值
self.x = r.randint(legal_x[0], legal_x[1]) #随机初始位置
self.y = r.randint(legal_x[0], legal_x[1])
def move(self):
self.hp -= 1 #乌龟移动消耗体力
up_down = r.randint(-1, 1)
left_right = r.randint(-1, 1)
step = r.randint(1,2)
self.x += left_right * step #上下左右移动
self.y += up_down * step
if self.x < legal_x[0]: #判断是否越界
self.x = -self.x
if self.x > legal_x[1]:
self.x = 2*legal_x[1] - self.x
if self.y < legal_y[0]:
self.y = -self.y
if self.y > legal_y[1]:
self.y = 2*legal_y[1] - self.y
## print('我是小乌龟,我现在的位置是',(self.x, self.y))
## print('='*80)
return (self.x, self.y)
def eat(self): #乌龟进食
self.hp += 20
if self.hp >= 100:
self.hp = 100
print('❤'*30)
print('hahaha,我吃到鱼了,我现在的体力是【%d】:'%self.hp)
return self.hp
class Fish():
def __init__(self):
self.x = r.randint(legal_x[0], legal_x[1]) #随机初始位置
self.y = r.randint(legal_x[0], legal_x[1])
def move(self):
up_down = r.randint(-1, 1)
left_right = r.randint(-1, 1)
self.x += left_right #上下左右移动
self.y += up_down
if self.x < legal_x[0]: #判断是否越界
self.x = -self.x
if self.x > legal_x[1]:
self.x = 2*legal_x[1] - self.x
if self.y < legal_y[0]:
self.y = -self.y
if self.y > legal_y[1]:
self.y = 2*legal_y[1] - self.y
## print('我是小鱼儿,我现在的位置是',(self.x, self.y))
## print('='*80)
return (self.x, self.y)
turtle = Turtle() #创造鱼和乌龟
fishes = []
for i in range(10):
fish = Fish()
fishes.append(fish)
while True:
turtle_site = (turtle.x, turtle.y) #乌龟的位置
fishes_site = [] #鱼群的位置
for each in fishes: #更新鱼群位置
fishes_site.append((each.x, each.y))
times = fishes_site.count(turtle_site) #搜集一次吃掉几条鱼
if times > 0:
for i in range(times):
kill = fishes_site.index(turtle_site)
fishes_site.pop(kill) #鱼被吃掉后,更新对象列表和鱼群位置
fishes.pop(kill)
turtle.hp = turtle.eat()
print('现在还有【%d】条小鱼儿,我要把它们都吃光!'%len(fishes))
turtle.move()
for each in fishes:
each.move()
if turtle.hp <= 0:
print('-'*80)
print('小乌龟被累死了!')
print('游戏结束!')
break
if len(fishes) == 0:
print('-'*80)
print('小乌龟把鱼都吃光了!')
print('游戏结束!')
break
在我的代码中,我用了两个列表,一个(fishes)存放Fish类的实例对象,另一个(fishes_site)存放每一个实例对象的x,y坐标组成的元组,当然他们的索引值也是一一对应,便于操作。我考虑到,万一小乌龟中大奖了,十条小鱼同时落地成盒,那岂不是美哉!所以先用了count()方法,判断一次能吃到几条鱼,然后以这个次数(times),当做for循环的条件。我的判断是否能吃掉鱼放在了小乌龟和小鱼儿move之前,所以如果落地成盒发生,则第一时间就能发现并且执行。但这样做同样我也用了比较笨的方法,用index()方法找到被吃的那条小鱼,然后用这个下标在两个列表中都把它删除。如果大佬们能更简单地解决,请在下面留言,我将非常欢迎!
最后,我想问问大家,为什么我用自己的代码和答案跑了好多次,一直都是小乌龟被累死,这也太惨了,不知道你们是不是和我一样的情况。最后欢迎大家和我交流,如果哪位鱼油能指出我的代码的纰漏或者不足,我将万分感谢!! |