Enable Javascript in your browser and then refresh this page, for a much enhanced experience.
dataclasses with common Attributes base solution in Clear category for The Warlords by juestr
from dataclasses import dataclass, field
from operator import attrgetter
class str_list(list):
def __str__(self):
return '[' + ', '.join(str(i) for i in self) + ']'
@dataclass
class Attributes:
health: int = 0
attack: int = 0
defense: int = 0
vampirism: int = 0
heal_power: int = 0
deephit: int = 0
@dataclass
class Weapon(Attributes):
def __str__(self):
return self.__class__.__name__
@dataclass
class Sword(Weapon):
health: int = 5
attack: int = 2
@dataclass
class Shield(Weapon):
health: int = 20
attack: int = -1
defense: int = 2
@dataclass
class GreatAxe(Weapon):
health: int = -15
attack: int = 5
defense: int = -2
vampirism: int = 10
@dataclass
class Katana(Weapon):
health: int = -20
attack: int = 6
defense: int = -5
vampirism: int = 50
@dataclass
class MagicWand(Weapon):
health: int = 30
attack: int = 3
heal_power: int = 3
@dataclass
class Warrior(Attributes):
health: int = 50
attack: int = 5
max_health: int = None
weapons: str_list = field(default_factory=str_list)
pref_army_pos = 2
def __post_init__(self):
if self.max_health is None:
self.max_health = self.health
@property
def is_alive(self):
return self.health > 0
def equip_weapon(self, weapon):
enhance = lambda base, mod: max(base + mod, 0) if base > 0 else base
self.weapons.append(weapon)
self.health = enhance(self.health, weapon.health)
self.max_health = enhance(self.max_health, weapon.health)
self.attack = enhance(self.attack, weapon.attack)
self.defense = enhance(self.defense, weapon.defense)
self.vampirism = enhance(self.vampirism, weapon.vampirism)
self.heal_power = enhance(self.heal_power, weapon.heal_power)
self.deephit = enhance(self.deephit, weapon.deephit)
def hit(self, other, ally=None, other_ally=None):
damage = max(self.attack - other.defense, 0)
other.health -= damage
self.health += damage * self.vampirism // 100
if other_ally:
other_ally.health -= damage * self.deephit // 100
if ally and ally.is_alive:
ally.heal(self)
def heal(self, ally):
ally.health = min(ally.max_health, ally.health + self.heal_power)
def __str__(self):
return f'{self.__class__.__name__}({self.health}{self.weapons or ""})'
@dataclass
class Knight(Warrior):
attack: int = 7
@dataclass
class Defender(Warrior):
health: int = 60
attack: int = 3
defense: int = 2
@dataclass
class Vampire(Warrior):
health: int = 40
attack: int = 4
vampirism: int = 50
@dataclass
class Lancer(Warrior):
attack: int = 6
deephit: int = 50
pref_army_pos = 1
@dataclass
class Healer(Warrior):
health: int = 60
attack: int = 0
heal_power: int = 2
pref_army_pos = 0
@dataclass
class Warlord(Warrior):
health: int = 100
attack: int = 4
defense: int = 2
pref_army_pos = 100
def fight(unit_1, unit_2, unit_1b=None, unit_2b=None, to_any_death=False):
if to_any_death:
keep_fighting = lambda: all(u is None or u.is_alive for u in (unit_1, unit_2, unit_1b, unit_2b))
else:
keep_fighting = lambda: unit_1.is_alive and unit_2.is_alive
while keep_fighting():
unit_1.hit(unit_2, ally=unit_1b, other_ally=unit_2b)
if keep_fighting():
unit_2.hit(unit_1, ally=unit_2b, other_ally=unit_1b)
return None if to_any_death else unit_1.is_alive
class Army(str_list):
@property
def units(self):
return self
@property
def has_warlord(self):
return any(type(w) is Warlord for w in self)
def add_units(self, factory, number):
if factory is Warlord:
number = min(number, 1 - self.has_warlord)
self.extend(factory() for _ in range(number))
def remove_fallen(self):
fallen = [w for w in self if not w.is_alive]
for w in fallen:
self.remove(w)
fallen and self.try_move_units()
def move_units(self):
assert self.has_warlord
self.sort(key=attrgetter('pref_army_pos'))
if type(self[0]) is Healer:
champion = next((w for w in self if 0 < w.pref_army_pos < 100), None)
if champion:
self.remove(champion)
self.insert(0, champion)
def try_move_units(self):
self.has_warlord and self.move_units()
class Battle:
@staticmethod
def fight(army1, army2):
next_pair = lambda army: army[:2] if len(army) >= 2 else (army[0], None)
army1.try_move_units()
army2.try_move_units()
print('battle starts', '\n army 1:', army1, '\n army 2:', army2)
while len(army1) and len(army2):
w1, w1b = next_pair(army1)
w2, w2b = next_pair(army2)
print('fight ', w1b, w1, '<->', w2, w2b)
fight(w1, w2, w1b, w2b, to_any_death=True)
print(' -->', w1b, w1, '<->', w2, w2b)
army1.remove_fallen()
army2.remove_fallen()
return bool(len(army1))
@staticmethod
def straight_fight(army1, army2):
print('battle starts with straight fight')
while len(army1) and len(army2):
print(' army 1:', army1, '\n army 2:', army2)
for w1, w2 in zip(army1[:], army2[:]):
print('fight ', w1, '<->', w2)
result = fight(w1, w2)
print(f'--> army {2-int(result)} won', w1, '<->', w2)
(army1, army2)[result].remove((w1, w2)[result])
return bool(len(army1))
Oct. 27, 2019