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

Day 8

livebook/2023/day_8.livemd

Day 8

Mix.install([
  {:kino_aoc, "~> 0.1"}
])

Input

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2023", "8", System.fetch_env!("LB_AOC_SESSION"))
test_input = """
RL

AAA = (BBB, CCC)
BBB = (DDD, EEE)
CCC = (ZZZ, GGG)
DDD = (DDD, DDD)
EEE = (EEE, EEE)
GGG = (GGG, GGG)
ZZZ = (ZZZ, ZZZ)
"""
"(test)"
|> String.slice(1..-2)

Part 1

defmodule Part1 do
  def parse(input) do
    [steps, nodes] = String.split(input, "\n\n")

    network =
      nodes
      |> String.split("\n", trim: true)
      |> Enum.map(&String.split(&1, " = "))
      |> Enum.map(fn [step, coordites] ->
        [left, right] = coordites |> String.slice(1..-2) |> String.split(", ")
        {step, %{left: left, right: right}}
      end)
      |> Enum.into(%{})

    {String.graphemes(steps), network}
  end

  def run(input) do
    {steps, network} = parse(input)

    Stream.cycle(steps)
    |> Enum.reduce_while({"AAA", 0}, fn move, {pos, step_counter} = _acc ->
      pos = walk(network, pos, move)
      step_counter = step_counter + 1

      if pos == "ZZZ", do: {:halt, step_counter}, else: {:cont, {pos, step_counter}}
    end)
  end

  defp walk(network, pos, "L"), do: network[pos][:left]
  defp walk(network, pos, "R"), do: network[pos][:right]
end

Part1.run(test_input)

Test Part 1

ExUnit.start(autorun: false)

defmodule PartOneTest do
  use ExUnit.Case, async: true

  @input puzzle_input

  @expected 19783

  test "part one" do
    actual = Part1.run(@input)
    assert actual == @expected
  end
end

ExUnit.run()
Part1.run(puzzle_input)