python - 调用另一个类的问题
问题描述
我必须使用 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])
解决方案
这是非常有趣的代码!我必须进行一些更改才能使其正常工作,部分原因是为了了解正在发生的事情并使代码更加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])
推荐阅读
- caching - 如何将 esi 标签包含到主 esi 标签中?
- sql - 使用其他表的 Postgres 合并示例
- visual-studio-code - 如何关闭 vscode 的搜索开关?
- android - 将带有父类的可打包 Kotlin 类转换为 JSON
- c# - 使用 .NET 核心 3.1 的 Azure 网络作业
- sql - 使用 WHERE 子句中的过滤器优化 OUTER JOIN 查询。(查询规划器)
- javascript - 为什么这个 React 组件在其 prop 更改时不重新渲染?
- java - 我可以在我的 Java 代码中使用 DynamoDB Streams 来更新/添加数据到我的 DynamoDB 吗?
- javascript - 一步一步的登记表
- c++ - C++ 中三元运算符的意外行为