def successors(game_state): """Return a dict of {state:action} pairs. A state is a (wizard, boss, timed_effects) tuple, wizard: (hit_points, armor, mana); boss: (hit_points)""" result = {} w, b, in_use_spells_state = game_state spells_in_use = [name for name, timer in in_use_spells_state if timer > 1] # apply_effects gets callend once before launch spell is called # this clears up spells that have just timer=1 left spells = [magic_missile, drain, shield, poison, recharge] available_spells = [s for s in spells if s().name not in spells_in_use] for available_spell in available_spells: boss = Boss(damage=boss_damage, hit_points=b) h,a,m = w wizard = Wizard(mana=m, hit_points=h, armor=a) gs = GameState(wizard, boss) candidate_spell = available_spell() # restore timed spells for name, timer in in_use_spells_state: gs.spells.append(get_spell_by_name(name)(timer)) gs.apply_effects() if not boss.is_alive(): result[represent(gs)] = None # boss is killed by existing spells else: wizard.launch_spell(gs, spell=candidate_spell) if not wizard.is_alive(): continue if not boss.is_alive(): # boss killed by spell result[represent(gs)] = candidate_spell.name else: gs.apply_effects() if not boss.is_alive(): # boss killed by timed effect result[represent(gs)] = candidate_spell.name else: boss.attack(wizard) if wizard.is_alive(): result[represent(gs)] = candidate_spell.name return result
if __name__ == '__main__' and __package__ is None: from os import sys, path sys.path.append(path.dirname(path.dirname(path.abspath(__file__)))) from game.spells import magic_missile, drain, shield, poison, recharge from game.characters import Boss, Wizard from game.strategies import SelectSpellByPredefinedOrder from game.game_state import combat def hit_armor_mana(wizard): return (wizard.hit_points, wizard.armor, wizard.mana) mock_order = [poison(), recharge(), magic_missile(), magic_missile(), poison(), shield(), magic_missile(), magic_missile()] wizard = Wizard(hit_points=50, mana=500, spell_selection_strategy=SelectSpellByPredefinedOrder(mock_order)) boss = Boss(hit_points=51, damage=9) combat(wizard, boss) assert not boss.is_alive() assert wizard.is_alive() # SOLUTION [((50, 0, 500), 51, frozenset([])), ('Poison', 173), ((41, 0, 327), 48, frozenset([('Poison', 5)])), ('Recharge', 402), ((32, 0, 199), 42, frozenset([('Poison', 3), ('Recharge', 4)])), ('Magic Missile', 455), ((23, 0, 348), 32, frozenset([('Poison', 1), ('Recharge', 2)])), ('Magic Missile', 508), ((14, 0, 497), 25, frozenset([])), ('Poison', 681), ((5, 0, 324), 22, frozenset([('Poison', 5)])), ('Shield', 794), ((3, 7, 211), 16, frozenset([('Poison', 3), ('Shield', 5)])), ('Magic Missile', 847), ((1, 7, 158), 6, frozenset([('Poison', 1), ('Shield', 3)])), ('Magic Missile', 900), ((1, 7, 105), -1, frozenset([('Shield', 2)]))]
def successors_2(game_state): """Return a dict of {state:action} pairs. A state is a (wizard, boss, timed_effects) tuple, wizard: (hit_points, armor, mana); boss: (hit_points)""" result = {} w, b, in_use_spells_state = game_state spells_in_use = [name for name, timer in in_use_spells_state if timer > 1] # apply_effects gets callend once before launch spell is called # this clears up spells that have just timer=1 left spells = [magic_missile, drain, shield, poison, recharge] available_spells = [s for s in spells if s().name not in spells_in_use] # print game_state for available_spell in available_spells: boss = Boss(damage=boss_damage, hit_points=b) h,a,m = w wizard = Wizard(mana=m, hit_points=h, armor=a) gs = GameState(wizard, boss, level='hard') candidate_spell = available_spell() # print 'candidate_spell: %s' % candidate_spell.name # restore timed spells for name, timer in in_use_spells_state: gs.spells.append(get_spell_by_name(name)(timer)) gs.apply_effects('before_wizard') if not wizard.is_alive(): # can't happen at easy level # print 'wizard is dead by hard level 1' # print represent(gs) continue if not boss.is_alive(): # if boss dies by existing effects it is not necessary to add # a new spell result[represent(gs)] = None # boss is killed by existing spells else: # here boss is alive wizard.launch_spell(gs, spell=candidate_spell) if not wizard.is_alive(): # print 'wizard is dead launching spell' # print represent(gs) continue if not boss.is_alive(): # boss killed by spell result[represent(gs)] = candidate_spell.name else: gs.apply_effects('before_boss') if not wizard.is_alive(): # print 'wizard is dead by hard level 2' # print represent(gs) continue if not boss.is_alive(): # boss killed by timed effect result[represent(gs)] = candidate_spell.name else: boss.attack(wizard) if wizard.is_alive(): # if wizard is alive I log the launched spell # or else I don't care about this state result[represent(gs)] = candidate_spell.name return result
from os import sys, path sys.path.append(path.dirname(path.dirname(path.abspath(__file__)))) from game.spells import Effect, TimedEffect from game.spells import magic_missile, drain, shield, poison, recharge from game.characters import Boss, Wizard from game.strategies import SelectSpellByPredefinedOrder from game.game_state import GameState def hit_armor_mana(wizard): return (wizard.hit_points, wizard.armor, wizard.mana) # Now, suppose the same initial conditions, except that the boss has 14 hit points instead: mock_order = [recharge(), shield(), drain(), poison(), magic_missile()] wizard = Wizard(hit_points=10, mana=250, spell_selection_strategy=SelectSpellByPredefinedOrder(mock_order)) boss = Boss(hit_points=14, damage=8) game_state = GameState(wizard, boss) # -- Player turn -- # - Player has 10 hit points, 0 armor, 250 mana assert hit_armor_mana(wizard) == (10, 0, 250) # - Boss has 14 hit points assert boss.hit_points == 14 # Player casts Recharge. s = wizard.launch_spell(game_state) assert s.name == 'Recharge' # -- Boss turn -- # - Player has 10 hit points, 0 armor, 21 mana assert hit_armor_mana(wizard) == (10, 0, 21) # - Boss has 14 hit points