-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathaoc202308.ex
116 lines (98 loc) · 3.22 KB
/
aoc202308.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
defmodule AOC2023.Day08 do
@moduledoc """
Advent of Code 2023, day 8: Haunted Wasteland.
"""
require AOC
@doc """
Parse input.
"""
def parse(puzzle_input) do
[path, nodes] = puzzle_input |> String.split("\n\n", trim: true)
{path |> String.to_charlist() |> Enum.map(fn char -> if char == ?L, do: 0, else: 1 end),
nodes
|> String.replace("(", "")
|> String.replace(")", "")
|> String.split("\n", trim: true)
|> Enum.map(fn line -> line |> String.split(" = ") end)
|> Enum.map(fn [from, to] -> {from, to |> String.split(", ") |> List.to_tuple()} end)
|> Enum.into(%{})}
end
@doc """
Solve part 1.
"""
def part1(data) do
{path, nodes} = data
walk_path(nodes, path)
end
@doc """
Solve part 2.
"""
def part2(data) do
{path, nodes} = data
walk_ghost_path(nodes, path)
end
@doc """
Walk a single path from AAA to ZZZ.
## Example:
iex> nodes = %{"AAA" => {"BBB", "BBB"}, "BBB" => {"AAA", "ZZZ"}, "ZZZ" => {"ZZZ", "ZZZ"}}
iex> walk_path(nodes, [0, 0, 1])
6
"""
def walk_path(nodes, directions),
do: walk_path(nodes, directions, directions, "AAA", 0)
def walk_path(_nodes, _directions, _path, "ZZZ", num_steps), do: num_steps
def walk_path(nodes, directions, [], current, num_steps),
do: walk_path(nodes, directions, directions, current, num_steps)
def walk_path(nodes, directions, [turn | path], current, num_steps) do
walk_path(nodes, directions, path, nodes[current] |> elem(turn), num_steps + 1)
end
@doc """
Walk multiple paths from all xxAs to all xxZs.
## Example:
iex> nodes = %{
...> "AAA" => {"11B", "XXX"},
...> "11B" => {"XXX", "ZZZ"},
...> "ZZZ" => {"11B", "XXX"},
...> "22A" => {"22B", "XXX"},
...> "22B" => {"22C", "22C"},
...> "22C" => {"22Z", "22Z"},
...> "22Z" => {"22B", "22B"},
...> "XXX" => {"XXX", "XXX"},
...> }
iex> walk_ghost_path(nodes, [0, 1])
6
"""
def walk_ghost_path(nodes, directions) do
start_nodes = nodes |> Map.keys() |> Enum.filter(fn node -> String.ends_with?(node, "A") end)
walk_ghost_path(
nodes,
directions,
directions,
start_nodes,
start_nodes |> Enum.with_index() |> Enum.map(fn {_, idx} -> {idx, 0} end) |> Enum.into(%{}),
0
)
end
def walk_ghost_path(nodes, directions, [], current, steps, num_steps),
do: walk_ghost_path(nodes, directions, directions, current, steps, num_steps)
def walk_ghost_path(nodes, directions, [turn | path], current, steps, num_steps) do
steps =
Enum.reduce(current |> Enum.with_index(), steps, fn {node, idx}, steps ->
if String.ends_with?(node, "Z"), do: Map.put(steps, idx, num_steps), else: steps
end)
if Enum.all?(Map.values(steps), &(&1 > 0)),
do: steps |> Map.values() |> Enum.reduce(1, fn step, lcm -> Math.lcm(step, lcm) end),
else:
walk_ghost_path(
nodes,
directions,
path,
current |> Enum.map(fn node -> nodes[node] |> elem(turn) end),
steps,
num_steps + 1
)
end
def main(args) do
Enum.map(args, fn path -> AOC.solve(path, &parse/1, &part1/1, &part2/1) end)
end
end