2015 day 22, part 1.
This commit is contained in:
		
							
								
								
									
										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}") | ||||
		Reference in New Issue
	
	Block a user