Skip to content

Instantly share code, notes, and snippets.

@horstjens
Last active July 14, 2023 10:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save horstjens/dabf7ea02e4ce695a52d8186ddba4d7d to your computer and use it in GitHub Desktop.
Save horstjens/dabf7ea02e4ce695a52d8186ddba4d7d to your computer and use it in GitHub Desktop.
konstantins_magic_duel
import PySimpleGUI as sg
import random
from PIL import Image
from io import BytesIO
import pathlib
class Game:
tile_size = 80
#image_size = 72*2 # ? # replaced by .size attribute of each monster
window = None
round = 1
spells = {}
log = []
armies = {"red":[],
"blue":[],
}
images = {}
red_spell = None
blue_spell = None
class Spell:
def __init__(self, name, **kwargs):
self.image_data = None #
self.name = name
self.image_name = None
self.text1 = ""
self.text2 = ""
Game.spells[name] = self
for k,v in kwargs.items():
setattr(self, k, v)
def get_image(self):
"""search for file lowercase f'{self.name}.png'
and load this image, (72x72 pixel),
double its size and stores it as data into
self.image_data """
filename = self.name.lower()+".png"
img = Image.open(filename)
w, h = img.size
new_size = w*2, h*2
img = img.resize(new_size)
# --- transform into data ---
with BytesIO() as output:
img.save(output, format="PNG")
data = output.getvalue()
self.image_data = data
def __repr__(self):
return f"{self.name} mana cost: {self.mana}"
def cast(self, caster, enemy_color):
my_color = caster.color
Game.log.append(f"{my_color} wizard is casting {self.name}....")
Game.log.append("effects:")
# enough mana to cast the spell?
if self.mana > caster.mana:
Game.log.append("not enough mana to cast this spell")
return
caster.mana -= self.mana
if self.name == "Rest":
caster.mana += 10
Game.log.append(f"{my_color} Wizard is resting and regains 10 mana.")
return
if self.name.startswith("Summon"):
monster = self.name.split(" ")[1]
Game.log.append(f"a {monster} appears and joins the {my_color} army.")
#if monster == "Dog":
#Game.armies[my_color].append(Dog(my_color))
#elif monster == "Cat":
#Game.armies[my_color].append(Cat(my_color))
new_monster = globals()[monster](my_color)
# load images
new_monster.load_images()
#new_monster.create_figures() # self.figure is now a number
# place new monster on correct square
if new_monster.color == "red":
i = len(Game.armies["red"])
i -= 1
x = i // 5
y = i % 5
new_monster.create_figures(
location=(Game.tile_size * (x) + Game.tile_size/2 - new_monster.size/2,
Game.tile_size * (y) + Game.tile_size/2 - new_monster.size/2,
))
elif new_monster.color == "blue":
i = len(Game.armies["blue"])
i -= 1
x = 10 - i //5
y = 5 - i % 5
new_monster.create_figures(
location=(Game.tile_size * (x-1) + Game.tile_size/2 - new_monster.size/2,
Game.tile_size * (y-1) + Game.tile_size/2 - new_monster.size/2,
))
return
# damage spells
victims = []
enemy_army = Game.armies[enemy_color]
if self.victims == -1:
victims = Game.armies[enemy_color]
elif self.victims == 1:
victims.append(random.choice(enemy_army))
else:
random.shuffle(enemy_army)
for i in range(self.victims):
if len(enemy_army) > i:
victims.append(enemy_army[i])
if random.random() > self.jump_chance:
break
# deal damage
for v in victims:
damage = random.randint(self.min_damage, self.max_damage)
if self.damage_type == "physical":
#v.hp -= damage
resist = 0
#Game.log.append(f"{my_color} Wizard hits {enemy_color} {v.__class__.__name__} causing {damage} hp damage")
# resist damage by type
if self.damage_type == "fire":
resist = damage * v.resist_fire
damage -= resist
if self.damage_type == "water":
resist = damage * v.resist_water
damage -= resist
if self.damage_type == "electricity":
resist = damage * v.resist_electricity
damage -= resist
if self.damage_type == "earth":
resist = damage * v.resist_earth
damage -= resist
if self.damage_type == "mind":
resist = damage * v.resist_mind
damage -= resist
# TODO: change state
resist = int(resist)
damage = int(damage)
text = f"{enemy_color} {v.__class__.__name__} can resist {resist} of {damage+resist} {self.damage_type} damage and looses {damage} hp"
v.hp -= damage
if v.hp <= 0:
text += "...killed!"
text += f" ({v.hp} hp left)."
Game.log.append(text)
class Monster:
def __init__(self, color):
self.color = color
self.state = "normal"
self.cast_user_spell = False
self.cast_random_spell = False
self.images = {"normal":[],
"attack":[],
"defend":[],
}
if self.color in Game.armies:
Game.armies[color].append(self)
else:
Game.armies[color] = [self]
def create_figures(self, location):
self.figure = Game.window["canvas"].draw_image(data=self.images["normal"][0],
location=location)
def __repr__(self):
return f"{self.color} {self.__class__.__name__} hp:{self.hp} state:{self.state}"
def get_image(self, filename, flip=False, factor=2):
"""search for file lowercase f'{self.name}.png'
and load this image, (72x72 pixel),
double its size and stores it as data into
self.image_data """
#filename = self.name.lower()+".png"
img = Image.open(filename)
w, h = img.size
print("factor:", factor)
new_size = w*factor, h*factor
img = img.resize(new_size)
self.size = img.size[0]
# --- flip left/right ? ----
if flip:
img = img.transpose(Image.FLIP_LEFT_RIGHT)
# --- transform into data ---
with BytesIO() as output:
img.save(output, format="PNG")
data = output.getvalue()
#self.image_data = data
return data
def load_images(self):
flip = False if self.color == "red" else True
for png_file_name in pathlib.Path(".").glob("*.png"):
p = png_file_name.parts[-1]
if p.startswith(self.__class__.__name__.lower()):
if p[:-4] == self.__class__.__name__.lower():
self.images["normal"].append(self.get_image(p, factor=1,flip=flip))
continue
for state in self.images: # keys: normal, attack, defend
if state == "normal":
continue
if state in p:
self.images[state].append(self.get_image(p, factor=1, flip=flip))
class Wizard(Monster):
def __init__(self, color):
super().__init__(color)
self.cast_user_spell = True
#self.color = color
self.hp = 100
self.mana = 100
self.resist_fire = 0.8
self.resist_water = 0.5
self.resist_electricity = 0.5
self.resist_earth = 0.5
self.resist_mind = 0.9
self.state = "clear"
self.attack = 10
self.defense = 20
self.damage = 10
self.load_images()
def load_images(self):
if self.color == "red":
self.images["normal"].append(self.get_image("red_wizard.png"))
self.images["attack"].append(self.get_image("red_wizard-attack1.png"))
self.images["attack"].append(self.get_image("red_wizard-attack2.png"))
self.images["attack"].append(self.get_image("red_wizard-attack3.png"))
self.images["attack"].append(self.get_image("red_wizard-attack4.png"))
self.images["attack"].append(self.get_image("red_wizard-attack5.png"))
self.images["attack"].append(self.get_image("red_wizard-attack6.png"))
self.images["attack"].append(self.get_image("red_wizard-attack7.png"))
self.images["attack"].append(self.get_image("red_wizard-attack8.png"))
self.images["defend"].append(self.get_image("red_wizard-defend1.png"))
self.images["defend"].append(self.get_image("red_wizard-defend2.png"))
elif self.color == "blue":
self.images["normal"].append(self.get_image("blue_wizard.png")) # flip not necessary because blue wizard images are already flipped
self.images["attack"].append(self.get_image("blue_wizard-attack1.png"))
self.images["attack"].append(self.get_image("blue_wizard-attack2.png"))
self.images["defend"].append(self.get_image("blue_wizard-defend.png"))
def __repr__(self):
# overwrite __repr__ of Monster class because wizard must show also mana
return f"{self.color} {self.__class__.__name__} hp:{self.hp} mana:{self.mana} state:{self.state}"
class Bear(Monster):
def __init__(self, color):
super().__init__(color)
#self.color = color
self.hp = 150
self.resist_fire = 0.5
self.resist_water = 0.5
self.resist_electricity = 0.5
self.resist_earth = 0.8
self.resist_mind = 0.2
self.state = "clear"
self.attack = 25
self.defense = 10
self.damage = 30
class Wolf(Monster):
def __init__(self, color):
super().__init__(color)
#self.color = color
self.hp = 50
self.resist_fire = 0.1
self.resist_water = 0.8
self.resist_electricity = 0.5
self.resist_earth = 0.2
self.resist_mind = 0.5
self.state = "clear"
self.attack = 15
self.defense = 10
self.damage = 15
def fight(attacker, defender):
a = random.randint(1,6) + random.randint(1,6)
d = random.randint(1,6) + random.randint(1,6)
attack_value = a + attacker.attack
defense_value = d + defender.defense
if attack_value > defense_value:
defender.hp -= attacker.damage
Game.log.append(f"{defender} looses {attacker.damage} hp")
if defender.hp > 0:
Game.log.append(f"({defender.hp} hp left)")
else:
Game.log.append(f"{defender} is killed")
else:
Game.log.append("attack fails")
def battle(attacker, defender):
Game.log.append("strike")
fight(attacker, defender)
if defender.hp > 0:
Game.log.append("counterstrike")
fight(defender, attacker)
#sg.PopupOK("Hallo")
layout = [
[sg.Table([["red wizard", 100, "clear"]],
headings=["monster"," hp ","state"],
key="red_army", size=(20,25)),
sg.Graph((800,400),
graph_bottom_left=(0,400),
graph_top_right = (800,0),
key="canvas",
background_color="black"),
sg.Table([["blue wizard", 100, "clear"]],
headings=["monster"," hp ","state"],
key="blue_army", size=(20,25)),
],
[sg.Text("red Wizard (100 mana)",font="Arial 20", text_color="red", key="redmana"),
#sg.Text("mana:100", key="redmana"),
sg.Push(),
sg.Text("blue Wizard (100 mana)",font="Arial 20", text_color="blue", key="bluemana"),
#sg.Text("m, key="bluewizstat", text_color="blue")
],
[sg.Table([["Fireball", 15]],
headings=["Spell name", "mana cost"],
select_mode=sg.TABLE_SELECT_MODE_BROWSE,
key="spells_red",
size=(25,10),
enable_events=True,
),
sg.Push(),
sg.Multiline("Game log:", key="log", disabled=True,
autoscroll=True,size=(45,10)),
sg.Graph(canvas_size=(450,180),
graph_bottom_left=(0.0,1.0),
graph_top_right=(1.0,0.0),
background_color="black",
key="details"),
sg.Push(),
#sg.Listbox(["Fireball","Tsunami"],
# select_mode=sg.SELECT_MODE_SINGLE,
# key="spells_blue",
# size=(25,10),
# disabled=False),
sg.Table([["Fireball", 15]],
headings=["Spell name", "mana cost"],
select_mode=sg.TABLE_SELECT_MODE_BROWSE,
key="spells_blue",
size=(25,10),
enable_events=True,
),
],
[sg.Button("OK"), sg.Button("Cancel"),sg.Text("red Wizard, choose a spell and click OK", key="message", font="Arial 20", text_color="red"),],
]
def main():
Spell("Fireball",
mana=20,
damage_type="fire",
min_damage=10,
max_damage=25,
victims = 3,
jump_chance=1.0,
text1 = "3 victims suffer 10-25 fire damage")
Spell("Flash",
mana=15,
damage_type="electricity",
victims=5,
min_damage=5,
max_damage=15,
jump_chance=0.6,
text1 = "Inflicts 5-15 electric damage ",
text2 = "to 1-5 victims (60% jump chance)"
)
Spell("Quake",
mana = 40,
damage_type="earth",
victims=-1,
min_damage=20,
max_damage=50,
text1= "Inflicts 20-50 earth damage",
text2= "to all enemies")
Spell("Tsunami",
mana=60,
damage_type="water",
victims=-1,
min_damage=30,
max_damage=60,
text1="Inflicts 30-60 water damage",
text2="to all enemies")
Spell("Dazzle",
mana = 10,
damage_type="mind",
victims=1,
min_damage=3,
max_damage=11,
text1="Inflicts 3-11 mind damage",
text2="to a single enemy",)
Spell("Summon Bear", mana=35, victims=0, text1="Bear has 120 hp")
Spell("Summon Wolf", mana=20, victims=0, text1="Wolf has 50 hp")
Spell("Melee attack",
mana=1,
victims=1,
damage_type="physical",
min_damage=1,
max_damage=5,
text1="Inflicts 1-5 physical damage",
text2="to one enemy (no counterstrike)")
Spell("Rest", mana=0, victims=0, text1="wizard regains 10 mana points")
# all spells done, load spell images
for s in Game.spells.values():
s.get_image() # dont do this on __init__ more safe here... should be only made once
Game.window = sg.Window("magic duel", layout)
Game.window.finalize()
# ---- grid lines ----
for y in range(0, 401, Game.tile_size):
Game.window["canvas"].draw_line((0,y),(800,y),"white",1)
for x in range(0, 801, Game.tile_size):
Game.window["canvas"].draw_line((x,0),(x,400),"white",1)
wiz_red = Wizard("red")
wiz_red.create_figures(location=(Game.tile_size/2 - wiz_red.size/2,
Game.tile_size/2 - wiz_red.size/2,))
wiz_blue = Wizard("blue")
wiz_blue.create_figures(location=(Game.tile_size * 9 + Game.tile_size/2 - wiz_blue.size/2,
Game.tile_size * 4 + Game.tile_size/2 - wiz_blue.size/2,))
spell_list = list(Game.spells.values())
spell_list.sort(key=lambda x: x.mana)
show_list = [[f"{s.name}",f"{s.mana}"] for s in spell_list if s.mana <= wiz_red.mana]
Game.window["spells_red"].update(show_list)
#show_list = [f"{s.name}" for s in spell_list if s.mana <= wiz_blue.mana]
show_list = [[f"{s.name}",f"{s.mana}"] for s in spell_list if s.mana <= wiz_blue.mana]
Game.window["spells_blue"].update(show_list)
Game.window["spells_blue"].update(num_rows=0)
number_of_spells = len(Game.spells)
while True:
event, values = Game.window.read()
if event in (sg.WIN_CLOSED, "Cancel"):
break
if event == "OK":
if Game.red_spell is None and Game.blue_spell is None:
sg.PopupError("red Wizard must choose a spell")
continue
if Game.red_spell is not None and Game.blue_spell is None:
Game.log.append(f"Red wizard choose: {Game.red_spell.name}")
Game.window["log"].update("\n".join(Game.log))
Game.window["message"].update("Blue wizard, choose a spell and click OK", text_color="blue")
Game.window["spells_red"].update(num_rows=0) # hide rows
Game.window["spells_blue"].update(num_rows=number_of_spells) # show all rows
continue
if Game.red_spell is not None and Game.blue_spell is not None:
Game.log.append(f"Blue wizard choose: {Game.blue_spell.name}")
# execute turn
Game.log.append(f"----- round {Game.round} ------")
# cast simultanious ?
# summoning spells
for i, s in enumerate([Game.red_spell, Game.blue_spell]):
if s.name.startswith("Summon"):
caster = wiz_red if i==0 else wiz_blue
enemy_color = "blue" if i == 0 else "red"
s.cast(caster, enemy_color )
# ---- non-summuning spells ------
for i, s in enumerate([Game.red_spell, Game.blue_spell]):
if not s.name.startswith("Summon"):
caster = wiz_red if i==0 else wiz_blue
enemy_color = "blue" if i == 0 else "red"
s.cast(caster, enemy_color )
# --- update mana ---
Game.window["redmana"].update(f"red Wizard ({wiz_red.mana} mana)")
Game.window["bluemana"].update(f"blue Wizard ({wiz_blue.mana} mana)")
# ---- update battle map (remove dead figures) ----
for color in ("red", "blue"):
for m in Game.armies[color]:
if m.hp <= 0:
Game.log.append(f"{m.color} {m.__class__.__name__} dies!")
Game.window["canvas"].delete_figure(m.figure)
# ---- update army list (remove dead monsters ) ----
Game.armies[color] = [m for m in Game.armies[color] if m.hp >0]
# ---- prepare next round ---
Game.round += 1
Game.window["log"].update("\n".join(Game.log))
Game.red_spell = None
Game.blue_spell = None
Game.window["spells_red"].update(num_rows=number_of_spells, select_rows=[])
Game.window["spells_blue"].update(num_rows=0)
# ---- update spell list ---
for i, wiz in enumerate((wiz_red, wiz_blue)):
show_list = [[f"{s.name}",f"{s.mana}"] for s in spell_list if s.mana <= wiz.mana]
if i == 0:
Game.window["spells_red"].update(show_list)
elif i == 1:
Game.window["spells_blue"].update(show_list)
Game.window["message"].update("Red wizard, choose a spell and click OK", text_color="red")
Game.window["details"].erase()
# ---- update army list ----
red_army_list = [("red wizard", wiz_red.hp, wiz_red.state)]
for m in Game.armies["red"]:
if m.__class__.__name__ != "Wizard":
red_army_list.append((f"red {m.__class__.__name__}", m.hp, m.state))
blue_army_list = [("blue wizard", wiz_blue.hp, wiz_blue.state)]
for m in Game.armies["blue"]:
if m.__class__.__name__ != "Wizard":
blue_army_list.append((f"blue {m.__class__.__name__}", m.hp, m.state))
Game.window["red_army"].update(red_army_list)
Game.window["blue_army"].update(blue_army_list)
if event in ("spells_red", "spells_blue"):
if len(values[event]) == 0: # nothing selected
continue
line_number = values[event][0] # list with line number inside
#print(line_number)
wiz = wiz_red if event == "spells_red" else wiz_blue
show_list = [[f"{s.name}",f"{s.mana}"] for s in spell_list if s.mana <= wiz.mana]
spell_name = show_list[line_number][0]
text1 = Game.spells[spell_name].text1
text2 = Game.spells[spell_name].text2
spell = Game.spells[spell_name]
if event == "spells_red":
Game.red_spell = spell
elif event == "spells_blue":
Game.blue_spell = spell
#print(spell_name, text1, text2)
# detail feld löschen
d = Game.window["details"]
d.erase()
d.draw_text(spell_name,
location=(0.4, 0),
color="red",
font="System 20",
text_location=sg.TEXT_LOCATION_TOP_LEFT
)
d.draw_text(text1,
location=(0.4, 0.5),
color="white",
font="System 12",
text_location=sg.TEXT_LOCATION_TOP_LEFT
)
d.draw_text(text2,
location=(0.4, 0.75),
color="white",
font="System 12",
text_location=sg.TEXT_LOCATION_TOP_LEFT
)
d.draw_image(#filename=spell_name.lower()+".png",
data = spell.image_data,
location=(0,0))
Game.window.close()
if __name__ == "__main__":
main()
import random
class Game:
spells = {}
log = []
armies = {"red":[],
"blue":[],
}
class Spell:
def __init__(self, name, **kwargs):
self.name = name
Game.spells[name] = self
for k,v in kwargs.items():
setattr(self, k, v)
def __repr__(self):
return f"{self.name} mana cost: {self.mana}"
def cast(self, my_color, enemy_color):
Game.log.append(f"{my_color} wizard is casting {self.name}....")
Game.log.append("effects:")
if self.name == "Rest":
self.mana += 10
Game.log.append(f"{my_color} Wizard is resting and regains 10 mana.")
return
if self.name.startswith("Summon"):
monster = self.name.split(" ")[1]
Game.log.append(f"a {monster} appears and joins the {my_color} army.")
#if monster == "Dog":
#Game.armies[my_color].append(Dog(my_color))
#elif monster == "Cat":
#Game.armies[my_color].append(Cat(my_color))
globals()[monster](my_color)
return
# damage spells
victims = []
enemy_army = Game.armies[enemy_color]
if self.victims == -1:
victims = Game.armies[enemy_color]
elif self.victims == 1:
victims.append(random.choice(enemy_army))
else:
random.shuffle(enemy_army)
for i in range(self.victims):
if len(enemy_army) > i:
victims.append(enemy_army[i])
if random.random() > self.jump_chance:
break
# deal damage
for v in victims:
damage = random.randint(self.min_damage, self.max_damage)
if self.damage_type == "physical":
#v.hp -= damage
resist = 0
#Game.log.append(f"{my_color} Wizard hits {enemy_color} {v.__class__.__name__} causing {damage} hp damage")
# resist damage by type
if self.damage_type == "fire":
resist = damage * v.resist_fire
damage -= resist
if self.damage_type == "water":
resist = damage * v.resist_water
damage -= resist
if self.damage_type == "electricity":
resist = damage * v.resist_electricity
damage -= resist
if self.damage_type == "earth":
resist = damage * v.resist_earth
damage -= resist
if self.damage_type == "mind":
resist = damage * v.resist_mind
damage -= resist
# TODO: change state
resist = int(resist)
damage = int(damage)
text = f"{enemy_color} {v.__class__.__name__} can resist {resist} of {damage+resist} {self.damage_type} damage and looses {damage} hp"
v.hp -= damage
if v.hp <= 0:
text += "...killed!"
text += f" ({v.hp} hp left)."
Game.log.append(text)
Spell("Fireball",
mana=20,
damage_type="fire",
min_damage=10,
max_damage=25,
victims = 3,
jump_chance=1.0)
Spell("Flash",
mana=15,
damage_type="electricity",
victims=5,
min_damage=5,
max_damage=15,
jump_chance=0.6)
Spell("Quake",
mana = 40,
damage_type="earth",
victims=-1,
min_damage=20,
max_damage=50)
Spell("Tsunami",
mana=60,
damage_type="water",
victims=-1,
min_damage=30,
max_damage=60)
Spell("Dazzle",
mana = 10,
damage_type="mind",
victims=1,
min_damage=3,
max_damage=11)
Spell("Summon Cat", mana=5, victims=0)
Spell("Summon Dog", mana=15, victims=0)
Spell("Melee attack",
mana=1,
victims=1,
damage_type="physical",
min_damage=1,
max_damage=5)
Spell("Rest", mana=0, victims=0)
class Monster:
def __init__(self, color):
self.color = color
if self.color in Game.armies:
Game.armies[color].append(self)
else:
Game.armies[color] = [self]
def __repr__(self):
return f"{self.color} {self.__class__.__name__} hp:{self.hp} state:{self.state}"
class Wizard(Monster):
def __init__(self, color):
super().__init__(color)
#self.color = color
self.hp = 100
self.mana = 100
self.resist_fire = 0.8
self.resist_water = 0.5
self.resist_electricity = 0.5
self.resist_earth = 0.5
self.resist_mind = 0.9
self.state = "clear"
self.attack = 10
self.defense = 20
self.damage = 10
def __repr__(self):
# overwrite __repr__ of Monster class because wizard must show also mana
return f"{self.color} {self.__class__.__name__} hp:{self.hp} mana:{self.mana} state:{self.state}"
class Cat(Monster):
def __init__(self, color):
super().__init__(color)
#self.color = color
self.hp = 20
self.resist_fire = 0.2
self.resist_water = 0.1
self.resist_electricity = 0.5
self.resist_earth = 0.8
self.resist_mind = 0.1
self.state = "clear"
self.attack = 8
self.defense = 30
self.damage = 4
class Dog(Monster):
def __init__(self, color):
super().__init__(color)
#self.color = color
self.hp = 40
self.resist_fire = 0.1
self.resist_water = 0.8
self.resist_electricity = 0.5
self.resist_earth = 0.2
self.resist_mind = 0.5
self.state = "clear"
self.attack = 15
self.defense = 10
self.damage = 15
wiz_red = Wizard("red")
wiz_blue = Wizard("blue")
#print(army1, army2)
game_round = 0
while (wiz_red.hp > 0) and (wiz_blue.hp > 0):
game_round += 1
print(f"========== Game round {game_round} =============")
print("++++++army overview:++++++")
for color in Game.armies:
print("=======",color, "army: =========")
for soldier in Game.armies[color]:
print(soldier)
print("----------------")
print("------ choosing actions -------")
actions = {}
spells = {}
for color in Game.armies:
print(f"{color} Wizard, please choose your action")
for i, spell in enumerate(Game.spells.values()):
print(i, spell)
actions[color] = input("?")
spells[color] = list(Game.spells.values())[int(actions[color])]
print("======== results =============")
# TODO: random colorlist (initative-wert?)
for color in Game.armies:
last_line = len(Game.log)
if color == "red":
enemy_color = "blue"
elif color == "blue":
enemy_color = "red"
# TODO: ask player for enemy color
# TODO: make sure summoning spells are executed before damage spells
spells[color].cast(color, enemy_color)
print("\n".join(Game.log[last_line:]))
print("----------------------------")
# eliminate dead monsters
for color in Game.armies:
Game.armies[color] = [m for m in Game.armies[color] if m.hp > 0]
# TODO: surviving monsters attack random enemy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment