aoc/23/elixir/lib/days/day8.ex

97 lines
2.7 KiB
Elixir
Raw Normal View History

2023-12-08 09:00:27 +00:00
defmodule AOC.Day8 do
# use AOC.Day, day: 8, input: "example1"
# use AOC.Day, day: 8, input: "example2"
# use AOC.Day, day: 8, input: "example3"
use AOC.Day, day: 8
@start "AAA"
@stop "ZZZ"
def parse_input([instructions, _ | nodes]) do
instructions = instructions |> String.to_charlist() |> Stream.cycle()
2023-12-08 09:02:14 +00:00
nodes =
Enum.map(nodes, fn s ->
%{"id" => id, "left" => left, "right" => right} =
Regex.named_captures(
~r/^(?<id>[[:alnum:]]{3}) = \((?<left>[[:alnum:]]{3}), (?<right>[[:alnum:]]{3})\)$/,
s
)
{id, {left, right}}
end)
|> Enum.into(%{})
2023-12-08 09:00:27 +00:00
{instructions, nodes}
end
def reach_end(instructions, nodes) do
Enum.reduce_while(instructions, {@start, 0}, fn instruction, {current_node, step_count} ->
if current_node == @stop do
{:halt, step_count}
else
{left, right} = Map.fetch!(nodes, current_node)
2023-12-08 09:02:14 +00:00
next_node =
case instruction do
?L -> left
?R -> right
end
2023-12-08 09:00:27 +00:00
{:cont, {next_node, step_count + 1}}
end
end)
end
def part1({instructions, nodes}) do
reach_end(instructions, nodes)
end
def find_cycle(instructions, nodes, start) do
2023-12-08 09:02:14 +00:00
Enum.reduce_while(instructions, {start, 0, nil}, fn instruction,
{current_node, step_count, last_end_count} ->
2023-12-08 09:00:27 +00:00
if String.ends_with?(current_node, "Z") and last_end_count != nil do
{:halt, {last_end_count, step_count - last_end_count}}
else
2023-12-08 09:02:14 +00:00
last_end_count =
if String.ends_with?(current_node, "Z"), do: step_count, else: last_end_count
2023-12-08 09:00:27 +00:00
{left, right} = Map.fetch!(nodes, current_node)
2023-12-08 09:02:14 +00:00
next_node =
case instruction do
?L -> left
?R -> right
end
2023-12-08 09:00:27 +00:00
{:cont, {next_node, step_count + 1, last_end_count}}
end
end)
end
def brute_force_find_cycle(cycles) do
2023-12-08 09:02:14 +00:00
Stream.repeatedly(fn -> nil end)
|> Enum.reduce_while(cycles, fn _, cycles ->
2023-12-08 09:00:27 +00:00
all_ended = cycles |> Enum.map(&elem(&1, 0)) |> Enum.uniq() |> length() == 1
if all_ended do
{:halt, cycles |> hd() |> elem(0)}
else
[{count, cycle_count} | tl] = Enum.sort(cycles)
IO.inspect(count)
{:cont, [{count + cycle_count, cycle_count} | tl]}
end
end)
end
def part2({instructions, nodes}) do
IO.puts("Use LCM for the following numbers :)")
2023-12-08 09:02:14 +00:00
2023-12-08 09:00:27 +00:00
Enum.filter(nodes, fn {k, _v} -> String.ends_with?(k, "A") end)
|> Enum.map(fn {k, _v} -> find_cycle(instructions, nodes, k) end)
|> Enum.map(&elem(&1, 0))
|> Enum.map(&Integer.to_string/1)
|> Enum.join(" ")
end
end