Skip to content

Commit f3b8884

Browse files
committed
Solve 2015 22
1 parent 6412fea commit f3b8884

5 files changed

Lines changed: 145 additions & 5 deletions

File tree

lib/2015/day_22.ex

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
defmodule AdventOfCode.Y2015.Day22 do
2+
@moduledoc """
3+
--- Day 22: Wizard Simulator 20XX ---
4+
Problem Link: https://adventofcode.com/2015/day/22
5+
Difficulty: m
6+
Tags: dijkstra game-sim
7+
"""
8+
alias AdventOfCode.Helpers.{InputReader, Transformers}
9+
alias Yog.Traversal.Implicit
10+
11+
def input, do: InputReader.read_from_file(2015, 22)
12+
13+
def run(input \\ input()) do
14+
boss = parse(input)
15+
{solve(boss, :normal), solve(boss, :hard)}
16+
end
17+
18+
def parse(input) do
19+
lines = Transformers.lines(input)
20+
21+
%{
22+
hp: lines |> Enum.at(0) |> String.split(": ") |> List.last() |> String.to_integer(),
23+
dmg: lines |> Enum.at(1) |> String.split(": ") |> List.last() |> String.to_integer()
24+
}
25+
end
26+
27+
defp solve(boss, mode) do
28+
initial_state = %{
29+
hp: 50,
30+
mana: 500,
31+
boss_hp: boss.hp,
32+
boss_dmg: boss.dmg,
33+
spent: 0,
34+
effects: %{shield: 0, poison: 0, recharge: 0},
35+
mode: mode
36+
}
37+
38+
Implicit.implicit_fold_by(
39+
from: initial_state,
40+
using: :best_first,
41+
priority: fn state, _ -> state.spent end,
42+
visited_by: fn state ->
43+
{state.hp, state.mana, state.boss_hp, state.effects}
44+
end,
45+
successors_of: fn state ->
46+
if state.hp <= 0 or state.boss_hp <= 0, do: [], else: get_successors(state)
47+
end,
48+
initial: nil,
49+
with: fn _, state, _ ->
50+
if state.boss_hp <= 0, do: {:halt, state.spent}, else: {:continue, nil}
51+
end
52+
)
53+
end
54+
55+
@spells [
56+
%{name: :missile, cost: 53, dmg: 4, heal: 0, effect: nil, duration: 0},
57+
%{name: :drain, cost: 73, dmg: 2, heal: 2, effect: nil, duration: 0},
58+
%{name: :shield, cost: 113, dmg: 0, heal: 0, effect: :shield, duration: 6},
59+
%{name: :poison, cost: 173, dmg: 0, heal: 0, effect: :poison, duration: 6},
60+
%{name: :recharge, cost: 229, dmg: 0, heal: 0, effect: :recharge, duration: 5}
61+
]
62+
63+
defp get_successors(state) do
64+
# Player Turn Start
65+
state = if state.mode == :hard, do: %{state | hp: state.hp - 1}, else: state
66+
67+
if state.hp <= 0 do
68+
[]
69+
else
70+
{state, _} = apply_effects(state)
71+
72+
if state.boss_hp <= 0 do
73+
[%{state | boss_hp: 0}]
74+
else
75+
# Try casting each spell
76+
for spell <- @spells,
77+
state.mana >= spell.cost,
78+
spell.effect == nil or Map.get(state.effects, spell.effect) <= 0 do
79+
cast(state, spell)
80+
end
81+
|> Enum.map(fn state_after_cast ->
82+
if state_after_cast.boss_hp <= 0 do
83+
%{state_after_cast | boss_hp: 0}
84+
else
85+
# Boss Turn
86+
{state_after_effects, armor} = apply_effects(state_after_cast)
87+
88+
if state_after_effects.boss_hp <= 0 do
89+
%{state_after_effects | boss_hp: 0}
90+
else
91+
# Boss Attack
92+
dmg = max(1, state_after_effects.boss_dmg - armor)
93+
final_state = %{state_after_effects | hp: state_after_effects.hp - dmg}
94+
if final_state.hp <= 0, do: nil, else: final_state
95+
end
96+
end
97+
end)
98+
|> Enum.reject(&is_nil/1)
99+
end
100+
end
101+
end
102+
103+
defp apply_effects(state) do
104+
%{shield: s, poison: p, recharge: r} = state.effects
105+
106+
boss_hp = if p > 0, do: state.boss_hp - 3, else: state.boss_hp
107+
mana = if r > 0, do: state.mana + 101, else: state.mana
108+
armor = if s > 0, do: 7, else: 0
109+
110+
new_effects = %{
111+
shield: max(0, s - 1),
112+
poison: max(0, p - 1),
113+
recharge: max(0, r - 1)
114+
}
115+
116+
{%{state | boss_hp: boss_hp, mana: mana, effects: new_effects}, armor}
117+
end
118+
119+
defp cast(state, spell) do
120+
state = %{state | mana: state.mana - spell.cost, spent: state.spent + spell.cost}
121+
122+
if spell.effect do
123+
%{state | effects: Map.put(state.effects, spell.effect, spell.duration)}
124+
else
125+
%{state | boss_hp: state.boss_hp - spell.dmg, hp: state.hp + spell.heal}
126+
end
127+
end
128+
end

lib/2022/day_08.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,7 @@ defmodule AdventOfCode.Y2022.Day08 do
7474
end
7575

7676
defp viewing_distance(grid, r, c, dr, dc, h) do
77-
if not in_bounds?(grid, r, c) do
78-
0
79-
else
77+
if in_bounds?(grid, r, c) do
8078
Implicit.implicit_fold(
8179
from: {r, c},
8280
using: :breadth_first,
@@ -91,6 +89,8 @@ defmodule AdventOfCode.Y2022.Day08 do
9189
{:continue, acc + 1}
9290
end
9391
)
92+
else
93+
0
9494
end
9595
end
9696

lib/2022/day_10.ex

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ defmodule AdventOfCode.Y2022.Day10 do
5757
IO.write("▒")
5858
end
5959
end
60-
61-
IO.puts("")
6260
end
6361

6462
:ok

priv/input_files/2015_22.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Hit Points: 58
2+
Damage: 9

test/2015/day_22_test.exs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
defmodule AdventOfCode.Y2015.Day22Test do
2+
@moduledoc false
3+
4+
use ExUnit.Case, async: true
5+
@moduletag :y1522
6+
7+
alias AdventOfCode.Y2015.Day22, as: Solution
8+
9+
test "Year 2015, Day 22 run/1" do
10+
assert Solution.run() == {1269, 1309}
11+
end
12+
end

0 commit comments

Comments
 (0)