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

Day 14

2022/day14.livemd

Day 14

Mix.install([{:kino, "~>0.8.0"}, {:vega_lite, "~> 0.1.6"}, {:kino_vega_lite, "~> 0.1.7"}])
alias VegaLite, as: Vl

Setup

input = Kino.Input.textarea("Input file")

Common

defmodule Day14 do
  def fall(state, bottom, {x, y} = sand) do
    if MapSet.member?(state, {500, 0}) do
      nil
    else
      air = Enum.find([{x, y + 1}, {x - 1, y + 1}, {x + 1, y + 1}], &(!MapSet.member?(state, &1)))

      case air do
        nil -> {state, MapSet.put(state, sand)}
        {_, y} when y > bottom -> nil
        _ -> fall(state, bottom, air)
      end
    end
  end
end

Part 1

# Get all points where there are rocks
rock_map =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.flat_map(fn line ->
    line
    |> String.split(~r/\D+/, trim: true)
    |> Enum.map(&String.to_integer/1)
    |> Enum.chunk_every(2)
    |> Enum.chunk_every(2, 1, :discard)
    |> Enum.flat_map(fn [[a, b], [c, d]] ->
      for x <- a..c, y <- b..d, do: {x, y}
    end)
  end)
  |> MapSet.new()

# Sand pouring from {500, 0}
sand_point = {500, 0}

bottom = Enum.max_by(rock_map, &amp;elem(&amp;1, 1)) |> elem(1)
Stream.unfold(rock_map, &amp;Day14.fall(&amp;1, bottom, sand_point)) |> Enum.to_list() |> Enum.count()

Part 2

rock_map =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.flat_map(fn line ->
    line
    |> String.split(~r/\D+/, trim: true)
    |> Enum.map(&amp;String.to_integer/1)
    |> Enum.chunk_every(2)
    |> Enum.chunk_every(2, 1, :discard)
    |> Enum.flat_map(fn [[a, b], [c, d]] ->
      for x <- a..c, y <- b..d, do: {x, y}
    end)
  end)
  |> MapSet.new()

# Sand pouring from {500, 0}
sand_point = {500, 0}

bottom =
  rock_map
  |> Enum.max_by(fn {_, y} -> y end)
  |> then(fn {_, y} -> y end)
  # Get position of bottom
  |> then(fn val -> val + 2 end)

state =
  (-bottom - 1)..(bottom + 1)
  |> Enum.map(fn y ->
    {500 + y, bottom}
  end)
  |> Enum.into(rock_map)

Stream.unfold(state, &amp;Day14.fall(&amp;1, bottom, {500, 0})) |> Enum.count()