Powered by AppSignal & Oban Pro

Advent of code day 14

2023/livebooks/day-14.livemd

Advent of code day 14

Mix.install([
  {:kino, "~> 0.5.0"}
])

Setup input

example = Kino.Input.textarea("Please paste your input example:")
input = Kino.Input.textarea("Please paste your real input:")
grid = example
|> Kino.Input.read()
|> String.split("\n", trim: true)
|> Enum.map(&String.split(&1,"", trim: true))

Part 01

grid
|> Enum.zip_with(&Function.identity/1)
|> Enum.map(fn col ->
  col
  |> Enum.join()
  |> String.split("#")
  |> Enum.map(fn group ->
    group
    |> String.graphemes()
    |> Enum.sort(:desc)
    |> Enum.join()
  end)
  |> Enum.join("#")
end)
|> Enum.map(&String.split(&1, "", trim: true))
|> Enum.zip_with(&Function.identity/1)
|> Enum.with_index(fn row, idx ->
  rocks = Enum.count(row, &(&1 == "O"))
  rocks * (length(grid) - idx)
end)
|> Enum.sum()

Part 02

cycle = fn grid ->
  Enum.reduce(1..4, grid, fn _, grid ->
    grid
    |> Enum.zip_with(&Function.identity/1)
    |> Enum.map(fn col ->
      col
      |> Enum.join()
      |> String.split("#")
      |> Enum.map(fn group ->
        group
        |> String.graphemes()
        |> Enum.sort(:desc)
        |> Enum.join()
      end)
      |> Enum.join("#")
    end)
    |> Enum.map(&(String.split(&1, "", trim: true) |> Enum.reverse()))
  end)
end

seen = MapSet.new([grid])
states = [grid]

{iter, states, grid} =
  Stream.iterate(1, &(&1 + 1))
  |> Enum.reduce_while({seen, states, grid}, fn iter, {seen, states, grid} ->
    grid = cycle.(grid)
    if MapSet.member?(seen, grid) do
      {:halt, {iter, states, grid}}
    else
      seen = MapSet.put(seen, grid)
      states = states ++ [grid]
      {:cont, {seen, states, grid}}
    end
  end)

first = Enum.find_index(states, &Kernel.==(&1, grid))

grid = Enum.at(states, rem(1_000_000_000 - first, iter - first) + first)

grid
|> Enum.with_index(fn row, idx ->
  rocks = Enum.count(row, &(&1 == "O"))
  rocks * (length(grid) - idx)
end)
|> Enum.sum()