Day 16
file:test/Day16Spec.jl
# add tests
file:src/Day16.jl
module Day16
using DataStructures
const Pos = CartesianIndex{2}
const PosDir = CartesianIndex{3}
pos(pd::PosDir) = Pos(pd[1], pd[2])
dir(pd::PosDir) = pd[3]
posdir(p::Pos, d::Int) = CartesianIndex(p[1], p[2], d)
struct Maze
::Matrix{Char}
map::Pos
start_tile::Pos
end_tileend
function read_input(io::IO)
= io |> readlines |> stack
map = findfirst(isequal('S'), map)
start_tile = findfirst(isequal('E'), map)
end_tile = '.'
map[start_tile] = '.'
map[end_tile] return Maze(map, start_tile, end_tile)
end
function grid_dijkstra(
::Type{T}, size::NTuple{Dim,Int},
::Vector{CartesianIndex{Dim}}, istarget::F1,
start::F2, dist_func::F3) where {T,Dim,F1,F2,F3}
neighbours
= falses(size...)
visited = fill(typemax(T), size)
distance = PriorityQueue{CartesianIndex{Dim},T}()
queue = Array{CartesianIndex{Dim},Dim}(undef, size)
prev
for s in start
= zero(T)
distance[s] enqueue!(queue, s, zero(T))
end
= nothing
current while !isempty(queue)
= dequeue!(queue)
current istarget(current) && break
&& continue
visited[current] for loc in neighbours(current)
&& continue
visited[loc] = distance[current] + dist_func(current, loc)
d if d < distance[loc]
= d
distance[loc] = current
prev[loc] = d
queue[loc] end
end
= true
visited[current] end
=distance, route=prev, target=current)
(distanceend
function trace_back_all(gd, start, neighbours, dist_func)
= falses(size(gd.distance)...)
visited = [gd.target]
stack while !isempty(stack)
= pop!(stack)
pt && continue
visited[pt] = true
visited[pt] == start && continue
pt = gd.route[pt]
optimal for nb in neighbours(pt)
if gd.distance[pt] == gd.distance[nb] + dist_func(nb, pt)
push!(stack, nb)
end
end
end
return visited
end
const NB = [Pos(1, 0), Pos(0, 1), Pos(-1, 0), Pos(0, -1)]
const EAST = 1
function main(io::IO)
= read_input(io)
maze is_target(a::PosDir) = pos(a) == maze.end_tile
path_tile(a::PosDir) = maze.map[pos(a)] == '.'
neighbours(a::PosDir) = filter(path_tile,
posdir(pos(a) + NB[dir(a)], dir(a)),
[posdir(pos(a), mod1(dir(a) + 1, 4)),
posdir(pos(a), mod1(dir(a) - 1, 4))])
dist_func(a::PosDir, b::PosDir) = abs(a[1]-b[1]) + abs(a[2]-b[2]) +
min(mod(a[3] - b[3], 4), mod(b[3] - a[3], 4)) * 1000
= grid_dijkstra(Int, (size(maze.map)..., 4),
gd posdir(maze.start_tile, EAST)],
[
is_target, neighbours, dist_func)
= gd.distance[gd.target]
part1
rev_neighbours(a::PosDir) = filter(path_tile,
posdir(pos(a) - NB[dir(a)], dir(a)),
[posdir(pos(a), mod1(dir(a) + 1, 4)),
posdir(pos(a), mod1(dir(a) - 1, 4))])
= trace_back_all(gd, posdir(maze.start_tile, EAST),
trace
rev_neighbours, dist_func)
= reduce(|, trace, dims=3) |> sum
part2
return part1, part2
end
end