1
1
module Day20
2
2
3
3
using AdventOfCode2024
4
-
4
+ using DataStructures
5
5
6
6
function day20 (input:: String = readInput (joinpath (@__DIR__ , " .." , " data" , " day20.txt" )))
7
- data = map (x -> x[ 1 ], reduce (vcat, permutedims .( map (x -> split (x, " " ), split (input))) ))
7
+ data = stack ( split (rstrip (input), ' \n ' ))
8
8
tte = time_to_end (data)
9
9
return [count_cheats (tte, 100 ), count_cheats (tte, 100 ; max_time= 20 )]
10
10
end
11
11
12
12
function time_to_end (data:: Matrix{Char} )
13
- tte = - 1 * ones (Int, size (data)... )
14
- goal = findall (x -> x == ' E' , data)[1 ]
15
- current = [goal]
16
- time = 0
17
- while ! isempty (current)
18
- for elem ∈ current
19
- tte[elem] = time
20
- end
21
- next = Vector {CartesianIndex{2}} ()
22
- while ! isempty (current)
23
- elem = popfirst! (current)
24
- for neigh ∈ [elem + x for x ∈ CartesianIndex .((1 ,- 1 ,0 ,0 ), (0 ,0 ,1 ,- 1 ))]
25
- if data[neigh] != ' #' && tte[neigh] == - 1
26
- push! (next, neigh)
13
+ rows, cols = size (data)
14
+ tte = fill (- 1 , rows, cols)
15
+ goal = findfirst (== (' E' ), data)
16
+ queue = Deque {CartesianIndex{2}} ()
17
+ push! (queue, goal)
18
+ tte[goal] = 0
19
+ time = 1
20
+
21
+ while ! isempty (queue)
22
+ for _ in 1 : length (queue)
23
+ elem = popfirst! (queue)
24
+ for dir in (CartesianIndex (- 1 , 0 ), CartesianIndex (1 , 0 ), CartesianIndex (0 , - 1 ), CartesianIndex (0 , 1 ))
25
+ neigh = elem + dir
26
+ if checkbounds (Bool, data, neigh) && data[neigh] != ' #' && tte[neigh] == - 1
27
+ tte[neigh] = time
28
+ push! (queue, neigh)
27
29
end
28
30
end
29
31
end
30
- current = next
31
32
time += 1
32
33
end
33
34
return tte
34
35
end
35
36
36
37
function count_cheats (tte:: Matrix{Int} , threshold:: Int ; max_time:: Int = 2 )
38
+ rows, cols = size (tte)
37
39
count = 0
38
- for c ∈ findall (x -> x >= 0 , tte)
39
- for e ∈ reachable_within (tte, c, max_time)
40
- time_saved = tte[e] < tte[c] ? tte[c] - tte[e] - sum (abs .((c- e). I)) : - 1
41
- if time_saved >= threshold
42
- count += 1
40
+ offsets = generate_offsets (max_time)
41
+
42
+ for c in findall (>= (0 ), tte)
43
+ ci, cj = Tuple (c)
44
+ tte_c = tte[c]
45
+
46
+ for (di, dj) in offsets
47
+ ni, nj = ci + di, cj + dj
48
+ if 1 <= ni <= rows && 1 <= nj <= cols
49
+ e = CartesianIndex (ni, nj)
50
+ if tte[e] >= 0 && tte[e] < tte_c
51
+ time_saved = tte_c - tte[e] - (abs (di) + abs (dj))
52
+ time_saved >= threshold && (count += 1 )
53
+ end
43
54
end
44
55
end
45
56
end
46
57
return count
47
58
end
48
59
49
- function reachable_within (tte:: Matrix{Int} , c:: CartesianIndex{2} , picoseconds:: Int )
50
- reachable = Set {CartesianIndex{2}} ()
51
- for p ∈ 1 : picoseconds
52
- for i ∈ 0 : p
53
- j = p - i
54
- for (s1, s2) ∈ zip ((1 , 1 , - 1 , - 1 ), (1 , - 1 , 1 , - 1 ))
55
- n = CartesianIndex (c[1 ] + s1* i, c[2 ] + s2* j)
56
- if checkbounds (Bool, tte, n) && tte[n] >= 0
57
- push! (reachable, n)
60
+ @inline function generate_offsets (max_dist:: Int )
61
+ offsets = Tuple{Int,Int}[]
62
+ for d in 0 : max_dist
63
+ for i in 0 : d
64
+ j = d - i
65
+ for s1 in (- 1 , 1 )
66
+ for s2 in (- 1 , 1 )
67
+ push! (offsets, (s1* i, s2* j))
58
68
end
59
69
end
60
70
end
61
71
end
62
- return reachable
72
+ unique! (offsets)
73
+ return offsets
63
74
end
64
75
65
76
end # module
0 commit comments