| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- #!/usr/bin/env python3
- from queue import PriorityQueue
- from collections import namedtuple
- Spell = namedtuple('Spell', 'cost duration damage heal armor mana'.split())
- spells = [
- Spell(53, 0, 4, 0, 0, 0),
- Spell(73, 0, 2, 2, 0, 0),
- Spell(113, 6, 0, 0, 7, 0),
- Spell(173, 6, 3, 0, 0, 0),
- Spell(229, 5, 0, 0, 0, 101),
- ]
- class State:
- def __init__(self, index, previous_cost, player_hp, player_mana,
- boss_hp, boss_damage, hard, timers=None):
- self.index = index
- self.cost = previous_cost + spells[index].cost
- self.player_hp = player_hp
- self.player_mana = player_mana
- self.boss_hp = boss_hp
- self.boss_damage = boss_damage
- self.hard = hard
- self.timers = timers if timers else [0] * len(spells)
- def __lt__(self, other):
- return self.cost < other.cost
- def can_cast(self, index):
- return self.timers[index] <= 1 and \
- self.player_mana >= spells[index].cost
- def cast(self, index):
- spell = spells[index]
- self.player_mana -= spell.cost
- if spell.duration:
- self.timers[index] = spell.duration
- else:
- self.boss_hp -= spell.damage
- self.player_hp += spell.heal
- def apply_effects(self):
- for i, remain in enumerate(self.timers):
- if remain > 0:
- spell = spells[i]
- self.boss_hp -= spell.damage
- self.player_mana += spell.mana
- if remain == spell.duration:
- self.boss_damage -= spell.armor
- elif remain == 1:
- self.boss_damage += spell.armor
- self.timers[i] = remain - 1
- def turn(self, verbose=False):
- if self.hard:
- self.player_hp -= 1
- if self.player_hp <= 0:
- return
- self.apply_effects()
- self.cast(self.index)
- self.apply_effects()
- self.player_hp -= max(self.boss_damage, 1)
- def add_spell(self, index):
- return State(index, self.cost, self.player_hp, self.player_mana,
- self.boss_hp, self.boss_damage, self.hard,
- list(self.timers))
- def min_win_cost(player_hp, player_mana, boss_hp, boss_damage, hard=False):
- worklist = PriorityQueue()
- indices = list(range(len(spells)))
- for index in indices:
- worklist.put(State(index, 0, player_hp, player_mana,
- boss_hp, boss_damage, hard))
- while not worklist.empty():
- state = worklist.get()
- state.turn()
- if state.boss_hp <= 0:
- return state.cost
- if state.player_hp > 0:
- for index in indices:
- if state.can_cast(index):
- worklist.put(state.add_spell(index))
- assert min_win_cost(10, 250, 13, 8) == 226
- assert min_win_cost(10, 250, 14, 8) == 641
- print(min_win_cost(50, 500, 55, 8))
- print(min_win_cost(50, 500, 55, 8, hard=True))
|