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

AoC 2023 Day 8

2023/day8.livemd

AoC 2023 Day 8

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

Input

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

Parsing

parse_node = fn string ->
  <> <> " = (" <> rest = string
  <> <> ", " <> rest = rest
  <> <> ")" = rest
  {id, {left, right}}
end
[steps, nodes] = String.split(puzzle_input, "\n\n", trim: true)

steps =
  steps
  |> String.codepoints()
  |> Enum.map(fn
    "L" -> 0
    "R" -> 1
  end)

nodes =
  nodes
  |> String.split("\n", trim: true)
  |> Enum.map(parse_node)
  |> Enum.into(%{})

Part 1

start = "AAA"

# Just because otherwise this cell breaks on the p2 example input
try do
  visited =
    steps
    |> Stream.cycle()
    |> Stream.scan(start, fn step, node ->
      nodes[node] |> elem(step)
    end)
    |> Stream.take_while(&amp;(&amp;1 != "ZZZ"))
    |> Enum.count()

  visited + 1
rescue
  _ -> :err
end

Part 2

steps = Stream.cycle(steps)

count =
  Map.keys(nodes)
  |> Enum.filter(&amp;match?(<<_, _, "A">>, &amp;1))
  |> Enum.map(fn start ->
    Stream.scan(steps, start, fn step, node ->
      nodes[node] |> elem(step)
    end)
  end)
  |> Stream.zip()
  |> Stream.take_while(fn set ->
    set
    |> Tuple.to_list()
    |> Enum.all?(&amp;match?(<<_, _, "Z">>, &amp;1))
    |> Kernel.not()
  end)
  |> Enum.count()

count + 1