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

Day 8

2023/day_8.livemd

Day 8

Mix.install([:kino])

Get input

input =
  Kino.Input.textarea("Paste input here",
    default: """
    RL

    AAA = (BBB, CCC)
    BBB = (DDD, EEE)
    CCC = (ZZZ, GGG)
    DDD = (DDD, DDD)
    EEE = (EEE, EEE)
    GGG = (GGG, GGG)
    ZZZ = (ZZZ, ZZZ)
    """
  )
{instructions, network} =
  Kino.Input.read(input)
  |> String.split("\n\n", trim: true)
  |> then(fn [instructions, network] ->
    n =
      network
      |> String.split("\n", trim: true)
      |> Enum.map(&String.split(&1, " = "))
      |> Enum.map(fn [target, dests] ->
        [_, l, r] = Regex.run(~r/\(([A-Z0-9]+)\,\s([A-Z0-9]+)\)/, dests)
        {target, {l, r}}
      end)
      |> Enum.into(%{})

    {String.split(instructions, "", trim: true), n}
  end)

Part 1

defmodule Part1 do
  def ride(_instructions, _network, step, "ZZZ") do
    step
  end

  def ride(instructions, network, step, node) do
    {l, r} = network[node]
    ins_idx = rem(step, Enum.count(instructions))

    case Enum.at(instructions, ins_idx) do
      "R" -> ride(instructions, network, step + 1, r)
      "L" -> ride(instructions, network, step + 1, l)
    end
  end
end

Part1.ride(instructions, network, 0, "AAA")

Part 2

defmodule Part2 do
  def find_steps_to_end(instructions, network, step, node) do
    ins_idx = rem(step, Enum.count(instructions))
    {l, r} = network[node]

    next_node =
      case Enum.at(instructions, ins_idx) do
        "R" -> r
        "L" -> l
      end

    if String.last(next_node) === "Z" do
      step + 1
    else
      find_steps_to_end(instructions, network, step + 1, next_node)
    end
  end
end

defmodule Math do
  def gcd(a, 0), do: a
  def gcd(a, b), do: gcd(b, rem(a, b))

  def lcm(a, b), do: div(abs(a * b), gcd(a, b))

  def lcm_list([]), do: 1

  def lcm_list([h | t]) do
    Enum.reduce(t, h, &lcm/2)
  end
end

starting_nodes =
  network
  |> Map.keys()
  |> Enum.filter(fn
    node -> String.last(node) === "A"
  end)

steps_to_finish =
  starting_nodes
  |> Enum.map(fn node ->
    Part2.find_steps_to_end(instructions, network, 0, node)
  end)

Math.lcm_list(steps_to_finish)