233倔强不秃 发表于 2020-3-5 08:54:20

看似抬杠的分析问题

       问题来源于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 =
legal_y =

class Turtle():
    def __init__(self):
      self.hp = 100                                              #乌龟的生命值
      self.x = r.randint(legal_x, legal_x)      #随机初始位置
      self.y = r.randint(legal_x, legal_x)

      
    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:                                     #判断是否越界
            self.x = -self.x
      if self.x > legal_x:
            self.x = 2*legal_x - self.x

      if self.y < legal_y:
            self.y = -self.y
      if self.y > legal_y:
            self.y = 2*legal_y - 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, legal_x)      #随机初始位置
      self.y = r.randint(legal_x, legal_x)


    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:                                     #判断是否越界
            self.x = -self.x
      if self.x > legal_x:
            self.x = 2*legal_x - self.x

      if self.y < legal_y:
            self.y = -self.y
      if self.y > legal_y:
            self.y = 2*legal_y - 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()方法找到被吃的那条小鱼,然后用这个下标在两个列表中都把它删除。如果大佬们能更简单地解决,请在下面留言,我将非常欢迎!

      最后,我想问问大家,为什么我用自己的代码和答案跑了好多次,一直都是小乌龟没有体力值,这也太惨了,不知道你们是不是和我一样的情况。最后欢迎大家和我交流,如果哪位朋友能指出我的代码的纰漏或者不足,我将万分感谢!!
页: [1]
查看完整版本: 看似抬杠的分析问题