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

Advent of Code 2023 - Day 14

2023/14.livemd

Advent of Code 2023 - Day 14

Mix.install([
  {:kino, "~> 0.11.0"},
  {:kino_aoc, github: "ljgago/kino_aoc"}
])

Input

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2023", "14", System.fetch_env!("LB_AOC_SESSION"))
input =
  puzzle_input
  |> String.split("\n", trim: true)
  |> Enum.map(&String.graphemes/1)

Part 1

transpose = fn matrix ->
  Enum.zip_with(matrix, &Function.identity/1)
end

tilt = fn dish ->
  dish
  |> transpose.()
  |> Enum.map(fn row ->
    row
    |> Enum.chunk_by(&(&1 == "#"))
    |> Enum.map(&Enum.sort(&1, :desc))
    |> List.flatten()
  end)
  |> transpose.()
end

total_load = fn dish ->
  dish
  |> Enum.reverse()
  |> Stream.with_index(1)
  |> Stream.map(fn {row, i} ->
    Enum.count(row, &(&1 == "O")) * i
  end)
  |> Enum.sum()
end

input
|> tilt.()
|> total_load.()

Part 2

rotate = fn dish ->
  dish
  |> Enum.reverse()
  |> transpose.()
end

cycle = fn dish ->
  Enum.reduce([:north, :west, :south, :east], dish, fn _, acc ->
    acc
    |> tilt.()
    |> rotate.()
  end)
end

total = 1_000_000_000

1..total
|> Enum.reduce_while(input, fn i, dish ->
  current = cycle.(dish)
  hash = :erlang.phash2(current)

  case Process.get(hash) do
    nil ->
      {:cont, tap(current, fn _ -> Process.put(hash, i) end)}

    imem ->
      remaining = total - imem
      idiff = imem - i
      loops = div(remaining, idiff)
      {:halt, {current, total - imem - 1 - loops * idiff}}
  end
end)
|> then(fn {dish, n} ->
  Enum.reduce(0..n, dish, fn _, acc -> cycle.(acc) end)
end)
|> total_load.()

Run in Livebook