Day 14: Regolith Reservoir
file:src/day14.jl
module Day14
using Base.Iterators: map as imap, flatten
export consecutive_pairs, read_input
@enum Material air rock sand
struct Cave{T}
origin::CartesianIndex
matrix::Matrix{T}
end
function consecutive_pairs(it)
Channel() do chan
length(it) < 2 && return
(prev, st) = iterate(it)
while !isnothing(begin next = iterate(it, st) end)
(next, st) = next
put!(chan, (prev, next))
prev = next
end
end
end
function read_input(inp::IO)
parse_coord(s) = (split(s, ",") .|> n -> parse(Int, n)) |> Tuple
parse_path(s) = split(s, "->") .|> parse_coord
paths = readlines(inp) .|> parse_path
xlim = extrema(imap(c->c[1], flatten(paths)))
ylim = extrema(imap(c->c[2], flatten(paths)))
ylim = (0, ylim[2] + 2)
xlim = (min(xlim[1], 500 - ylim[2]), max(xlim[2], 500 + ylim[2]))
cave = Cave(CartesianIndex((xlim[1]-1, ylim[1]-1)),
fill(air, (xlim[2]-xlim[1]+1, ylim[2]-ylim[1]+1)))
for path in paths
cs = path .|> x -> CartesianIndex(x) - cave.origin
for (a, b) in consecutive_pairs(cs)
(a, b) = minmax(a, b)
cave.matrix[a:b] .= rock
end
end
cave
end
function Base.print(io::IO, cave::Cave)
println(io, cave.origin)
chars = Dict(rock => "\033[1;33m#", air => "\033[1;30m.", sand => "\033[1;34mo")
for line in eachcol(cave.matrix)
println(io, join(line .|> m -> getindex(chars, m)))
end
print(io, "\033[m")
end
function drop_grain_of_sand(cave::Cave)
pos = CartesianIndex((500, 0)) - cave.origin
dx = CartesianIndex((1, 0))
dy = CartesianIndex((0, 1))
while true
!checkbounds(Bool, cave.matrix, pos) && return false
if get(cave.matrix, pos+dy, air) == air
pos += dy
elseif get(cave.matrix, pos+dy-dx, air) == air
pos += (dy - dx)
elseif get(cave.matrix, pos+dy+dx, air) == air
pos += (dy + dx)
else
cave.matrix[pos] = sand
break
end
end
return true
end
function main(inp::IO, out::IO)
input = read_input(inp)
cave = deepcopy(input)
count = 0
while drop_grain_of_sand(cave)
count += 1
end
println(out, cave)
println(out, "Part 1: $count")
cave = input; count = 1 # off-by-one
cave.matrix[:,end] .= rock
while drop_grain_of_sand(cave) &&
cave.matrix[CartesianIndex((500, 0)) - cave.origin] == air
count += 1
end
println(out, cave)
println(out, "Part 2: $count")
end
end # module