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

Advent of Code 2022 - Day 14

2022/day14.livemd

Advent of Code 2022 - Day 14

Mix.install([
  :kino,
  {:kino_aoc, git: "https://github.com/ljgago/kino_aoc"}
])

Input

test_input = """
498,4 -> 498,6 -> 496,6
503,4 -> 502,4 -> 502,9 -> 494,9
"""
{:ok, puzzle_input} = KinoAOC.download_puzzle("2022", "14", System.fetch_env!("LB_AOC_SESSION"))
input_field =
  Kino.Input.select("input", [
    {test_input, "test_input"},
    {puzzle_input, "puzzle_input"}
  ])

Parse & Prep

lines =
  input_field
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.flat_map(fn path ->
    path
    |> String.split(["-", ">", ",", " "], trim: true)
    |> Enum.map(&String.to_integer/1)
    |> Enum.chunk_every(4, 2, :discard)
  end)
  |> Enum.uniq()

columns =
  for(
    [startx, starty, endx, endy] <- lines,
    x <- startx..endx,
    y <- starty..endy,
    reduce: %{},
    do: (acc -> Map.update(acc, x, [y], &amp;[y | &amp;1]))
  )
  |> Map.new(fn {x, y} ->
    {x, y |> Enum.sort() |> Enum.uniq()}
  end)

Part 1

defmodule Simulator do
  def fall(columns, floor, x, y) do
    col = Map.get(columns, x)
    {lower, higher} = Enum.split_while(List.wrap(col), &amp;(&amp;1 < y))

    cond do
      higher == [] and floor -> Map.put(columns, x, lower ++ [floor])
      higher == [] -> :halt
      true -> do_fall(columns, floor, lower, higher, x, y)
    end
  end

  defp do_fall(columns, floor, lower, [next_y | higher], x, y) do
    case max(y, next_y) do
      ^y ->
        :nope

      new_y ->
        left = fall(columns, floor, x - 1, new_y)
        right = fall(columns, floor, x + 1, new_y)

        cond do
          left == :nope and right == :nope ->
            Map.put(columns, x, lower ++ [new_y - 1, next_y | higher])

          left == :nope ->
            right

          true ->
            left
        end
    end
  end
end
Stream.unfold(0, fn x -> {x, x + 1} end)
|> Enum.reduce_while(columns, fn
  x, acc ->
    case Simulator.fall(acc, nil, 500, 0) do
      :halt -> {:halt, x}
      next_acc -> {:cont, next_acc}
    end
end)

Part 2

floor =
  columns
  |> Map.values()
  |> List.flatten()
  |> Enum.max()
  |> then(&amp;Kernel.+(&amp;1, 1))

Stream.unfold(0, fn x -> {x, x + 1} end)
|> Enum.reduce_while(columns, fn
  x, acc ->
    case Simulator.fall(acc, floor, 500, 0) do
      :nope -> {:halt, x}
      next_acc -> {:cont, next_acc}
    end
end)