Skip to content

Commit 4488a3c

Browse files
committed
[Day 16] Optimize
1 parent 8239e81 commit 4488a3c

File tree

2 files changed

+65
-54
lines changed

2 files changed

+65
-54
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ This Julia package contains my solutions for [Advent of Code 2024](https://adven
2525
| 13 | [:white_check_mark:](https://adventofcode.com/2024/day/13) | 3.101 ms | 1.15 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2024.jl/blob/main/src/day13.jl) |
2626
| 14 | [:white_check_mark:](https://adventofcode.com/2024/day/14) | 75.842 ms | 275.06 KiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2024.jl/blob/main/src/day14.jl) |
2727
| 15 | [:white_check_mark:](https://adventofcode.com/2024/day/15) | 1.944 ms | 3.16 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2024.jl/blob/main/src/day15.jl) |
28-
| 16 | [:white_check_mark:](https://adventofcode.com/2024/day/16) | 63.589 ms | 41.34 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2024.jl/blob/main/src/day16.jl) |
28+
| 16 | [:white_check_mark:](https://adventofcode.com/2024/day/16) | 20.822 ms | 9.21 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2024.jl/blob/main/src/day16.jl) |
2929
| 17 | [:white_check_mark:](https://adventofcode.com/2024/day/17) | 6.921 ms | 706.30 KiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2024.jl/blob/main/src/day17.jl) |
30-
| 18 | [:white_check_mark:](https://adventofcode.com/2024/day/18) |3.007 ms | 5.51 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2024.jl/blob/main/src/day18.jl) |
30+
| 18 | [:white_check_mark:](https://adventofcode.com/2024/day/18) | 3.007 ms | 5.51 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2024.jl/blob/main/src/day18.jl) |
3131
| 19 | [:white_check_mark:](https://adventofcode.com/2024/day/19) | 57.908 ms | 3.41 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2024.jl/blob/main/src/day19.jl) |
3232
| 20 | [:white_check_mark:](https://adventofcode.com/2024/day/20) | 227.453 ms | 224.97 MiB| [:white_check_mark:](https://github.com/goggle/AdventOfCode2024.jl/blob/main/src/day20.jl) |
3333
| 21 | [:white_check_mark:](https://adventofcode.com/2024/day/21) | 296.609 μs | 78.89 KiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2024.jl/blob/main/src/day21.jl) |

src/day16.jl

Lines changed: 63 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,80 +3,91 @@ module Day16
33
using AdventOfCode2024
44
using DataStructures
55

6+
const DIRS = [(-1, 0), (0, 1), (1, 0), (0, -1)]
67

78
function day16(input::String = readInput(joinpath(@__DIR__, "..", "data", "day16.txt")))
8-
data = map(x -> x[1], reduce(vcat, permutedims.(map(x -> split(x, ""), split(input)))))
9-
startpos = findall(x -> x == 'S', data)[1]
9+
data = stack(split(rstrip(input), '\n')) |> permutedims
10+
passable = data .!= '#'
11+
startpos = findfirst(==('S'), data)
12+
nrows, ncols = size(data)
1013

11-
dist, prev = dijkstra(data, startpos)
12-
endpos = findall(x -> x == 'E', data)[1]
13-
p1 = minimum(dist[endpos.I..., i] for i 1:4)
14+
dist, prev = dijkstra(passable, startpos, nrows, ncols)
15+
endpos = findfirst(==('E'), data)
16+
p1 = minimum(dist[endpos[1], endpos[2], dir] for dir in 1:4)
1417

15-
endpositions = Tuple{Int,Int,Int}[]
16-
for i 1:4
17-
if dist[endpos.I..., i] == p1
18-
push!(endpositions, (endpos.I..., i))
19-
end
20-
end
21-
p2 = part2(endpositions, prev, size(data)...)
22-
return [p1, p2]
18+
visited = bfs_trace(prev, endpos, nrows, ncols)
19+
return [p1, sum(visited)]
2320
end
2421

25-
function dijkstra(data::Matrix{Char}, startpos::CartesianIndex{2})
26-
dist = Dict{Tuple{Int,Int,Int},Int}()
27-
dist[startpos.I..., 2] = 0
22+
function dijkstra(passable::BitMatrix, startpos::CartesianIndex{2}, nrows::Int, ncols::Int)
23+
dist = fill(typemax(Int), nrows, ncols, 4)
24+
prev = [Tuple{Int,Int,Int}[] for _ in 1:nrows, _ in 1:ncols, _ in 1:4]
2825
pq = PriorityQueue{Tuple{Int,Int,Int},Int}()
29-
prev = Dict{Tuple{Int,Int,Int},Vector{Tuple{Int,Int,Int}}}()
30-
for position findall(x -> x ('.', 'E', 'S'), data)
31-
for i 1:4
32-
v = (position.I..., i)
33-
prev[v] = []
34-
dist[v] = typemax(Int)
35-
pq[v] = typemax(Int)
36-
end
37-
end
38-
dist[startpos.I..., 2] = 0
39-
pq[startpos.I..., 2] = 0
26+
27+
start_dir = 2
28+
dist[startpos[1], startpos[2], start_dir] = 0
29+
pq[(startpos[1], startpos[2], start_dir)] = 0
4030

4131
while !isempty(pq)
42-
u = dequeue!(pq)
43-
ncandids = (((u[1:2] .+ _number_to_dir(u[3]))..., u[3]), (u[1], u[2], mod1(u[3] + 1, 4)), (u[1], u[2], mod1(u[3] - 1, 4)))
44-
nscores = (1, 1000, 1000)
45-
neighbours = [(x, cost) for (x, cost) zip(ncandids, nscores) if data[x[1:2]...] != '#']
46-
for (v, cost) neighbours
47-
alt = dist[u] + cost
48-
if alt <= dist[v]
49-
if alt < dist[v]
50-
prev[v] = []
51-
end
52-
push!(prev[v], u)
53-
dist[v] = alt
54-
pq[v] = alt
32+
(i, j, dir), cost = peek(pq)
33+
dequeue!(pq)
34+
cost > dist[i, j, dir] && continue
35+
36+
di, dj = DIRS[dir]
37+
ni, nj = i + di, j + dj
38+
if checkbounds(Bool, passable, ni, nj) && passable[ni, nj]
39+
new_cost = cost + 1
40+
if new_cost < dist[ni, nj, dir]
41+
dist[ni, nj, dir] = new_cost
42+
prev[ni, nj, dir] = [(i, j, dir)]
43+
pq[(ni, nj, dir)] = new_cost
44+
elseif new_cost == dist[ni, nj, dir]
45+
push!(prev[ni, nj, dir], (i, j, dir))
46+
end
47+
end
48+
49+
for new_dir in (mod1(dir + 1, 4), mod1(dir - 1, 4))
50+
new_cost = cost + 1000
51+
if new_cost < dist[i, j, new_dir]
52+
dist[i, j, new_dir] = new_cost
53+
prev[i, j, new_dir] = [(i, j, dir)]
54+
pq[(i, j, new_dir)] = new_cost
55+
elseif new_cost == dist[i, j, new_dir]
56+
push!(prev[i, j, new_dir], (i, j, dir))
5557
end
5658
end
5759
end
60+
5861
return dist, prev
5962
end
6063

61-
function part2(queue::Vector{Tuple{Int,Int,Int}}, prev::Dict{Tuple{Int,Int,Int},Vector{Tuple{Int,Int,Int}}}, nrows::Int, ncols::Int)
62-
visited = zeros(Bool, nrows, ncols)
64+
function bfs_trace(prev::Array{Vector{Tuple{Int,Int,Int}},3}, endpos::CartesianIndex{2}, nrows::Int, ncols::Int)
65+
visited = falses(nrows, ncols)
66+
queue = Deque{Tuple{Int,Int,Int}}()
67+
enqueued = falses(nrows, ncols, 4)
68+
69+
for dir in 1:4
70+
pos = (endpos[1], endpos[2], dir)
71+
if !isempty(prev[pos...])
72+
push!(queue, pos)
73+
enqueued[pos...] = true
74+
end
75+
end
76+
6377
while !isempty(queue)
6478
elem = popfirst!(queue)
65-
visited[elem[1:2]...] = true
66-
for p prev[elem]
67-
if p queue
79+
i, j, _ = elem
80+
visited[i, j] = true
81+
82+
for p in prev[elem...]
83+
if !enqueued[p...]
84+
enqueued[p...] = true
6885
push!(queue, p)
6986
end
7087
end
7188
end
72-
return sum(visited)
73-
end
7489

75-
function _number_to_dir(n)
76-
n == 1 && return (-1, 0)
77-
n == 2 && return (0, 1)
78-
n == 3 && return (1, 0)
79-
n == 4 && return (0, -1)
90+
return visited
8091
end
8192

8293
end # module

0 commit comments

Comments
 (0)