Enable Javascript in your browser and then refresh this page, for a much enhanced experience.
Letting groups of soldiers fight and simplified class initializers solution in Clear category for The Lancers by Mysta
from collections import deque
from typing import Deque, 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
MAX_SOLDIERS_PER_FIGHT: int = 2
# -----------------------------------------------------------------------------
class Warrior:
def __init__(
self, health: int = START_HEALTH_WARRIOR, attack_strength: int = ATTACK_STRENGTH_WARRIOR
) -> None:
self._health: int = health
self._attack_strength: int = attack_strength
@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, START_HEALTH_VAMPIRE)
# 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 Army:
def __init__(self) -> None:
self._soldiers: Deque[Warrior] = deque()
def get_next_soldiers(self, amount: int = MAX_SOLDIERS_PER_FIGHT) -> List[Warrior]:
soldiers: List[Warrior] = []
# First remove dead soldiers from previous fights
while self._soldiers and not self._soldiers[0].is_alive:
self._soldiers.popleft()
# Fill as much as possible but maximun the requested amount of soldiers into the list
idx: int = 0
while idx < amount and idx < len(self._soldiers):
soldiers.append(self._soldiers[idx])
idx += 1
return soldiers
def add_units(self, soldier_type: Type[Warrior], number: int) -> None:
self._soldiers.extend([soldier_type() for _ in range(number)])
# -----------------------------------------------------------------------------
class Battle:
@staticmethod
def fight(army1: Army, army2: Army) -> bool:
while True: # Fight until one of the armies has got no more soldiers
soldiers1: List[Warrior] = army1.get_next_soldiers()
if not soldiers1:
return False # army2 is the winner
soldiers2: List[Warrior] = army2.get_next_soldiers()
if not soldiers2:
return True # army2 is the winner
while True: # Let them fight
if soldiers1[0].attack_combatants(soldiers2):
# At least the first soldier of soldiers2 is dead -> This fight is finished!
break
if soldiers2[0].attack_combatants(soldiers1):
# At least the first soldier of soldiers1 is dead -> This fight is finished!
break
# -----------------------------------------------------------------------------
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. 14, 2026