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
def action_cost(action):
	if not action:
		return 0

	spell = get_spell_by_name(action)()
	return spell.cost
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