Advent 2022 - Day 14
Mix.install([
{:kino, github: "livebook-dev/kino"},
{:libgraph, "~> 0.7"}
])
:ok
Setup
input = Kino.Input.textarea("Please paste your input file:")
structures =
input
|> Kino.Input.read()
|> String.split("\n")
|> Enum.map(fn structure ->
structure
|> String.split(" -> ")
|> Enum.map(fn pair ->
pair |> String.split(",") |> Enum.map(&String.to_integer/1)
end)
end)
[[[498, 4], [498, 6], [496, 6]], [[503, 4], [502, 4], [502, 9], [494, 9]]]
Utils
defmodule StructuresToGrid do
def unravel_pair([[a, b], [x, y]]) when b == y do
range = a..x
dups = List.duplicate(b, Range.size(range))
Enum.zip(range, dups)
end
def unravel_pair([[a, b], [x, y]]) when a == x do
range = b..y
dups = List.duplicate(a, Range.size(range))
Enum.zip(dups, range)
end
def unravel_structure(structure) do
Enum.chunk_every(structure, 2, 1, :discard)
|> Enum.map(&unravel_pair/1)
|> Enum.map(&MapSet.new/1)
|> Enum.reduce(&MapSet.union(&1, &2))
end
def run(structures) do
structures
|> Enum.map(&unravel_structure/1)
|> Enum.reduce(&MapSet.union(&1, &2))
end
end
{:module, StructuresToGrid, <<70, 79, 82, 49, 0, 0, 11, ...>>, {:run, 1}}
grid = StructuresToGrid.run(structures)
MapSet.new([
{494, 9},
{495, 9},
{496, 6},
{496, 9},
{497, 6},
{497, 9},
{498, 4},
{498, 5},
{498, 6},
{498, 9},
{499, 9},
{500, 9},
{501, 9},
{502, 4},
{502, 5},
{502, 6},
{502, 7},
{502, 8},
{502, 9},
{503, 4}
])
max_y =
structures
|> List.flatten()
|> tl
|> Enum.take_every(2)
|> Enum.max()
9
defmodule SandSim do
def drop_sand(grid, max_y, has_floor, {x, y}) do
cond do
!has_floor and y > max_y -> nil
has_floor and y + 1 == max_y + 2 -> MapSet.put(grid, {x, y})
!MapSet.member?(grid, {x, y + 1}) -> drop_sand(grid, max_y, has_floor, {x, y + 1})
!MapSet.member?(grid, {x - 1, y + 1}) -> drop_sand(grid, max_y, has_floor, {x - 1, y + 1})
!MapSet.member?(grid, {x + 1, y + 1}) -> drop_sand(grid, max_y, has_floor, {x + 1, y + 1})
true -> MapSet.put(grid, {x, y})
end
end
def drop_sand(grid, max_y, has_floor), do: drop_sand(grid, max_y, has_floor, {500, 0})
def run(grid, max_y, has_floor, count \\ 0) do
next_grid = drop_sand(grid, max_y, has_floor)
if next_grid == nil or MapSet.member?(grid, {500, 0}) do
count
else
run(next_grid, max_y, has_floor, count + 1)
end
end
end
{:module, SandSim, <<70, 79, 82, 49, 0, 0, 13, ...>>, {:run, 4}}
Part 1
SandSim.run(grid, max_y, false)
24
Part 2
SandSim.run(grid, max_y, true)
93