Day 12
We need to find borders of regions on a grid. First we need to find all connected regions.
«day12»
struct Dim{N} end
function grid_neighbours(::Type{Dim{N}}) where {N}
Iterators.flatten(
CartesianIndex((k == d ? -1 : 0 for k in 1:N)...),
(CartesianIndex((k == d ? 1 : 0 for k in 1:N)...))
for d in 1:N) |> collect
end
function mark_regions(a::AbstractArray{T,N}) where {T,N}
= grid_neighbours(Dim{N})
nb = 1
label = zeros(Int, size(a)...)
mark
while (start = findfirst(mark .== 0)) !== nothing
= CartesianIndex{N}[start]
queue while !isempty(queue)
= pop!(queue)
x = label
mark[x] for n in nb
= x + n
y checkbounds(Bool, a, y) || continue
== 0 || continue
mark[y] == a[y] || continue
a[x] push!(queue, y)
end
end
+= 1
label end
return mark
end
Once all regions have a unique id, we can scan for fence locations. I scan columns and rows seperately.
«day12»
function add_border(a::AbstractArray{T,Dim}, e::T) where {T,Dim}
= Array{T}(undef, (size(a) .+ 2)...)
bordered for d = 1:Dim
= Union{Int,Colon}[Colon() for _ in 1:Dim]
sel = 1
sel[d] ...] .= e
bordered[sel= size(a)[d] + 2
sel[d] ...] .= e
bordered[selend
2:s+1 for s in size(a))...] = a
bordered[(return bordered
end
function fence_regions(input::Matrix{T}) where {T}
= add_border(input, zero(T))
b edge(a, b) = a == b ? (zero(T)=>zero(T)) : (a => b)
= edge.(b[1:end-1,2:end-1], b[2:end,2:end-1])
col_fences = edge.(b[2:end-1,1:end-1], b[2:end-1,2:end])
row_fences return row_fences, col_fences
end
Part 1
We need to count the total number of fences.
«day12»
function cost1(input::Matrix{T}) where {T}
= mark_regions(input)
regions = fence_regions(regions)
rf, cf
= length(unique(regions))
nregions = zeros(Int, nregions+1)
nfences
@views nfences[first.(rf).+1] .+= 1
@views nfences[last.(rf).+1] .+= 1
@views nfences[first.(cf).+1] .+= 1
@views nfences[last.(cf).+1] .+= 1
k(c::Int) = nfences[c+1] * sum(c .== regions)
return sum(k(c) for c in unique(regions))
end
Part 2
I reuse the nice count_sorted
function from day 1.
«day12»
function cost2(input::Matrix{T}) where {T}
= mark_regions(input)
regions = fence_regions(regions)
rf, cf
= length(unique(regions))
nregions = zeros(Int, nregions + 1)
nsides
tally_sides(x) = @views nsides[first.(count_sorted(x)).+1] .+= 1
for r in eachrow(cf)
tally_sides(first.(r))
tally_sides(last.(r))
end
for c in eachcol(rf)
tally_sides(first.(c))
tally_sides(last.(c))
end
k(c::Int) = nsides[c+1] * sum(c .== regions)
return sum(k(c) for c in 1:nregions)
end
Main and test
file:src/Day12.jl
module Day12
using ..Day01: count_sorted
read_input(io::IO) = io |> readlines .|> collect |> stack
<<day12>>
function main(io::IO)
= read_input(io)
input cost1(input), cost2(input)
end
end
file:test/Day12Spec.jl
# add tests