2015 day 22, part 1.
This commit is contained in:
parent
850c66cd8d
commit
dd8458fa96
168
src/holt59/aoc/2015/day22.py
Normal file
168
src/holt59/aoc/2015/day22.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import heapq
|
||||||
|
import sys
|
||||||
|
from typing import Literal, TypeAlias
|
||||||
|
|
||||||
|
PlayerType: TypeAlias = Literal["player", "boss"]
|
||||||
|
SpellType: TypeAlias = Literal["magic missile", "drain", "shield", "poison", "recharge"]
|
||||||
|
BuffType: TypeAlias = Literal["shield", "poison", "recharge"]
|
||||||
|
Node: TypeAlias = tuple[
|
||||||
|
PlayerType,
|
||||||
|
int,
|
||||||
|
int,
|
||||||
|
int,
|
||||||
|
int,
|
||||||
|
int,
|
||||||
|
tuple[tuple[BuffType, int], ...],
|
||||||
|
tuple[tuple[SpellType, int], ...],
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def play(
|
||||||
|
player_hp: int,
|
||||||
|
player_mana: int,
|
||||||
|
player_armor: int,
|
||||||
|
boss_hp: int,
|
||||||
|
boss_attack: int,
|
||||||
|
hard_mode: bool,
|
||||||
|
) -> tuple[tuple[SpellType, int], ...]:
|
||||||
|
winning_node: tuple[tuple[SpellType, int], ...] | None = None
|
||||||
|
|
||||||
|
visited: set[
|
||||||
|
tuple[PlayerType, int, int, int, int, tuple[tuple[BuffType, int], ...]]
|
||||||
|
] = set()
|
||||||
|
nodes: list[Node] = [
|
||||||
|
("player", 0, player_hp, player_mana, player_armor, boss_hp, (), ())
|
||||||
|
]
|
||||||
|
|
||||||
|
while winning_node is None:
|
||||||
|
(
|
||||||
|
player,
|
||||||
|
mana,
|
||||||
|
player_hp,
|
||||||
|
player_mana,
|
||||||
|
player_armor,
|
||||||
|
boss_hp,
|
||||||
|
buffs,
|
||||||
|
spells,
|
||||||
|
) = heapq.heappop(nodes)
|
||||||
|
|
||||||
|
if (player, player_hp, player_mana, player_armor, boss_hp, buffs) in visited:
|
||||||
|
continue
|
||||||
|
|
||||||
|
visited.add((player, player_hp, player_mana, player_armor, boss_hp, buffs))
|
||||||
|
|
||||||
|
if hard_mode and player == "player":
|
||||||
|
player_hp = max(0, player_hp - 1)
|
||||||
|
|
||||||
|
if player_hp == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if boss_hp == 0:
|
||||||
|
winning_node = spells
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_buffs: list[tuple[BuffType, int]] = []
|
||||||
|
for buff, length in buffs:
|
||||||
|
length = length - 1
|
||||||
|
match buff:
|
||||||
|
case "poison":
|
||||||
|
boss_hp = max(boss_hp - 3, 0)
|
||||||
|
case "shield":
|
||||||
|
if length == 0:
|
||||||
|
player_armor -= 7
|
||||||
|
case "recharge":
|
||||||
|
player_mana += 101
|
||||||
|
|
||||||
|
if length > 0:
|
||||||
|
new_buffs.append((buff, length))
|
||||||
|
|
||||||
|
buffs = tuple(new_buffs)
|
||||||
|
|
||||||
|
if player == "boss":
|
||||||
|
heapq.heappush(
|
||||||
|
nodes,
|
||||||
|
(
|
||||||
|
"player",
|
||||||
|
mana,
|
||||||
|
max(0, player_hp - max(boss_attack - player_armor, 1)),
|
||||||
|
player_mana,
|
||||||
|
player_armor,
|
||||||
|
boss_hp,
|
||||||
|
buffs,
|
||||||
|
spells,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
buff_types = {b for b, _ in buffs}
|
||||||
|
|
||||||
|
for spell, cost, damage, regeneration in (
|
||||||
|
("magic missile", 53, 4, 0),
|
||||||
|
("drain", 73, 2, 2),
|
||||||
|
):
|
||||||
|
if player_mana < cost:
|
||||||
|
continue
|
||||||
|
|
||||||
|
heapq.heappush(
|
||||||
|
nodes,
|
||||||
|
(
|
||||||
|
"boss",
|
||||||
|
mana + cost,
|
||||||
|
player_hp + regeneration,
|
||||||
|
player_mana - cost,
|
||||||
|
player_armor,
|
||||||
|
max(0, boss_hp - damage),
|
||||||
|
buffs,
|
||||||
|
spells + ((spell, cost),),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
for buff_type, buff_cost, buff_length in (
|
||||||
|
("shield", 113, 6),
|
||||||
|
("poison", 173, 6),
|
||||||
|
("recharge", 229, 5),
|
||||||
|
):
|
||||||
|
if buff_type in buff_types:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if player_mana < buff_cost:
|
||||||
|
continue
|
||||||
|
|
||||||
|
heapq.heappush(
|
||||||
|
nodes,
|
||||||
|
(
|
||||||
|
"boss",
|
||||||
|
mana + buff_cost,
|
||||||
|
player_hp,
|
||||||
|
player_mana - buff_cost,
|
||||||
|
player_armor + 7 * (buff_type == "shield"),
|
||||||
|
boss_hp,
|
||||||
|
buffs + ((buff_type, buff_length),),
|
||||||
|
spells + ((buff_type, buff_cost),),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return winning_node
|
||||||
|
|
||||||
|
|
||||||
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
||||||
|
player_hp = 50
|
||||||
|
player_mana = 500
|
||||||
|
player_armor = 0
|
||||||
|
|
||||||
|
boss_hp = int(lines[0].split(":")[1].strip())
|
||||||
|
boss_attack = int(lines[1].split(":")[1].strip())
|
||||||
|
|
||||||
|
answer_1 = sum(
|
||||||
|
c
|
||||||
|
for _, c in play(player_hp, player_mana, player_armor, boss_hp, boss_attack, False)
|
||||||
|
)
|
||||||
|
print(f"answer 1 is {answer_1}")
|
||||||
|
|
||||||
|
# 1242 (not working)
|
||||||
|
answer_2 = sum(
|
||||||
|
c for _, c in play(player_hp, player_mana, player_armor, boss_hp, boss_attack, True)
|
||||||
|
)
|
||||||
|
print(f"answer 2 is {answer_2}")
|
2
src/holt59/aoc/inputs/holt59/2015/day22.txt
Normal file
2
src/holt59/aoc/inputs/holt59/2015/day22.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Hit Points: 51
|
||||||
|
Damage: 9
|
Loading…
Reference in New Issue
Block a user