AoC 2023 Day 8
Mix.install([
{:kino_aoc, "~> 0.1.5"}
])
Input
{:ok, puzzle_input} =
KinoAOC.download_puzzle("2023", "8", System.fetch_env!("LB_AOC_COOKIE_SECRET"))
Parsing
parse_node = fn string ->
<> <> " = (" <> rest = string
<> <> ", " <> rest = rest
<> <> ")" = rest
{id, {left, right}}
end
[steps, nodes] = String.split(puzzle_input, "\n\n", trim: true)
steps =
steps
|> String.codepoints()
|> Enum.map(fn
"L" -> 0
"R" -> 1
end)
nodes =
nodes
|> String.split("\n", trim: true)
|> Enum.map(parse_node)
|> Enum.into(%{})
Part 1
start = "AAA"
# Just because otherwise this cell breaks on the p2 example input
try do
visited =
steps
|> Stream.cycle()
|> Stream.scan(start, fn step, node ->
nodes[node] |> elem(step)
end)
|> Stream.take_while(&(&1 != "ZZZ"))
|> Enum.count()
visited + 1
rescue
_ -> :err
end
Part 2
steps = Stream.cycle(steps)
count =
Map.keys(nodes)
|> Enum.filter(&match?(<<_, _, "A">>, &1))
|> Enum.map(fn start ->
Stream.scan(steps, start, fn step, node ->
nodes[node] |> elem(step)
end)
end)
|> Stream.zip()
|> Stream.take_while(fn set ->
set
|> Tuple.to_list()
|> Enum.all?(&match?(<<_, _, "Z">>, &1))
|> Kernel.not()
end)
|> Enum.count()
count + 1