@@ -3,80 +3,91 @@ module Day16
3
3
using AdventOfCode2024
4
4
using DataStructures
5
5
6
+ const DIRS = [(- 1 , 0 ), (0 , 1 ), (1 , 0 ), (0 , - 1 )]
6
7
7
8
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)
10
13
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 )
14
17
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)]
23
20
end
24
21
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 ]
28
25
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
40
30
41
31
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))
55
57
end
56
58
end
57
59
end
60
+
58
61
return dist, prev
59
62
end
60
63
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
+
63
77
while ! isempty (queue)
64
78
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
68
85
push! (queue, p)
69
86
end
70
87
end
71
88
end
72
- return sum (visited)
73
- end
74
89
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
80
91
end
81
92
82
93
end # module
0 commit comments