Advent of code day 08
Mix.install([
{:kino, "~> 0.5.0"}
])
Setup input
example = Kino.Input.textarea("Please paste your input example:")
input = Kino.Input.textarea("Please paste your real input:")
[dirs | nodes] =
example
|> Kino.Input.read()
|> String.split(["\n"], trim: true)
original_dirs = String.split(dirs, "", trim: true)
nodes =
Enum.reduce(nodes, Map.new(), fn node, acc ->
[key, _, children | _] = String.split(node, [" = ", "(", ")"])
[left, right | _] = String.split(children, ", ", trim: true)
acc
|> Map.put(key, [left, right])
end)
Part 01
start = "AAA"
Stream.iterate(0, &(&1 + 1))
|> Enum.reduce_while({original_dirs, start, 0}, fn _move, {dirs, node, moves} ->
cond do
node == "ZZZ" -> {:halt, moves}
dirs == [] -> {:cont, {original_dirs, node, moves}}
hd(dirs) == "L" -> {:cont, {tl(dirs), Map.get(nodes, node) |> Enum.at(0), moves + 1}}
hd(dirs) == "R" -> {:cont, {tl(dirs), Map.get(nodes, node) |> Enum.at(1), moves + 1}}
end
end)
Part 02
starts = Map.keys(nodes) |> Enum.filter(fn node -> String.ends_with?(node, "A") end)
# tried simulation the same way as part 01. It was too slow.
defmodule Solver do
def lcm(a, b), do: div(a * b, Integer.gcd(a,b))
def find_cycle_length(start, dirs, nodes) do
Stream.cycle(dirs)
|> Enum.reduce_while({start, 0}, fn dir, {node, steps} ->
cond do
String.ends_with?(node, "Z") -> {:halt, steps}
dir == "L" -> {:cont, { Map.get(nodes, node) |> Enum.at(0), steps + 1}}
dir == "R" -> {:cont, { Map.get(nodes, node) |> Enum.at(1), steps + 1}}
end
end)
end
end
starts
|> Enum.map(&Solver.find_cycle_length(&1, original_dirs, nodes))
|> Enum.reduce(&Solver.lcm/2)