22_wizard.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. #!/usr/bin/env python3
  2. from queue import PriorityQueue
  3. from collections import namedtuple
  4. Spell = namedtuple('Spell', 'cost duration damage heal armor mana'.split())
  5. spells = [
  6. Spell(53, 0, 4, 0, 0, 0),
  7. Spell(73, 0, 2, 2, 0, 0),
  8. Spell(113, 6, 0, 0, 7, 0),
  9. Spell(173, 6, 3, 0, 0, 0),
  10. Spell(229, 5, 0, 0, 0, 101),
  11. ]
  12. class State:
  13. def __init__(self, index, previous_cost, player_hp, player_mana,
  14. boss_hp, boss_damage, hard, timers=None):
  15. self.index = index
  16. self.cost = previous_cost + spells[index].cost
  17. self.player_hp = player_hp
  18. self.player_mana = player_mana
  19. self.boss_hp = boss_hp
  20. self.boss_damage = boss_damage
  21. self.hard = hard
  22. self.timers = timers if timers else [0] * len(spells)
  23. def __lt__(self, other):
  24. return self.cost < other.cost
  25. def can_cast(self, index):
  26. return self.timers[index] <= 1 and \
  27. self.player_mana >= spells[index].cost
  28. def cast(self, index):
  29. spell = spells[index]
  30. self.player_mana -= spell.cost
  31. if spell.duration:
  32. self.timers[index] = spell.duration
  33. else:
  34. self.boss_hp -= spell.damage
  35. self.player_hp += spell.heal
  36. def apply_effects(self):
  37. for i, remain in enumerate(self.timers):
  38. if remain > 0:
  39. spell = spells[i]
  40. self.boss_hp -= spell.damage
  41. self.player_mana += spell.mana
  42. if remain == spell.duration:
  43. self.boss_damage -= spell.armor
  44. elif remain == 1:
  45. self.boss_damage += spell.armor
  46. self.timers[i] = remain - 1
  47. def turn(self, verbose=False):
  48. if self.hard:
  49. self.player_hp -= 1
  50. if self.player_hp <= 0:
  51. return
  52. self.apply_effects()
  53. self.cast(self.index)
  54. self.apply_effects()
  55. self.player_hp -= max(self.boss_damage, 1)
  56. def add_spell(self, index):
  57. return State(index, self.cost, self.player_hp, self.player_mana,
  58. self.boss_hp, self.boss_damage, self.hard,
  59. list(self.timers))
  60. def min_win_cost(player_hp, player_mana, boss_hp, boss_damage, hard=False):
  61. worklist = PriorityQueue()
  62. indices = list(range(len(spells)))
  63. for index in indices:
  64. worklist.put(State(index, 0, player_hp, player_mana,
  65. boss_hp, boss_damage, hard))
  66. while not worklist.empty():
  67. state = worklist.get()
  68. state.turn()
  69. if state.boss_hp <= 0:
  70. return state.cost
  71. if state.player_hp > 0:
  72. for index in indices:
  73. if state.can_cast(index):
  74. worklist.put(state.add_spell(index))
  75. assert min_win_cost(10, 250, 13, 8) == 226
  76. assert min_win_cost(10, 250, 14, 8) == 641
  77. print(min_win_cost(50, 500, 55, 8))
  78. print(min_win_cost(50, 500, 55, 8, hard=True))