Powered by AppSignal & Oban Pro

Day 8

notebooks/day08.livemd

Day 8

Mix.install([
  {:req, "~> 0.4.5"},
  {:kino, "~> 0.11.3"}
])

Input

session = System.fetch_env!("LB_AOC_SESSION")

input =
  Req.get!(
    "https://adventofcode.com/2023/day/8/input",
    headers: [{"Cookie", ~s"session=#{session}"}]
  ).body

Kino.Text.new(input, terminal: true)

Part 1

[instructions, nodes] = String.split(input, "\n\n")

nodes =
  for line <- String.split(nodes, "\n", trim: true), into: Map.new() do
    [head | rest] = String.split(line, ~r/[ =\(\),]/, trim: true)
    {head, rest}
  end

defmodule Part1 do
  def solve(instructions, nodes, initial, fun) do
    String.graphemes(instructions)
    |> Stream.cycle()
    |> Enum.reduce_while({initial, 0}, fn instruction, {node, count} ->
      if fun.(node) do
        {:halt, count}
      else
        neighbors = nodes[node]

        case instruction do
          "L" -> {:cont, {hd(neighbors), count + 1}}
          "R" -> {:cont, {hd(tl(neighbors)), count + 1}}
        end
      end
    end)
  end
end

Part1.solve(instructions, nodes, "AAA", &amp;(&amp;1 == "ZZZ"))

Part 2

starting_nodes =
  nodes
  |> Map.keys()
  |> Enum.filter(&amp;String.ends_with?(&amp;1, "A"))

lcm = fn a, b -> trunc(abs(a * b) / Integer.gcd(a, b)) end

for initial <- starting_nodes do
  Part1.solve(instructions, nodes, initial, &amp;String.ends_with?(&amp;1, "Z"))
end
|> Enum.reduce(lcm)