首页 > 解决方案 > 调用另一个类的问题

问题描述

我必须使用 2 个类来完成一种游戏,

“单位”是在我们虚构的战争中战斗的士兵。单位将由 Python 中的一个名为 Unit 的类定义。对于 Nick Wars,我们的单位将具有以下属性:

Team - 一个字符串,指示该单位属于哪个团队。这将始终是“红色”或“蓝色”。必须初始化。HP - 生命值/健康/能量/活力。单位在被击杀之前可以承受的伤害量。单位无法恢复或恢复生命值。必须初始化。ATT - 攻击/力量。单位可以在单次命中造成的伤害量。必须初始化。attack_timeout - 两次攻击之间必须经过的时间量,以滴答为单位(参见下面的方法)。必须初始化。isDead - 如果该单位已死,则为 True,如果它还活着,则为 False。初始化为假。方法:

滴答(自我,对手) - 每次滴答被调用,我们模拟一个单位的时间在游戏场景中传递。单元的任何“自动”行为都在这里定义(因此,我们实质上是在对单元 AI 进行编程)。参数对手是所有作为有效攻击目标的单位的列表。如果 isDead 为 False,则必须创建以下行为。死亡单位不执行上述任何行为,立即返回整数值 0。如果对手列表中有活单位,则该单位攻击 HP 最低的单位(参见攻击方法)。在这种情况下,此方法返回整数值 0。如果多个单位具有相同的 HP,则使用对立单位列表中具有该 HP 的第一个单位。如果对手列表为空或没有活体单位,则返回该单位的攻击值。这将在稍后用于计算对敌人基地的伤害。上述两个动作只有在该单位最后一次攻击后已达到指定的刻度数时才会发生。如何继续尝试这个留给读者作为练习。Attack(self, other) - 当这个单位(self)攻击一个防御单位(other)时调用。防御单位的生命值会因本单位的攻击值而减少。如果这将防御单位的 HP 减少到或超过零,则将 isDead 设置为 True。s 攻击值。如果这将防御单位的 HP 减少到或超过零,则将 isDead 设置为 True。s 攻击值。如果这将防御单位的 HP 减少到或超过零,则将 isDead 设置为 True。

我已经完成了这部分,它看起来像这样

class Unit:

def __init__(self,Team,HP,ATT,attack_timeout,isDead = False):
    self.Team = Team
    self.HP = HP
    self.ATT = ATT
    self.attack_timeout = attack_timeout
    self.isDead = isDead
    self.lastuse = attack_timeout
    
def attack(self,other):
    self.lastuse = 0
    other.HP -= self.ATT
    if other.HP <= 0:
        other.isDead = True

def tick(self,opponents):
    
    if self.isDead == True:
        return 0
    
    else:
        
        healths = []
        lowest = None
        
        if not opponents:
            self.lastuse = 0
            return self.ATT
        
        
            
        if self.lastuse == self.attack_timeout:
            
            for i in opponents:
                healths.append(i.HP)
            
            if sum(healths)<=0:
                self.lastuse = 0
                return self.ATT
            
            else:
                for i in opponents:
                    if lowest == None or i.HP < lowest:
                        lowest = i.HP
                for j in opponents:
                    if j.HP == lowest:
                        break

                Unit.attack(self,j)
                return (0)


        self.lastuse += 1
        return 0

给我带来困难的第二部分是在下面给出的军队课程中使用这个课程

由于大量代码将在两个对立的军队之间重用,因此将它们封装为自己的类是有意义的。

定义具有以下属性和方法的 Army 类:

特性:

Team - 一个字符串,指示该单位属于哪个团队。这将始终是“红色”或“蓝色”。必须初始化。BaseHP - 军队基地剩余的生命值。初始化为 1000 GoldRate - 军队每刻获得的金币数量。初始化为 10。 Gold - 团队用于购买单位和增加 GoldRate 的黄金数量。初始化为 0。 Roster - 指定可购买单位属性的字典列表。必须初始化。价格 - 单位花费的金币数量 HP - 单位开始时的生命值 ATT - 单位的攻击力 attack_timeout - 单位在攻击之间必须等待的时间。Units - 当前存在的 Unit 对象列表。方法:

tick(self,opponents) - 除了 self 之外,获取其他团队单位对手的列表。调用 Units 列表中每个单位的 tick 方法,并传递每个对手的列表。每个 Unit 的 tick 方法的返回值表示对对手基地造成的伤害量。计算这些返回值的总和,并从此 tick 方法返回该总和。此外,团队的金币价值会因 GoldRate 的价值而增加。BuryDead(self) - 从单位列表中删除所有 isDead 为 True 的单位。BuyGoldRate(self) - 如果团队的金币超过 250,则将团队的金币减少 250,并在 GoldRate 上增加 2。BuyUnit(self, x) - x 是一个整数,表示要购买的花名册中的单位的索引。如果团队的黄金数量大于或等于 x 指定单位的价格值,则使用名册条目中指定的属性初始化一个新单位,并将其添加到单位列表中。如果指定的花名册项目不存在,则引发 NoSuchUnitError 异常。如果没有足够的金币购买该单位,则创建一个 TooPoorError 异常。

我试着这样做

class TooPoorError (Exception):
     pass

class NoSuchUnitError (Exception):
     pass

class Army(Unit):

     def __init__ (self,Team,Roster,BaseHP = 1000,GoldRate = 10,Gold = 0):
         self.Team = Team
         self.BaseHP = BaseHP
         self.GoldRate = GoldRate
         self.Gold = Gold
         self.Roster = Roster
         self.Units = []
    
    def tick (self,opponents):
         self.Gold += self.GoldRate  
         for i in self.Units:
             return (Unit.tick(i,opponents))       
        
    def BuryDead(self):
         pass
            
        
    def BuyGoldRate(self):
         if self.Gold>=250:
             self.GoldRate += 2
              self.Gold-= 250

    def BuyUnit(self, x):
             if self.Gold>=self.Roster[x]["Price"]:
                 self.Gold -= self.Roster[x]["Price"]
                 self.Units.append(Unit(self,self.Roster[x]["HP"],self.Roster[x]["ATT"],self.Roster[x]["attack_timeout"]) )
            
             if self.Gold<self.Roster[x]["Price"]:
                 raise TooPoorError

我无法使用我以前的课程来运行 tick 函数并不断返回 0

测试用例如下:

#Visible test

RedArmy  = Army('Red',  [{'Price': 20, 'HP': 100, 'ATT': 7, 'attack_timeout': 3},
                         {'Price': 15, 'HP':  70, 'ATT': 5, 'attack_timeout': 2}])

BlueArmy = Army('Blue', [{'Price': 13, 'HP':  60, 'ATT': 4, 'attack_timeout': 1},
                         {'Price': 16, 'HP':  80, 'ATT': 6, 'attack_timeout': 3}])

# Collecting gold

for i in range(30):
    RedArmy.tick([])
    BlueArmy.tick([])

RedArmy.BuyGoldRate()
BlueArmy.BuyGoldRate()

for i in range(5):
    RedArmy.tick([])
    BlueArmy.tick([])

    
# Training the army

RedArmy.BuyUnit(0)
RedArmy.BuyUnit(0)
RedArmy.BuyUnit(1)

BlueArmy.BuyUnit(1)
BlueArmy.BuyUnit(1)
BlueArmy.BuyUnit(1)
BlueArmy.BuyUnit(0)

print('Gold calculation for Blue: --------', BlueArmy.Gold == 49)
print('Gold calculation for Red: ---------', RedArmy.Gold == 55)

# Fight
    
BlueDamage = 0
RedDamage  = 0

BlueUnits_HP = []
BlueUnits_Dead = []

RedUnits_HP = []
RedUnits_Dead = []

for i in range(80):
    BlueDamage += RedArmy.tick(BlueArmy.Units)
    RedDamage  += BlueArmy.tick(RedArmy.Units)
    
for u in BlueArmy.Units:
    BlueUnits_HP += [u.HP]
    BlueUnits_Dead += [u.isDead]

for u in RedArmy.Units:
    RedUnits_HP  += [u.HP]
    RedUnits_Dead += [u.isDead]

print('Damage calcultion for Blue: -------', BlueDamage == 0)
print('Damage calcultion for Red: --------', RedDamage == 42)
print('Blue units HP: --------------------', BlueUnits_HP == [-4, 31, 80, -4] or BlueUnits_HP == [0, 31, 80, 0])
print('Blue units status: ----------------', BlueUnits_Dead == [True, False, False, True])
print('Red units HP: ---------------------', RedUnits_HP == [-4, -2, 0] or RedUnits_HP == [0, 0, 0])
print('Red units status: -----------------', RedUnits_Dead == [True, True, True])

标签: pythonpython-3.xclass

解决方案


这是非常有趣的代码!我必须进行一些更改才能使其正常工作,部分原因是为了了解正在发生的事情并使代码更加pythonic。我对各种方法的预期目的/您希望攻击优先级如何工作做出了一些假设,但我会解释一切并尝试证明一切。

整个过程中,对于变量名,我已经从 CamelCase 切换到了 snake_case,并通过 linter 传递了代码以使其符合 Python PEP 8 样式指南。

添加了TooPoorError

class NoSuchUnitError(Exception):
    pass


class TooPoorError(Exception):
    pass

军队

添加了保护条款以在购买单位时引发异常。稍微改变了滴答声的工作方式——现在你可以把它称为army.tick()只是推进金币,或者让另一支军队与他们作战。你调用滴答声的方式有点奇怪,所以我不确定这是否可能是让你返回 0 的原因?您可以循环遍历army.units每个 和 for each中的单位,针对对手unit调用该方法。tick我使用列表推导来执行此循环并汇总输出损坏。我认为您可能遇到了一些问题,因为您正在返回第一个单元“tick”的输出,并终止循环。

你也是从 Unit 继承的——我不会说军队是一个单位,而是说军队有一个单位。

class Army:

    def __init__(self, team, roster, hp=1000, gold_rate=10, gold=0):
        self.team = team
        self.hp = hp
        self.gold_rate = gold_rate
        self.gold = gold
        self.roster = roster
        self.units = []

    def tick(self, opponents=None):
        self.gold += self.gold_rate

        if opponents is not None:
            return sum([unit.tick(opponents) for unit in self.units])

    def buy_gold_rate(self):
        if self.gold >= 250:
            self.gold_rate += 2
            self.gold -= 250

    def buy_unit(self, x):
        if self.gold < self.roster[x]["Price"]:
            raise TooPoorError

        if x > len(self.roster):
            raise NoSuchUnitError

        self.gold -= self.roster[x]["Price"]
        self.units.append(Unit(self.roster[x]["HP"], self.roster[x]["ATT"],
                               self.roster[x]["attack_timeout"]))

单元

删除了作为关键字传入的团队以稍微澄清一下,因为它没有被使用。删除了 isDead 参数(你会制作一个开始死亡的新单位吗?)。

保护子句是减少缩进使用量的好方法,我在tick方法中加入了一对。我还添加了一个属性,有助于更简单地跟踪单位是否处于冷却状态。

class Unit:
    def __init__(self, hp, attack_strength, attack_timeout):
        self.hp = hp
        self.attack_strength = attack_strength
        self.attack_timeout = attack_timeout
        self._last_use = attack_timeout

        self.is_dead = False

    def attack(self, other):
        self.lastuse = 0
        other.hp -= self.attack_strength
        if other.hp <= 0:
            other.hp = 0
            other.is_dead = True

    def tick(self, opponent):
        # 
        if self.is_dead:
            return 0

        if self.on_cooldown:
            self._last_use += 1
            return 0

        # Find out if any opposing units are alive
        if all([unit.is_dead for unit in opponent.units]):
            self._last_use = 0
            return self.attack_strength
        else:
            lowest_hp = 1e10
            for unit in opponent.units:
                if unit.hp < lowest_hp and not unit.is_dead:
                    lowest = unit
                    lowest_hp = unit.hp

            self.attack(lowest)

        self._last_use = 0
        return 0

    @property
    def on_cooldown(self):
        if self._last_use != self.attack_timeout:
            return True
        else:
            return False

以下是更新的测试:


red_army = Army('Red',  [{'Price': 20, 'HP': 100, 'ATT': 7, 'attack_timeout': 3},
                         {'Price': 15, 'HP':  70, 'ATT': 5, 'attack_timeout': 2}])

blue_army = Army('Blue', [{'Price': 13, 'HP':  60, 'ATT': 4, 'attack_timeout': 1},
                          {'Price': 16, 'HP':  80, 'ATT': 6, 'attack_timeout': 3}])

# Collecting gold
for _ in range(30):
    red_army.tick()
    blue_army.tick()

red_army.buy_gold_rate()
blue_army.buy_gold_rate()

for _ in range(5):
    red_army.tick()
    blue_army.tick()

# Training the army
red_army.buy_unit(0)
red_army.buy_unit(0)
red_army.buy_unit(1)

blue_army.buy_unit(1)
blue_army.buy_unit(1)
blue_army.buy_unit(1)
blue_army.buy_unit(0)

print('Gold calculation for Blue: --------', blue_army.gold == 49)

print('Gold calculation for Red: ---------', red_army.gold == 55)

# Fight
blue_damage = 0
red_damage = 0

for i in range(80):
    blue_damage += red_army.tick(blue_army)
    red_damage += blue_army.tick(red_army)

blue_units_hp = [unit.hp for unit in blue_army.units]
blue_units_dead = [unit.is_dead for unit in blue_army.units]

red_units_hp = [unit.hp for unit in red_army.units]
red_units_dead = [unit.is_dead for unit in red_army.units]

print('Damage calculation for Blue: ---', blue_damage == 0)
print('Damage calculation for Red: ----', red_damage == 42)
print('Blue units HP: -----------------', blue_units_hp == [0, 31, 80, 0])
print('Blue units status: -------------', blue_units_dead == [True, False, False, True])
print('Red units HP: ------------------', red_units_hp == [0, 0, 0])
print('Red units status: --------------', red_units_dead == [True, True, True])

推荐阅读