this repo has no description

ft: add day 14.2022

+189
+189
2022/day14.livemd
··· 1 + <!-- livebook:{"persist_outputs":true} --> 2 + 3 + # Day 14 4 + 5 + ```elixir 6 + Mix.install( 7 + [ 8 + {:kino_aoc, git: "https://github.com/ljgago/kino_aoc"}, 9 + :image 10 + ], 11 + consolidate_protocols: false 12 + ) 13 + ``` 14 + 15 + <!-- livebook:{"output":true} --> 16 + 17 + ``` 18 + :ok 19 + ``` 20 + 21 + ## Setup 22 + 23 + <!-- livebook:{"attrs":{"day":"14","session_secret":"ADVENT_OF_CODE_SESSION","variable":"puzzle_input","year":"2022"},"kind":"Elixir.KinoAOC.HelperCell","livebook_object":"smart_cell"} --> 24 + 25 + ```elixir 26 + {:ok, puzzle_input} = 27 + KinoAOC.download_puzzle("2022", "14", System.fetch_env!("LB_ADVENT_OF_CODE_SESSION")) 28 + ``` 29 + 30 + <!-- livebook:{"output":true} --> 31 + 32 + ``` 33 + {:ok, 34 + "492,26 -> 492,17 -> 492,26 -> 494,26 -> 494,16 -> 494,26 -> 496,26 -> 496,22 -> 496,26 -> 498,26 -> 498,17 -> 498,26 -> 500,26 -> 500,20 -> 500,26 -> 502,26 -> 502,25 -> 502,26 -> 504,26 -> 504,23 -> 504,26 -> 506,26 -> 506,21 -> 506,26 -> 508,26 -> 508,16 -> 508,26 -> 510,26 -> 510,24 -> 510,26\n481,92 -> 481,96 -> 476,96 -> 476,99 -> 487,99 -> 487,96 -> 485,96 -> 485,92\n460,73 -> 460,70 -> 460,73 -> 462,73 -> 462,69 -> 462,73 -> 464,73 -> 464,65 -> 464,73 -> 466,73 -> 466,65 -> 466,73 -> 468,73 -> 468,64 -> 468,73 -> 470,73 -> 470,68 -> 470,73 -> 472,73 -> 472,68 -> 472,73\n470,76 -> 470,80 -> 466,80 -> 466,84 -> 481,84 -> 481,80 -> 474,80 -> 474,76\n492,26 -> 492,17 -> 492,26 -> 494,26 -> 494,16 -> 494,26 -> 496,26 -> 496,22 -> 496,26 -> 498,26 -> 498,17 -> 498,26 -> 500,26 -> 500,20 -> 500,26 -> 502,26 -> 502,25 -> 502,26 -> 504,26 -> 504,23 -> 504,26 -> 506,26 -> 506,21 -> 506,26 -> 508,26 -> 508,16 -> 508,26 -> 510,26 -> 510,24 -> 510,26\n495,155 -> 499,155\n460,73 -> 460,70 -> 460,73 -> 462,73 -> 462,69 -> 462,73 -> 464,73 -> 464,65 -> 464,73 -> 466,73 -> 466,65 -> 466,73 -> 468,73 -> 468,64 -> 468,73 -> 470,73 -> 470,68 -> 470,73 -> 472,73 -> 472,68 -> 472,73\n500,138 -> 505,138\n484,39 -> 484,38 -> 484,39 -> 486,39 -> 486,33 -> 486,39 -> 488,39 -> 488,30 -> 488,39 -> 490,39 -> 490,35 -> 490,39 -> 492,39 -> 492,38 -> 492,39 -> 494,39 -> 494,30 -> 494,39 -> 496,39 -> 496,36 -> 496,39\n481,92 -> 481,96 -> 476,96 -> 476,99 -> 487,99 -> 487,96 -> 485,96 -> 485,92\n492,26 -> 492,17 -> 492,26 -> 494,26 -> 494,16 -> 494,26 -> 496,26 -> 496,22 -> 496,26 -> 498,26 -> 498,17 -> 498,26 -> 500,26 -> 500,20 -> 500,26 -> 502,26 -> 502,25 -> 502,26 -> 504,26 -> 504,23 -> 504,26 -> 506,26 -> 506,21 -> 506,26 -> 508,26 -> 508,16 -> 508,26 -> 510,26 -> 510,24 -> 510,26\n460,73 -> 460,70 -> 460,73 -> 462,73 -> 462,69 -> 462,73 -> 464,73 -> 464,65 -> 464,73 -> 466,73 -> 466,65 -> 466,73 -> 468,73 -> 468,64 -> 468,73 -> 470,73 -> 470,68 -> 470,73 -> 472,73 -> 472,68 -> 472,73\n492,26 -> 492,17 -> 492,26 -> 494,26 -> 494,16 -> 494,26 -> 496,26 -> 496,22 -> 496,26 -> 498,26 -> 498,17 -> 498,26 -> 500,26 -> 500,20 -> 500,26 -> 502,26 -> 502,25 -> 502,26 -> 504,26 -> 504,23 -> 504,26 -> 506,26 -> 506,21 -> 506,26 -> 508,26 -> 508,16 -> 508,26 -> 510,26 -> 510,24 -> 510,26\n460,73 -> 460,70 -> 460,73 -> 462,73 -> 462,69 -> 462,73 -> 464,73 -> 464,65 -> 464,73 -> 466,73 -> 466,65 -> 466,73 -> 468,73 -> 468,64 -> 468,73 -> 470,73 -> 470,68 -> 470,73 -> 472,73 -> 472,68 -> 472,73\n492,26 -> 492,17 -> 492,26 -> 494,26 -> 494,16 -> 494,26 -> 496,26 -> 496,22 -> 496,26 -> 498,26 -> 498,17 -> 498,26 -> 500,26 -> 500,20 -> 500,26 -> 502,26 -> 502,25 -> 502,26 -> 504,26 -> 504,23 -> 504,26 -> 506,26 -> 506,21 -> 506,26 -> 508,26 -> 508,16 -> 508,26 -> 510,26 -> 510,24 -> 510,26\n489,115 -> 489,119 -> 481,119 -> 481,126 -> 494,126 -> 494,119 -> 493,119 -> 493,115\n492,26 -> 492,17 -> 492,26 -> 494,26 -> 494,16 -> 494,26 -> 496,26 -> 496,22 -> 496,26 -> 498,26 -> 498,17 -> 498,26 -> 500,26 -> 500,20 -> 500,26 -> 502,26 -> 502,25 -> 502,26 -> 504,26 -> 504,23 -> 504,26 -> 506,26 -> 506,21 -> 506,26 -> 508,26 -> 508,16 -> 508,26 -> 510,26 -> 510,24 -> 510,26\n485,112 -> 485,102 -> 485,112 -> 487,112 -> 487,111 -> 487,112 -> 489,112 -> 489,105 -> 489,112\n481,92 -> 481,96 -> 476,96 -> 476,99 -> 487,99 -> 487,96 -> 485,96 -> 485,92\n484,39 -> 484,38 -> 484,39 -> 486,39 -> 486,33 -> 486,39 -> 488,39 -> 488,30 -> 488,39 -> 490,39 -> 490,35 -> 490,39 -> 492,39 -> 492,38 -> 492,39 -> 494,39 -> 494,30 -> 494,39 -> 496,39 -> 496,36 -> 496,39\n470,49 -> 470,53 -> 468,53 -> 468,60 -> 477,60 -> 477,53 -> 476,53 -> 476,49\n483,160 -> 487,160\n492,26 -> 492,17 -> 492,26 -> 494,26 -> 494,16 -> 494,26 -> 496,26 -> 496,22 -> 496,26 -> 498,26 -> 498,17 -> 498,26 -> 500,26 -> 500,20 -> 500,26 -> 502,26 -> 502,25 -> 502,26 -> 504,26 -> 504,23 -> 504,26 -> 506,26 -> 506,21 -> 506,26 -> 508,26 -> 508,16 -> 508,26 -> 510,26 -> 510,24 -> 510,26\n460,73 -> 460,70 -> 460,73 -> 462,73 -> 462,69 -> 462,73 -> 464,73 -> 464,65 -> 464,73 -> 466,73 -> 466,65 -> 466,73 -> 468,73 -> 468,64 -> 468,73 -> 47" <> ...} 35 + ``` 36 + 37 + ```elixir 38 + defmodule Cave do 39 + defstruct map: MapSet.new(), occupied: MapSet.new(), width: nil, height: 0, start: {500, 0} 40 + 41 + def parse(input, start \\ {500, 0}) do 42 + map = 43 + input 44 + |> String.split("\n", trim: true) 45 + |> Enum.map(&Cave.parse_line/1) 46 + |> Enum.reduce(&MapSet.union/2) 47 + 48 + {width, height} = 49 + for {x, y} <- map, 50 + reduce: {nil, nil} do 51 + {nil, nil} -> 52 + {x..x, y} 53 + 54 + {min_x..max_x, max_y} -> 55 + {min(min_x, x)..max(max_x, x), max(max_y, y)} 56 + end 57 + 58 + %__MODULE__{map: map, occupied: map, start: start, width: width, height: height} 59 + end 60 + 61 + def add_line(%__MODULE__{} = cave, line) do 62 + {width, height} = 63 + Enum.reduce(line, {cave.width, cave.height}, fn {x, y}, {x0..x1, h} -> 64 + {min(x, x0)..max(x, x1), max(y, h)} 65 + end) 66 + 67 + struct(cave, 68 + width: width, 69 + height: height, 70 + map: MapSet.union(cave.map, line), 71 + occupied: MapSet.union(cave.occupied, line) 72 + ) 73 + end 74 + 75 + def parse_point(str) do 76 + [x, y] = String.split(str, ",") 77 + 78 + {String.to_integer(x), String.to_integer(y)} 79 + end 80 + 81 + def parse_line(line) do 82 + points = 83 + line 84 + |> String.split(" -> ") 85 + |> Enum.map(&parse_point/1) 86 + |> Enum.chunk_every(2, 1, :discard) 87 + |> Enum.flat_map(fn 88 + [{x, y1}, {x, y2}] -> 89 + for y <- y1..y2, do: {x, y} 90 + 91 + [{x1, y}, {x2, y}] -> 92 + for x <- x1..x2, do: {x, y} 93 + end) 94 + |> MapSet.new() 95 + 96 + points 97 + end 98 + 99 + def drop_sand(%__MODULE__{} = cave), do: drop_sand(cave, cave.start) 100 + 101 + def drop_sand(%__MODULE__{height: h, width: x0..x1} = cave, {sx, sy}) 102 + when sx not in x0..x1 or sy >= h, 103 + do: {:fall_out, cave} 104 + 105 + def drop_sand(%__MODULE__{} = cave, {sx, sy}) do 106 + cond do 107 + {sx, sy + 1} not in cave.occupied -> drop_sand(cave, {sx, sy + 1}) 108 + {sx - 1, sy + 1} not in cave.occupied -> drop_sand(cave, {sx - 1, sy + 1}) 109 + {sx + 1, sy + 1} not in cave.occupied -> drop_sand(cave, {sx + 1, sy + 1}) 110 + true -> {{sx, sy}, struct(cave, occupied: MapSet.put(cave.occupied, {sx, sy}))} 111 + end 112 + end 113 + end 114 + 115 + defimpl Kino.Render, for: Cave do 116 + def to_livebook(%@for{} = cave) do 117 + x0..x1 = cave.width 118 + {sx, sy} = cave.start 119 + image = Image.new!(x1 - x0 + 3, cave.height + 3) 120 + 121 + {:ok, image} = 122 + Image.mutate(image, fn im -> 123 + Image.Draw.point(im, sx - x0 + 1, sy + 1, color: :red) 124 + 125 + for {x, y} <- cave.occupied do 126 + Image.Draw.point(im, x - x0 + 1, y + 1, color: :tan) 127 + end 128 + 129 + for {x, y} <- cave.map do 130 + Image.Draw.point(im, x - x0 + 1, y + 1, color: :white) 131 + end 132 + 133 + :ok 134 + end) 135 + 136 + buf = 137 + image 138 + |> Image.resize!(4, interpolate: :nearest) 139 + |> Image.write!(:memory, suffix: ".png") 140 + 141 + Kino.Output.image(buf, "image/png") 142 + end 143 + end 144 + ``` 145 + 146 + <!-- livebook:{"output":true} --> 147 + 148 + ``` 149 + {:module, Kino.Render.Cave, <<70, 79, 82, 49, 0, 0, 16, ...>>, {:to_livebook, 1}} 150 + ``` 151 + 152 + ```elixir 153 + cave = Cave.parse(puzzle_input) 154 + ``` 155 + 156 + ## Task 1 157 + 158 + ```elixir 159 + Stream.unfold(cave, &Cave.drop_sand/1) 160 + |> Enum.take_while(&(&1 != :fall_out)) 161 + |> length() 162 + ``` 163 + 164 + <!-- livebook:{"output":true} --> 165 + 166 + ``` 167 + 768 168 + ``` 169 + 170 + ## Task 2 171 + 172 + ```elixir 173 + floor_y = cave.height + 2 174 + 175 + floor = for x <- 0..(cave.width.last * 2), into: MapSet.new(), do: {x, floor_y} 176 + 177 + cave = Cave.add_line(cave, floor) 178 + 179 + Stream.unfold(cave, &Cave.drop_sand/1) 180 + |> Enum.take_while(&(&1 != cave.start)) 181 + |> length() 182 + |> then(&(&1 + 1)) 183 + ``` 184 + 185 + <!-- livebook:{"output":true} --> 186 + 187 + ``` 188 + 26686 189 + ```