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

Day Seventeen

day17.livemd

Day Seventeen

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

Section

input = Kino.Input.textarea("Input")
defmodule DaySeventeen do
  @rocks [
    [{2, 0}, {3, 0}, {4, 0}, {5, 0}],
    [{3, -2}, {2, -1}, {3, -1}, {4, -1}, {3, 0}],
    [{4, -2}, {4, -1}, {2, 0}, {3, 0}, {4, 0}],
    [{2, -3}, {2, -2}, {2, -1}, {2, 0}],
    [{2, -1}, {3, -1}, {2, 0}, {3, 0}]
  ]

  def solve1(input) do
    all_jets =
      input
      |> String.to_charlist()

    @rocks
    |> Stream.cycle()
    |> Stream.take(1_000_000_000_000)
    |> Stream.with_index()
    |> Enum.reduce({all_jets, MapSet.new(), all_jets}, fn {rock, i}, {jets, occupied, all_jets} ->
      #      if MapSet.size(occupied) > 0, do: draw(occupied)
      if jets == [] || i == 17444 + 1016 do
        IO.inspect(height(occupied), label: "height")
        IO.inspect(i, label: "rocks")
      end

      highest_y = highest_y(occupied)
      rock = Enum.map(rock, fn {x, y} -> {x, highest_y + y - 4} end)
      place(rock, jets, occupied, all_jets, 0)
    end)
    |> then(fn {_, occupied, _} ->
      ys = Enum.map(occupied, &elem(&1, 1))
      Enum.max(ys) - Enum.min(ys) + 1
    end)
  end

  defp solve2(input) do
    iters = 1_000_000_000_000
    floor(iters / (7049 - 7014)) * (10678 - 10625)
  end

  defp height(occupied) do
    ys = Enum.map(occupied, &elem(&1, 1))
    Enum.max(ys) - Enum.min(ys) + 1
  end

  defp highest_y(occupied) do
    if MapSet.size(occupied) == 0 do
      4
    else
      occupied |> Enum.min_by(&elem(&1, 1)) |> elem(1)
    end
  end

  defp place(rock, [], occupied, all_jets, c) do
    place(rock, all_jets, occupied, all_jets, c)
  end

  defp place(rock, [jet | jets], occupied, all_jets, c) do
    case rock
         |> push(jet, occupied)
         |> drop(occupied) do
      {:halt, rock} ->
        {
          jets,
          Enum.reduce(rock, occupied, fn pt, acc -> MapSet.put(acc, pt) end),
          all_jets
        }

      {:cont, rock} ->
        place(rock, jets, occupied, all_jets, c + 1)
    end
  end

  defp push(rock, dir, occupied) do
    new_rock = Enum.map(rock, fn {x, y} -> {x + if(dir == ?>, do: 1, else: -1), y} end)

    cond do
      Enum.any?(new_rock, fn {x, _y} -> x < 0 || x > 6 end) ->
        rock

      Enum.any?(new_rock, fn pt -> MapSet.member?(occupied, pt) end) ->
        rock

      true ->
        new_rock
    end
  end

  defp drop(rock, occupied) do
    new_rock = Enum.map(rock, fn {x, y} -> {x, y + 1} end)

    cond do
      Enum.any?(new_rock, fn {_x, y} -> y > 3 end) ->
        {:halt, rock}

      Enum.any?(new_rock, fn pt -> MapSet.member?(occupied, pt) end) ->
        {:halt, rock}

      true ->
        {:cont, new_rock}
    end
  end

  defp draw(occupied) do
    min_y = Enum.map(occupied, &amp;elem(&amp;1, 1)) |> Enum.min()

    Enum.each(min_y..3, fn y ->
      Enum.each(0..7, fn x ->
        if MapSet.member?(occupied, {x, y}) do
          IO.write("#")
        else
          IO.write(".")
        end
      end)

      IO.puts("")
    end)

    IO.puts("")
    IO.puts("")
  end
end

DaySeventeen.solve1(Kino.Input.read(input))
# Math for part 2
48 rocks: 78
83 rocks: 131

78 + div(1_000_000_000_000-48, 83-48)*(131-78)

height: 2736
rocks: 1739
height: 5486
rocks: 3484

rocks_to_repeat: 1745
diff: 2750
27486 + div(1_000_000_000_000-17444, 1745)*(2750) + 1590
# 1575931231646
# 1575931232076