Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day8

2023/elixir/day8.livemd

Day8

Mix.install([
  {:kino_aoc, git: "https://github.com/ljgago/kino_aoc"},
  {:benchee, "~> 1.0"},
  {:nimble_parsec, "~> 1.0"},
  {:libgraph, "~> 0.16.0"},
  {:math, "~> 0.7.0"}
])

Get Input

{:ok, data} = KinoAOC.download_puzzle("2023", "8", System.fetch_env!("LB_AOC_SECRET"))

Solve

defmodule Day8 do
  @instr %{L: 0, R: 1}

  def out(res, t), do: IO.puts("Res #{t}: #{res}")

  def run(data, part) do
    data
    |> String.split("\n", trim: true)
    |> parse()
    |> solve(part)
  end

  def parse([instr | net]) do
    i = instr |> String.graphemes() |> Enum.map(&String.to_existing_atom/1)
    n = Enum.into(net, %{}, &build_node/1)
    {i, n}
  end

  def build_node(row) do
    {n, l, r} = Regex.scan(~r/[1-9A-Z]+/, row) |> List.flatten() |> List.to_tuple()
    {n, {l, r}}
  end

  def solve(data, :p1) do
    find_end("AAA", data, :p1)
  end

  def solve({_iter, nodes} = data, :p2) do
    nodes
    |> get_start()
    |> Enum.map(fn st -> find_end(st, data, :p2) end)
    |> Enum.reduce(1, &Math.lcm/2)
  end

  def get_start(nodes) do
    nodes
    |> Enum.filter(fn {k, _v} -> match?(<<_, _, "A">>, k) end)
    |> Enum.map(fn {k, _v} -> k end)
  end

  def find_end(st_node, {iter, nodes}, part) do
    iter
    |> Stream.cycle()
    |> Enum.reduce_while({st_node, 0}, fn move, {node, cnt} ->
      nxt = nodes[node] |> elem(@instr[move])
      end_check(nxt, node, cnt, part)
    end)
  end

  def end_check(_nxt, "ZZZ", cnt, :p1), do: {:halt, cnt}
  def end_check(_nxt, <<_, _, "Z">>, cnt, :p2), do: {:halt, cnt}
  def end_check(nxt, _, cnt, _), do: {:cont, {nxt, cnt + 1}}
end

Check

ExUnit.start(autorun: false)

defmodule Day8.Test do
  use ExUnit.Case, async: false

  @td1 """
  LLR

  AAA = (BBB, BBB)
  BBB = (AAA, ZZZ)
  ZZZ = (ZZZ, ZZZ)
  """

  @td2 """
  LR

  11A = (11B, XXX)
  11B = (XXX, 11Z)
  11Z = (11B, XXX)
  22A = (22B, XXX)
  22B = (22C, 22C)
  22C = (22Z, 22Z)
  22Z = (22B, 22B)
  XXX = (XXX, XXX)
  """

  setup do
    {:ok, data} = KinoAOC.download_puzzle("2023", "8", System.fetch_env!("LB_AOC_SECRET"))
    %{data: data}
  end

  test "solves test cases" do
    assert Day8.run(@td1, :p1) == 6
    assert Day8.run(@td2, :p2) == 6
  end

  test "solves live cases", %{data: data} do
    assert Day8.run(data, :p1) == 18023
    assert Day8.run(data, :p2) == 14_449_445_933_179
  end
end

ExUnit.run()

data |> Day8.run(:p1) |> Day8.out("p1")
data |> Day8.run(:p2) |> Day8.out("p2")