Enable Javascript in your browser and then refresh this page, for a much enhanced experience.
An Army derived from list simplifies the battles solution in Clear category for Straight Fight by Mysta
from typing import List, Self, Type
START_HEALTH_WARRIOR: int = 50
ATTACK_STRENGTH_WARRIOR: int = 5
START_HEALTH_KNIGHT: int = 50
ATTACK_STRENGTH_KNIGHT: int = 7
START_HEALTH_DEFENDER: int = 60
ATTACK_STRENGTH_DEFENDER: int = 3
DEFENSE_STRENGTH: int = 2
START_HEALTH_VAMPIRE: int = 40
ATTACK_STRENGTH_VAMPIRE: int = 4
DEFAULT_VAMPIRISM: int = 50
START_HEALTH_LANCER: int = 50
ATTACK_STRENGTH_LANCER: int = 6
START_HEALTH_HEALER: int = 60
ATTACK_STRENGTH_HEALER: int = 0
HEALER_STRENGTH: int = 2
# -----------------------------------------------------------------------------
class Warrior:
def __init__(
self, health: int = START_HEALTH_WARRIOR, attack_strength: int = ATTACK_STRENGTH_WARRIOR
) -> None:
self._max_health: int = health
self._health: int = health
self._attack_strength: int = attack_strength
@property
def max_health(self) -> int:
return self._max_health
@property
def is_alive(self) -> bool:
return self._health > 0
# The property "attack" and the corresponding setter is only required for compatibility
# purposes to the Rookie class defined in the CheckiO tests.
# See https://py.checkio.org/forum/post/12692/the-defenders-chek-fail-but-why/#comment-64098
@property
def attack(self) -> int:
return self._attack_strength
@attack.setter
def attack(self, strength: int):
self._attack_strength = strength
# The property "health" and its corresponding setter is only required to pass CheckiO tests
@property
def health(self) -> int:
return self._health
@health.setter
def health(self, value: int):
self._health = value
def attack_combatants(self, combatants: List[Self]) -> bool:
# Attack the first combatant with own attack strength
combatants[0].attacked(self._attack_strength)
# Return True if that combatant is dead
return not combatants[0].is_alive
def attacked(self, attack_strength: int) -> int:
# Weaken the health by the attacker's strength and give back the damage
self._health -= attack_strength
return attack_strength
# -----------------------------------------------------------------------------
class Knight(Warrior):
def __init__(self) -> None:
super().__init__(health=START_HEALTH_KNIGHT, attack_strength=ATTACK_STRENGTH_KNIGHT)
# -----------------------------------------------------------------------------
class Defender(Warrior):
def __init__(self) -> None:
super().__init__(health=START_HEALTH_DEFENDER, attack_strength=ATTACK_STRENGTH_DEFENDER)
# Defenders have got an additional attribute defence strength
self._defense_strength: int = DEFENSE_STRENGTH
def attacked(self, attack_strength: int) -> int:
# If the attacker's strength is higher than the own defense strength then weaken the
# health by the attacker's strength minus the defense strength and give back that damage
damage: int = max(0, attack_strength - self._defense_strength)
self._health -= damage
return damage
# -----------------------------------------------------------------------------
class Vampire(Warrior):
def __init__(self) -> None:
super().__init__(health=START_HEALTH_VAMPIRE, attack_strength=ATTACK_STRENGTH_VAMPIRE)
# Vampires have got an additional attribute vampirism
self._vampirism: int = DEFAULT_VAMPIRISM
def attack_combatants(self, combatants: List[Warrior]) -> bool:
# Attack the first combatant with own attack strength ...
damage: int = combatants[0].attacked(self._attack_strength)
# ... and increase the own health depending on the combatants's damage
# (but never exceed the vampire's maximum health)
self._health = min(self._health + (damage * self._vampirism) // 100, self.max_health)
# Return True if that combatant is dead
return not combatants[0].is_alive
# -----------------------------------------------------------------------------
class Lancer(Warrior):
def __init__(self) -> None:
super().__init__(health=START_HEALTH_LANCER, attack_strength=ATTACK_STRENGTH_LANCER)
def attack_combatants(self, combatants: List[Warrior]) -> bool:
# Attack the first combatant with own attack strength
damage: int = combatants[0].attacked(self._attack_strength)
# And then attack the second combatant (if there is one) with 50% of the damage of the
# first one
if len(combatants) > 1:
combatants[0].attacked(damage // 2)
# Return True if the first combatant is dead
return not combatants[0].is_alive
# -----------------------------------------------------------------------------
class Healer(Warrior):
def __init__(self) -> None:
super().__init__(health=START_HEALTH_HEALER, attack_strength=ATTACK_STRENGTH_HEALER)
def heal(self, warrior: Warrior) -> None:
warrior.health = min(warrior.health + HEALER_STRENGTH, warrior.max_health)
# -----------------------------------------------------------------------------
class Army(list):
def heal(self) -> None:
if len(self) > 1 and isinstance(self[1], Healer):
self[1].heal(self[0])
def remove_dead_soldiers(self) -> None:
idx: int = 0
while idx < len(self):
if not self[idx].is_alive:
del self[idx]
else:
idx += 1
def add_units(self, soldier_type: Type[Warrior], number: int) -> None:
self.extend([soldier_type() for _ in range(number)])
# -----------------------------------------------------------------------------
class Battle:
@staticmethod
def fight(army1: Army, army2: Army) -> bool:
# Fight until one of the armies has got no more soldiers
while army1 and army2:
while True: # Let them fight
army1.heal() # if there are any healers available then let them do their job!
if army1[0].attack_combatants(army2):
# At least the first soldier of army2 is dead -> This fight is finished!
break
army2.heal() # if there are any healers available then let them do their job!
if army2[0].attack_combatants(army1):
# At least the first soldier of army1 is dead -> This fight is finished!
break
army1.remove_dead_soldiers()
army2.remove_dead_soldiers()
return not army2
@staticmethod
def straight_fight(army1: Army, army2: Army) -> bool:
# Fight until one of the armies has got no more soldiers
while army1 and army2:
for soldier1, soldier2 in zip(army1, army2):
fight(soldier1, soldier2)
army1.remove_dead_soldiers()
army2.remove_dead_soldiers()
return not army2
# -----------------------------------------------------------------------------
def fight(combatant1: Warrior, combatant2: Warrior) -> bool:
while True: # Fight until death
if combatant1.attack_combatants([combatant2]):
return True # combatant2 is dead
if combatant2.attack_combatants([combatant1]):
return False # combatant1 is dead
# -----------------------------------------------------------------------------
Feb. 15, 2026