呼叫另一个班级的问题

时间:2020-06-20 21:57:28

标签: python python-3.x class

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

“部队”是在我们虚拟战争中战斗的士兵。单位将由python中称为Unit的类定义。对于尼克战争,我们的部队将具有以下属性:

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

tick(自己,对手)-每次调用tick,我们都会模拟一个时间单位经过游戏场景。此处定义了单元的任何“自动”行为(因此,我们实质上是对单元AI进行编程)。参数对手是有效攻击目标的所有单位的列表。如果isDead为False,则必须创建以下行为。死单元将不执行任何这些行为,并立即返回整数值0。 如果对手名单中有活单位,则该单位攻击HP最低的单位(请参阅攻击方法)。在这种情况下,此方法返回整数值0。如果多个单位具有相同的HP,请在相对单位列表中使用具有该HP的第一个单位。 如果对手列表为空或不包含任何生存单位,则返回该单位的攻击值。稍后将用于计算对敌人基地的伤害。 如果自本机上次攻击以来已达到指定的滴答数,则只能执行上述两个操作。如何保持尝试作为练习留给读者。 Attack(self,other)-当本单位(自身)攻击防御单位(其他)时调用。防御单位的生命值被其单位的攻击值降低。如果这会使防御单位的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

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

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

定义具有以下属性和方法的陆军课程:

属性:

Team-一个字符串,指示该单位属于哪个团队。这将始终是“红色”或“蓝色”。必须初始化。 BaseHP-陆军基地剩余的生命值。初始化到1000 GoldRate-军队每跳获得的金数。初始化为10。 黄金-团队用于购买单位和增加GoldRate的黄金数量。初始化为0。 名册-词典列表,用于指定可购买的单位的属性。必须初始化。 价格-单位成本的黄金数量 HP-单位起始的生命值 ATT-单位的攻击力 Attack_timeout-单元必须在两次攻击之间等待的时间。 单位-当前存在的单位对象列表。 方法:

勾号(self,opponents)-除self之外,还列出了另一支队伍的单位对手。调用“单位”列表中每个单位的报价方法,并传递每个对手列表。每个单位的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])

1 个答案:

答案 0 :(得分:2)

这是很有趣的代码! 我必须进行一些更改才能使其正常运行,部分原因是为了了解正在发生的事情并使代码更具Python风格。我对各种方法的预期目的/您希望攻击优先级如何发挥作用进行了一些假设,但我将解释所有内容并尝试证明所有理由。

常规 在整个过程中,我已从CamelCase切换到snake_case以获取变量名,并已通过linter传递了代码,以使其与Python PEP 8样式指南保持一致。

添加了TooPoorError

class NoSuchUnitError(Exception):
    pass


class TooPoorError(Exception):
    pass

军队

添加了保护条款,以在购买单位时引发例外情况。 更改了滴答的工作方式-现在您可以将其称为army.tick()来推进金币,或者通过另一支军队与之战斗。您打勾的方式有点怪异,所以我不确定这是否是给您0回报的原因?您可以仅循环army.units中的单位,并针对每个unit,针对对手调用tick方法。我使用列表理解来执行此循环并求和输出损害。我认为您可能会遇到一些问题,因为您正在返回第一个单元“ tick”的输出并杀死了循环。

您也从部队继承而来-我不会说陆军是一个部队,而是陆军是一个部队。

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参数(您是否会创建一个开始失效的新Unit?)。

Guard子句是减少缩进量的一种好方法,我在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])
相关问题