Enable Javascript in your browser and then refresh this page, for a much enhanced experience.
Personally fighting with this Rookie - but I won! solution in Clear category for The Defenders by Mysta
from collections import deque
from typing import Deque, Type
START_HEALTH: int = 50
START_HEALTH_DEFENDER: int = 60
ATTACK_STRENGTH_WARRIOR: int = 5
ATTACK_STRENGTH_KNIGHT: int = 7
ATTACK_STRENGTH_DEFENDER: int = 3
ATTACK_STRENGTH_ROOKIE: int = 1
DEFENSE_STRENGTH: int = 2
# -----------------------------------------------------------------------------
class Warrior:
def __init__(self, *args, **kwargs) -> None:
self.health: int = START_HEALTH
self.attack_strength: int = ATTACK_STRENGTH_WARRIOR
@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
def attacked(self, attack_strength: int) -> bool:
if not self.is_alive:
# What the hell is that attacker doing? Overkilling is absolutely
# dishonourably!
return False
# Weaken the health by the attacker's strength
self.health -= attack_strength
# Give back my alive status
return self.is_alive
# -----------------------------------------------------------------------------
class Knight(Warrior):
def __init__(self, *args, **kwargs) -> None:
# Initialise the Knight with the same starting values as the base class
# Warrior ...
super().__init__(*args, **kwargs)
# ... but then adjust (overwrite) his attack strength
self.attack_strength = ATTACK_STRENGTH_KNIGHT
# -----------------------------------------------------------------------------
class Defender(Warrior):
def __init__(self, *args, **kwargs) -> None:
# Initialise the Defender with the same starting values as the base
# class Warrior ...
super().__init__(*args, **kwargs)
# ... but then adjust (overwrite) his settings
self.health = START_HEALTH_DEFENDER
self.attack_strength = ATTACK_STRENGTH_DEFENDER
self.defense_strength: int = DEFENSE_STRENGTH
def attacked(self, attack_strength: int) -> bool:
if not self.is_alive:
# What the hell is that attacker doing? Overkilling is absolutely
# dishonourably!
return False
# Weaken the health by the attacker's strength
if attack_strength > self.defense_strength:
self.health -= attack_strength - self.defense_strength
# Give back my alive status
return self.is_alive
# -----------------------------------------------------------------------------
class Army:
def __init__(self) -> None:
self.soldiers: Deque[Warrior] = deque()
@property
def has_soldiers(self) -> bool:
return len(self.soldiers) > 0
def get_next_soldier(self) -> Warrior:
return self.soldiers.popleft()
def add_units(self, soldier_type: Type[Warrior], number: int) -> None:
self.soldiers.extend([soldier_type() for _ in range(number)])
# -----------------------------------------------------------------------------
class Battle:
def fight(self, army1: Army, army2: Army) -> bool:
# Get the first combatants
combatant1: Warrior = army1.get_next_soldier()
combatant2: Warrior = army2.get_next_soldier()
while True: # Fight until one of the armies has got no more soldiers
# Let the two combatants fight
if fight(combatant1, combatant2):
# The first combatant killed the other
if not army2.has_soldiers:
# The second army has no soldiers left and has lost the battle
return True
# The second army continues the battle with the next soldier
combatant2 = army2.get_next_soldier()
else:
# The second combatant killed the other
if not army1.has_soldiers:
# The first army has no soldiers left and has lost the battle
return False
# The first army continues the battle with the next soldier
combatant1 = army1.get_next_soldier()
# -----------------------------------------------------------------------------
def fight(combatant1: Warrior, combatant2: Warrior) -> bool:
while True: # Fight until death
if not combatant2.attacked(combatant1.attack_strength):
return True
if not combatant1.attacked(combatant2.attack_strength):
return False
# -----------------------------------------------------------------------------
if __name__ == "__main__":
# These "asserts" using only for self-checking and not necessary for auto-testing
# fight tests
chuck = Warrior()
bruce = Warrior()
carl = Knight()
dave = Warrior()
mark = Warrior()
bob = Defender()
mike = Knight()
rog = Warrior()
lancelot = Defender()
assert fight(chuck, bruce) == True
assert fight(dave, carl) == False
assert chuck.is_alive == True
assert bruce.is_alive == False
assert carl.is_alive == True
assert dave.is_alive == False
assert fight(carl, mark) == False
assert carl.is_alive == False
assert fight(bob, mike) == False
assert fight(lancelot, rog) == True
# battle tests
my_army = Army()
my_army.add_units(Defender, 1)
enemy_army = Army()
enemy_army.add_units(Warrior, 2)
army_3 = Army()
army_3.add_units(Warrior, 1)
army_3.add_units(Defender, 1)
army_4 = Army()
army_4.add_units(Warrior, 2)
battle = Battle()
assert battle.fight(my_army, enemy_army) == False
assert battle.fight(army_3, army_4) == True
print("Coding complete? Let's try tests!")
Feb. 13, 2026