Powered by AppSignal & Oban Pro

Day 14: Regolith Reservoir

Day%2014:%20Regolith%20Reservoir.livemd

Day 14: Regolith Reservoir

Section

input = """
512,122 -> 512,118 -> 512,122 -> 514,122 -> 514,120 -> 514,122 -> 516,122 -> 516,121 -> 516,122
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
516,125 -> 516,129 -> 515,129 -> 515,136 -> 526,136 -> 526,129 -> 519,129 -> 519,125
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
512,122 -> 512,118 -> 512,122 -> 514,122 -> 514,120 -> 514,122 -> 516,122 -> 516,121 -> 516,122
511,86 -> 515,86
485,22 -> 489,22
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
509,150 -> 513,150
516,65 -> 516,69 -> 508,69 -> 508,74 -> 521,74 -> 521,69 -> 520,69 -> 520,65
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
494,157 -> 494,159 -> 491,159 -> 491,167 -> 503,167 -> 503,159 -> 499,159 -> 499,157
494,157 -> 494,159 -> 491,159 -> 491,167 -> 503,167 -> 503,159 -> 499,159 -> 499,157
512,139 -> 512,142 -> 509,142 -> 509,145 -> 521,145 -> 521,142 -> 517,142 -> 517,139
512,139 -> 512,142 -> 509,142 -> 509,145 -> 521,145 -> 521,142 -> 517,142 -> 517,139
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
494,24 -> 498,24
516,65 -> 516,69 -> 508,69 -> 508,74 -> 521,74 -> 521,69 -> 520,69 -> 520,65
494,157 -> 494,159 -> 491,159 -> 491,167 -> 503,167 -> 503,159 -> 499,159 -> 499,157
497,154 -> 501,154
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
523,80 -> 527,80
512,139 -> 512,142 -> 509,142 -> 509,145 -> 521,145 -> 521,142 -> 517,142 -> 517,139
505,17 -> 510,17
510,104 -> 510,96 -> 510,104 -> 512,104 -> 512,101 -> 512,104 -> 514,104 -> 514,100 -> 514,104 -> 516,104 -> 516,103 -> 516,104
498,17 -> 503,17
512,122 -> 512,118 -> 512,122 -> 514,122 -> 514,120 -> 514,122 -> 516,122 -> 516,121 -> 516,122
512,139 -> 512,142 -> 509,142 -> 509,145 -> 521,145 -> 521,142 -> 517,142 -> 517,139
494,157 -> 494,159 -> 491,159 -> 491,167 -> 503,167 -> 503,159 -> 499,159 -> 499,157
516,125 -> 516,129 -> 515,129 -> 515,136 -> 526,136 -> 526,129 -> 519,129 -> 519,125
510,104 -> 510,96 -> 510,104 -> 512,104 -> 512,101 -> 512,104 -> 514,104 -> 514,100 -> 514,104 -> 516,104 -> 516,103 -> 516,104
516,125 -> 516,129 -> 515,129 -> 515,136 -> 526,136 -> 526,129 -> 519,129 -> 519,125
509,31 -> 509,32 -> 513,32 -> 513,31
504,90 -> 504,91 -> 513,91 -> 513,90
493,48 -> 493,49 -> 512,49 -> 512,48
479,26 -> 483,26
488,24 -> 492,24
512,152 -> 516,152
491,26 -> 495,26
493,48 -> 493,49 -> 512,49 -> 512,48
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
512,122 -> 512,118 -> 512,122 -> 514,122 -> 514,120 -> 514,122 -> 516,122 -> 516,121 -> 516,122
517,80 -> 521,80
510,104 -> 510,96 -> 510,104 -> 512,104 -> 512,101 -> 512,104 -> 514,104 -> 514,100 -> 514,104 -> 516,104 -> 516,103 -> 516,104
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
494,157 -> 494,159 -> 491,159 -> 491,167 -> 503,167 -> 503,159 -> 499,159 -> 499,157
514,83 -> 518,83
510,104 -> 510,96 -> 510,104 -> 512,104 -> 512,101 -> 512,104 -> 514,104 -> 514,100 -> 514,104 -> 516,104 -> 516,103 -> 516,104
510,104 -> 510,96 -> 510,104 -> 512,104 -> 512,101 -> 512,104 -> 514,104 -> 514,100 -> 514,104 -> 516,104 -> 516,103 -> 516,104
529,86 -> 533,86
526,83 -> 530,83
497,13 -> 502,13
516,108 -> 516,109 -> 520,109 -> 520,108
512,139 -> 512,142 -> 509,142 -> 509,145 -> 521,145 -> 521,142 -> 517,142 -> 517,139
516,125 -> 516,129 -> 515,129 -> 515,136 -> 526,136 -> 526,129 -> 519,129 -> 519,125
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
503,154 -> 507,154
494,15 -> 499,15
520,77 -> 524,77
506,152 -> 510,152
516,65 -> 516,69 -> 508,69 -> 508,74 -> 521,74 -> 521,69 -> 520,69 -> 520,65
516,65 -> 516,69 -> 508,69 -> 508,74 -> 521,74 -> 521,69 -> 520,69 -> 520,65
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
494,157 -> 494,159 -> 491,159 -> 491,167 -> 503,167 -> 503,159 -> 499,159 -> 499,157
512,139 -> 512,142 -> 509,142 -> 509,145 -> 521,145 -> 521,142 -> 517,142 -> 517,139
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
493,48 -> 493,49 -> 512,49 -> 512,48
510,104 -> 510,96 -> 510,104 -> 512,104 -> 512,101 -> 512,104 -> 514,104 -> 514,100 -> 514,104 -> 516,104 -> 516,103 -> 516,104
516,108 -> 516,109 -> 520,109 -> 520,108
512,122 -> 512,118 -> 512,122 -> 514,122 -> 514,120 -> 514,122 -> 516,122 -> 516,121 -> 516,122
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
488,20 -> 492,20
500,152 -> 504,152
509,31 -> 509,32 -> 513,32 -> 513,31
509,154 -> 513,154
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
516,125 -> 516,129 -> 515,129 -> 515,136 -> 526,136 -> 526,129 -> 519,129 -> 519,125
523,86 -> 527,86
516,125 -> 516,129 -> 515,129 -> 515,136 -> 526,136 -> 526,129 -> 519,129 -> 519,125
491,22 -> 495,22
497,26 -> 501,26
506,148 -> 510,148
516,65 -> 516,69 -> 508,69 -> 508,74 -> 521,74 -> 521,69 -> 520,69 -> 520,65
512,122 -> 512,118 -> 512,122 -> 514,122 -> 514,120 -> 514,122 -> 516,122 -> 516,121 -> 516,122
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
510,104 -> 510,96 -> 510,104 -> 512,104 -> 512,101 -> 512,104 -> 514,104 -> 514,100 -> 514,104 -> 516,104 -> 516,103 -> 516,104
510,104 -> 510,96 -> 510,104 -> 512,104 -> 512,101 -> 512,104 -> 514,104 -> 514,100 -> 514,104 -> 516,104 -> 516,103 -> 516,104
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
494,157 -> 494,159 -> 491,159 -> 491,167 -> 503,167 -> 503,159 -> 499,159 -> 499,157
516,108 -> 516,109 -> 520,109 -> 520,108
501,15 -> 506,15
516,125 -> 516,129 -> 515,129 -> 515,136 -> 526,136 -> 526,129 -> 519,129 -> 519,125
491,17 -> 496,17
516,65 -> 516,69 -> 508,69 -> 508,74 -> 521,74 -> 521,69 -> 520,69 -> 520,65
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
512,122 -> 512,118 -> 512,122 -> 514,122 -> 514,120 -> 514,122 -> 516,122 -> 516,121 -> 516,122
512,139 -> 512,142 -> 509,142 -> 509,145 -> 521,145 -> 521,142 -> 517,142 -> 517,139
504,90 -> 504,91 -> 513,91 -> 513,90
503,45 -> 503,41 -> 503,45 -> 505,45 -> 505,43 -> 505,45 -> 507,45 -> 507,37 -> 507,45 -> 509,45 -> 509,35 -> 509,45 -> 511,45 -> 511,37 -> 511,45
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
482,24 -> 486,24
510,104 -> 510,96 -> 510,104 -> 512,104 -> 512,101 -> 512,104 -> 514,104 -> 514,100 -> 514,104 -> 516,104 -> 516,103 -> 516,104
509,31 -> 509,32 -> 513,32 -> 513,31
516,65 -> 516,69 -> 508,69 -> 508,74 -> 521,74 -> 521,69 -> 520,69 -> 520,65
512,122 -> 512,118 -> 512,122 -> 514,122 -> 514,120 -> 514,122 -> 516,122 -> 516,121 -> 516,122
515,154 -> 519,154
517,86 -> 521,86
485,26 -> 489,26
504,90 -> 504,91 -> 513,91 -> 513,90
510,104 -> 510,96 -> 510,104 -> 512,104 -> 512,101 -> 512,104 -> 514,104 -> 514,100 -> 514,104 -> 516,104 -> 516,103 -> 516,104
510,104 -> 510,96 -> 510,104 -> 512,104 -> 512,101 -> 512,104 -> 514,104 -> 514,100 -> 514,104 -> 516,104 -> 516,103 -> 516,104
503,150 -> 507,150
520,83 -> 524,83
508,62 -> 508,56 -> 508,62 -> 510,62 -> 510,52 -> 510,62 -> 512,62 -> 512,60 -> 512,62 -> 514,62 -> 514,58 -> 514,62 -> 516,62 -> 516,55 -> 516,62
"""
# {x, y}
# {0, 0} {1, 0}
# {0, 1} {1, 1}

defmodule D14 do
  # 初始的 grid, [{x, y}, ...] 放入牆壁們

  ## 每回合算出流沙的落點並加進去 grid
  ## guard 流沙的 y 超過 grid 最底層
  ## guard 流沙固定位置 -> 寫入 grid
  ## 繼續滾動
  def falling(grid, count, lowest_y) do
    case rest_coordinate(grid, lowest_y) do
      {:rest, sand} -> falling([sand | grid], count + 1, lowest_y)
      {:falling_into_abyss} -> count
    end
  end

  def rest_coordinate(grid, lowest_y) do
    tick(grid, {500, 0}, lowest_y)
  end

  def tick(_grid, {_x, y} = _sand, lowest_y) when lowest_y < y, do: {:falling_into_abyss}

  def tick(grid, {x, y}, lowest_y) do
    cond do
      moveable?({x, y + 1}, grid) -> tick(grid, {x, y + 1}, lowest_y)
      moveable?({x - 1, y + 1}, grid) -> tick(grid, {x - 1, y + 1}, lowest_y)
      moveable?({x + 1, y + 1}, grid) -> tick(grid, {x + 1, y + 1}, lowest_y)
      true -> {:rest, {x, y}}
    end
  end

  def moveable?(coord, grid) do
    !Enum.find(grid, &amp;(&amp;1 == coord))
  end

  def lowest_y(grid) do
    grid |> Enum.max_by(&amp;elem(&amp;1, 1)) |> elem(1)
  end

  def build_grid(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&amp;rock_line/1)
    |> List.flatten()
    |> Enum.uniq()
  end

  def rock_line(rock_corners) do
    rock_corners
    |> String.split(" -> ", trim: true)
    |> Enum.map(&amp;to_coordinate/1)
    |> Enum.chunk_every(2, 1, :discard)
    |> Enum.map(&amp;fill_rocks/1)
  end

  def to_coordinate(raw_coordinate) do
    raw_coordinate
    |> String.split(",")
    |> Enum.map(&amp;String.to_integer/1)
    |> List.to_tuple()
  end

  def fill_rocks([{x, c1y}, {x, c2y}]), do: for(y <- c1y..c2y, do: {x, y})
  def fill_rocks([{c1x, y}, {c2x, y}]), do: for(x <- c1x..c2x, do: {x, y})
end

# grid = D14.build_grid(input)
# lowest_y = D14.lowest_y(grid)
# D14.falling(grid, 0, lowest_y)
defmodule D14B do
  def falling(grid, count, floor) do
    case rest_coordinate(grid, floor) do
      {:rest, {500, 0}} -> count
      {:rest, sand} -> falling([sand | grid], count + 1, floor)
    end
  end

  def rest_coordinate(grid, floor) do
    tick(grid, {500, 0}, floor)
  end

  def tick(grid, {x, y}, floor) do
    cond do
      moveable?({x, y + 1}, grid, floor) -> tick(grid, {x, y + 1}, floor)
      moveable?({x - 1, y + 1}, grid, floor) -> tick(grid, {x - 1, y + 1}, floor)
      moveable?({x + 1, y + 1}, grid, floor) -> tick(grid, {x + 1, y + 1}, floor)
      true -> {:rest, {x, y} |> IO.inspect()}
    end
  end

  def moveable?({_x, y}, _, floor) when not y == floor, do: false

  def moveable?(coord, grid, _floor) do
    !Enum.find(grid, &amp;(&amp;1 == coord))
  end

  def floor(grid) do
    grid |> Enum.max_by(&amp;elem(&amp;1, 1)) |> elem(1) |> Kernel.+(2)
  end

  def build_grid(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&amp;rock_line/1)
    |> List.flatten()
    |> Enum.uniq()
  end

  def rock_line(rock_corners) do
    rock_corners
    |> String.split(" -> ", trim: true)
    |> Enum.map(&amp;to_coordinate/1)
    |> Enum.chunk_every(2, 1, :discard)
    |> Enum.map(&amp;fill_rocks/1)
  end

  def to_coordinate(raw_coordinate) do
    raw_coordinate
    |> String.split(",")
    |> Enum.map(&amp;String.to_integer/1)
    |> List.to_tuple()
  end

  def fill_rocks([{x, c1y}, {x, c2y}]), do: for(y <- c1y..c2y, do: {x, y})
  def fill_rocks([{c1x, y}, {c2x, y}]), do: for(x <- c1x..c2x, do: {x, y})
end

# grid = D14B.build_grid(input)
# floor = D14B.floor(grid)
# D14B.falling(grid, 0, floor)
defmodule D14B2 do
  def falling(grid, count, floor) do
    case rest_coordinate(grid, floor) do
      {:rest, {500, 0}} ->
        count

      {:rest, {_x, y} = sand} ->
        if y > floor - 5 do
          IO.inspect(sand)
        end

        falling(Map.put(grid, sand, true), count + 1, floor)
    end
  end

  def rest_coordinate(grid, floor) do
    tick(grid, {500, 0}, floor)
  end

  def tick(grid, {x, y}, floor) do
    cond do
      moveable?({x, y + 1}, grid, floor) -> tick(grid, {x, y + 1}, floor)
      moveable?({x - 1, y + 1}, grid, floor) -> tick(grid, {x - 1, y + 1}, floor)
      moveable?({x + 1, y + 1}, grid, floor) -> tick(grid, {x + 1, y + 1}, floor)
      true -> {:rest, {x, y}}
    end
  end

  def moveable?({_x, y}, _, floor) when not y == floor, do: false

  def moveable?(coord, grid, _floor) do
    not Map.has_key?(grid, coord)
  end

  def floor(grid) do
    grid |> Map.keys() |> Enum.max_by(&amp;elem(&amp;1, 1)) |> elem(1) |> Kernel.+(2)
  end

  def build_grid(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&amp;rock_line/1)
    |> List.flatten()
    |> Enum.uniq()
    |> Enum.into(%{}, &amp;{&amp;1, true})
  end

  def rock_line(rock_corners) do
    rock_corners
    |> String.split(" -> ", trim: true)
    |> Enum.map(&amp;to_coordinate/1)
    |> Enum.chunk_every(2, 1, :discard)
    |> Enum.map(&amp;fill_rocks/1)
  end

  def to_coordinate(raw_coordinate) do
    raw_coordinate
    |> String.split(",")
    |> Enum.map(&amp;String.to_integer/1)
    |> List.to_tuple()
  end

  def fill_rocks([{x, c1y}, {x, c2y}]), do: for(y <- c1y..c2y, do: {x, y})
  def fill_rocks([{c1x, y}, {c2x, y}]), do: for(x <- c1x..c2x, do: {x, y})
end

grid = D14B2.build_grid(input)
# floor = D14B2.floor(grid)
# D14B2.falling(grid, 0, floor)