-
Notifications
You must be signed in to change notification settings - Fork 0
/
24.rkt
219 lines (198 loc) · 15.2 KB
/
24.rkt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
;;; adventofcode.com/2016/day/24
#lang racket
(require data/queue)
(require srfi/25) ; for multi-dimensional arrays
;; duct map given as puzzle input
(define duct-map
(apply array
(shape 0 37 0 181)
(flatten
(map string->list
'("#####################################################################################################################################################################################"
"#.....#.........#.#...#.....#.............#.......#.....#.....#...........#...#.........#.#.#.....#.......#...............#..........3#.#.#.....#.......#...#.....#...#.#.#.....#...#"
"#.###.#.#.###.#.#.#.#.#.#.#.#.#.#####.#####.#.###.#.#.#######.###.#.#######.#.#.#.#.#.#.#.#.#.#####.#.#.###.#######.#.###.###.#.#.#.#.#.#.#.#.#.#.#.#.#####.#.###.#.#.#.#.###.#.###.#"
"#.......#.#...#...#.#...#...#.#...#...#.#...#.....#...#.#.....#.....#.....#.......#...#...#.................#.#.............#...#.....#.........#...#...#.#...#...#.....#.......#...#"
"#.#.#.###.#.#.###.#.#.#.#.###.#.###.###.#.#.#.#######.#.#####.#.#.#####.#.#.#.#####.#.###.#.#####.#####.#.###.###.###.#####.#.#.#.#.#.#.#.#.#.#.###.###.#.#.#.#.#####.#.#.#.#.#.#####"
"#..1#.......#...........#...#.........#.#.....#...#.#...#.........#...#...#...#.....#.#...#.#.#.....#...#.#.#...#.......#.........#.......#...#.#...#.....#.#.....#...#...#..2#.....#"
"#.#####.###.#.#.#.###.###.###.#####.#.#.#.#.###.#.#.#.#.#####.###.#.#.#####.#.#.#.#.#.#.###.#.#.#.#.#.#.#.#.#.#.#.#.#####.###.#.#.#####.###.###.#.#.#.###.#.#####.#.#.#.###.#.#.#.#.#"
"#...#.............#.#...#.#...#...#.#.#...#...#.............#.#.....#.........#.........#.#...#.#.#.#...#.......#.#.......#...#...#.#.......#...#.#.....#.........#.#.#.#.........#.#"
"#.#.#.###.###.#.#.#.#.#.#.#.#.#.#.#.###.###.###.#.#####.#.#.#.###.#.#.#.#####.#.#.###.#.#.#.#.#.###.#.#.#.#####.#####.###.#.#.#.#.#.#.#######.#.#.#.#.#.#.#######.#.#.#.#.###.#.#.#.#"
"#.......#...#.....#.....#.......#...#.....#.#.#.........#.......#.#.....#...#.#...#...#.#.....#...#.#...........#.........#...#.#.#...#.#.....#.....#.....#...........#...#.......#.#"
"#####.###.#.###########.###.#.###.###.###.#.#.#.###.#.###.###.#.#.#.#####.#.#.#.#########.#####.#.#.###.#.#.#.#.#.###.#.#.#.###.#.#####.#.#.#.#.#.#.#.#####.###.#####.###.#.#.#.#.#.#"
"#...#...#.......#.....#.....#.....#.....#.......#.#.#.....#...........#.....#.#.#.#.......#.....#.......#...........#.#...#...#.#.......#...#.....#...#.#...#.#...#...#.....#.....#.#"
"#.#.###.#.###.#.#####.#.#.#.#.#.#.###.#.###.###.#.#.#.###.#.#.#.###.#.#.###.#.#.#.#.#.#.#.###.#.#.#######.###.#######.#.###.###.#.###.#.#.#.###.###.#.#.#.#.#.#.#.#.###.#.#.###.#.#.#"
"#.....#...#.........#...#...#.#.#.........#.#.#...#.#...#.#...#.#.........#.....#.#...#.#...#...#.......#.....#...#...#.#.....#.......#.#...#...#.........#.#...#.#.........#.#...#.#"
"#.###.###########.#.###.#.#.#.#####.#.#.#.###.#.#.#.#####.#.###.#.#.#######.#####.#.#.#.#.###.#.#.###.#.#.#####.###.#.#.#.#.#.#########.###.#.#.#.#.#.###.#.#.#.#.#.#.#.#.#.#.#####.#"
"#.#.................#.............#...#...#.#.#.#...#...#...#.....#.......#.#.#...#...........#.........#.......#.........#...#...#...#.........#.#...#...#.........#.........#...#.#"
"#.#.#.#####.#######.###.###.###.#.#######.#.#.###.###.#.#.#.#####.#####.###.#.#.#.#.###.#.###.#.#.#####.#.#.#.#.#.#.###.#.#.#.#.#.#.#.#.###.###.#######.###.#.#.#.#.#.#.###.#.#.#.#.#"
"#...#.#.#...................#0............#...........#.#.....#.#.....#.#.........#.....#.......#.......#.....#.......#.#...#.......#.#.#...#.............#...#.....#.#.......#...#6#"
"#.#.#.#.#.###.#.#.#.#.#####.###.#.#.#####.#####.###.#.###.###.#.#.#.#.#.#.#####.#.#.#.#.#.#####.#.###.#.#####.#.#####.#.#.#.#.#####.#.#.#.#.#.#.#.#########.#.###.###.#######.#.#.###"
"#.#...#.#.......#.#.#.#.....#...#...#.#...#...#.#...#.........#...#...#...#.....#.....#.....#...#.....#.......#.....#...#...#.#.....#.#...#.#.#.#.#.......#...#.......#...#...#...#.#"
"#.###.#.###.#.#.#.#.#.###.#.#.#.###.#.###.#.#.#.#.###.#.#.#.#.#.#.#####.#.#####.#.#####.#.#.#.###.#.#############.###.###.###.###########.#.###.#.#.#.###.#.###.###.#.#.#.#.#.#.###.#"
"#.....#.#...#...#...#...#.#.#.........#.....#...#...#.#.....#...#.#...........#.#.......#...#.#.......#.#...#.........#...#...#.#.#.....#...#.#.#.#.......#...........#...#.#.......#"
"#.#.#####.#.###########.#.#.#.#############.#.#.#.#.#######.#######.###.#.###.###.###.#######.#.###.#.#.#.#.#######.###.###.###.#.#.#.#.#.#.#.#.#.#.###.#.#######.###.###.#.#.#.#####"
"#...#.#.......#.................#.#.........#.....#.#.#.....#...#.....#.......#...#...#.......#.#...#.#.#...#...........#.#.#.....#.#.........#...#.#...........#...#.....#...#.#...#"
"###.#.#.#.###.#.#.#.#.#.#.###.#.#.#.#.#.#.###.#.#.#.#.#.#.#.###.#.###.#.###.###.#.#####.#####.###.#.#.#.#######.#.#.#.#.#.#.#.###.#.#.###.#.#.###.#.#.#####.#.#.#.###.#.#.###.#.#.#.#"
"#...#...#.....#.#.#...#...#...#...#.............#.....#...#.#.#...#.............#.#.............#...#.#.#...#.#.#...#.#...#.#.#.......#.#.......#...#.#.....#...#...#.#...#.#...#...#"
"#.#.#.#.#.#####.#.#.#.#.#.#.#######.###.#######.#.###.#.###.#.#.#.###.#.#.###.#.#.#.#.#.#.#.###.###.#.###.###.###.###.###.###.###.#####.#######.###.#.###.#.#.###.#.#.#.###.###.#.#.#"
"#...#.#.#.......#.#.#...#...........#.........#.#.#...#.#.#.#.#.#.............#...#...#...#.....#.......#...#.#...#...#...#...#.........#...#...#.....#.#.....#.#.#...#...#.#...#...#"
"###.#.#.#.###.###.###.#.#####.#.#.#.#.#.#####.###.#.###.#.#.#.#.###.#.###.###.###.#.#.#.###.###.###.###.###.###.#.#.###.#.#.#.#.###.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.###.#.###.###.#"
"#...#...#.#...#...#.#.#.......#...#...#.#.......#.......#.#.....#.........#...........#.....#...#...#.......#...........#...#...#.#.#...#.......#...#.....#.....#.#....5#.....#.....#"
"#.#.#.#####.#.#.#.#.###.#.#.#.###.#.#.###.#####.#.#.#.#.###.#.#.#.#.#.#.#.#.#.#.#.###############.#.###.#.#.#.###.###.#.#.#.#.###.#.#.###.#####.#.#.#####.###.###.#.#.#.###.#.#.#.#.#"
"#.........#.#...#.....#...#.#.#...#.....#...#.....#.....#...#.#.#...#.#.....#...#.............#...#.#.....#.#.....#...........#...#.............#...#...#.#...#...#.#.......#...#...#"
"#.#.#.#.#.#.###.#####.###.#.#.#.#.#.###.#.###.#.###.#.#.#.#.#.###.#.#.#.#####.#.#####.#####.###.#.#.#.#############.#####.#.###.#.###.#.#.#.#.#####.#.#.#.#.#.#.#.#.#.#.#.#.#.#######"
"#4#.#.....#.#.....#...#...#...#...#...#.#.#...#...#...#.#.....#...#...#.........#...#.#.....#...#.#...#.#.....#.#.#...#...#.#...#.#.......#.#.......#...#.......#.#.#.#.#.........#.#"
"#####.#.###.###.###.#####.###.#.#.###.#.#.#.#.#.#.#.#.#####.#.#.#.#.###.#.#.#.#.#.#.#.#.#.###.#.#.###.#.#.#.#.#.#.###.#.#.###.#.#.###.#.#.#.###.###.#.#.#.#.#####.#.###.#.#####.###.#"
"#.......#...#...#...#.#.#.........#...#.#7#.#...#...#.......#.#.#.#.....#.#.....#.....#.....#...#.#.#.#...........#...#.....#.............#...............#.....#.........#...#.....#"
"#####################################################################################################################################################################################")))))
;; returns coordinates of the given waypoint in the given duct map
;; (top-left room is #(0 0), down/up is +/- on the x axis, right/left is
;; +/- on the y axis)
(define (coordinates-of duct-map waypoint)
(define coordinates-list
(map list->vector (cartesian-product (range (array-end duct-map 0))
(range (array-end duct-map 1)))))
(findf (lambda (coordinates)
(equal? (array-ref duct-map coordinates) waypoint))
coordinates-list))
;; returns a list of all coordinates in the given duct map neighbouring
;; the given ones at which there is an open space; put another way, this is
;; the successor function for graph representation of the duct map
(define (neighbouring-open-space-coordinates duct-map coordinates)
(define (neighbouring-coordinates coordinates)
(define x (vector-ref coordinates 0))
(define y (vector-ref coordinates 1))
(list (vector (+ x 1) y)
(vector (- x 1) y)
(vector x (+ y 1))
(vector x (- y 1))))
(define (in-map? coordinates)
(define x (vector-ref coordinates 0))
(define y (vector-ref coordinates 1))
(and (and (>= x 0) (< x (array-end duct-map 0)))
(and (>= y 0) (< y (array-end duct-map 1)))))
(define (open-space? coordinates)
(not (equal? (array-ref duct-map coordinates) #\#)))
(filter (lambda (coordinates)
(and (in-map? coordinates) (open-space? coordinates)))
(neighbouring-coordinates coordinates)))
;; returns a vector of minimum distances from the given initial vertex to the
;; given target vertices in the graph specified by the given successor function
;; (the returned vector contains at index i the distance to the i-th target)
(define (distances-to-targets initial-vertex successors targets)
;; returns the path the traversal took to reach the given vertex
(define (traversed-path vertex)
(define predecessor (hash-ref predecessor-references vertex))
(cond
[(equal? predecessor #f)
; vertex is the initial vertex
(list vertex)]
[else
(append (traversed-path predecessor) (list vertex))]))
; use breadth-first search to find shortest paths/minimum distances to all
; specified target vertices
(define seen-vertices (mutable-set))
(set-add! seen-vertices initial-vertex)
(define to-be-visited-vertices (make-queue))
(enqueue! to-be-visited-vertices initial-vertex)
(define predecessor-references (make-hash))
(hash-set! predecessor-references initial-vertex #f)
(define distances-vector (make-vector (length targets)))
(define num_found_distances 0)
(for ([_ (in-naturals)])
#:break (= num_found_distances (length targets))
(define vertex (dequeue! to-be-visited-vertices))
(define successor-vertices (successors vertex))
(define unseen-successor-vertices
(filter-not ((curry set-member?) seen-vertices) successor-vertices))
(for ([successor-vertex unseen-successor-vertices])
(set-add! seen-vertices successor-vertex)
(enqueue! to-be-visited-vertices successor-vertex)
(hash-set! predecessor-references successor-vertex vertex))
; check if vertex is a target vertex
(define i (index-of targets vertex))
(cond
[(not (equal? i #f))
; vertex is the i-th target vertex, so record its distance
(define distance (- (length (traversed-path vertex)) 1))
(vector-set! distances-vector i distance)
(set! num_found_distances (+ num_found_distances 1))]))
distances-vector)
;; returns an array of minimum pairwise distances between the given vertices
;; in the graph specified by the given successor function (the returned array
;; contains at index #(i j) the distance between the i-th and the j-th target)
; XXX unnecessarily determines each distance twice, could be rewritten not to
(define (distances-between-targets successors targets)
; call distances-to-targets with each target as initial vertex and store
; the results in an array
(define distances-matrix
(make-array (shape 0 (length targets) 0 (length targets))))
(for ([i (in-range (length targets))])
(define target-i (list-ref targets i))
(define distances-vector-i
(distances-to-targets target-i successors targets))
(for ([j (in-range (vector-length distances-vector-i))])
(array-set! distances-matrix
(vector i j)
(vector-ref distances-vector-i j))))
distances-matrix)
;; returns an array of minimum pairwise distances between the given waypoints
;; in the given duct map (the returned array contains at index #(i j)
;; the distance between the i-th and the j-th waypoint)
(define (distances-between-waypoints duct-map waypoints)
(define successors ((curry neighbouring-open-space-coordinates) duct-map))
(define targets (map ((curry coordinates-of) duct-map) waypoints))
(distances-between-targets successors targets))
;; returns the length of the given path in a complete graph specified by
;; the given distance matrix (vertices are represented as indices
;; to the matrix)
(define (path-length distances-matrix path)
(for/fold ([path-length 0])
([i (in-range (- (length path) 1))])
(define vertex (list-ref path i))
(define next-vertex (list-ref path (+ i 1)))
(define distance-to-next-vertex
(array-ref distances-matrix (vector vertex next-vertex)))
(+ path-length distance-to-next-vertex)))
;; returns the length of the shortest path beginning with the given vertex
;; and visiting all other vertices in a complete graph specified by the given
;; distance matrix
(define (shortest-hamiltonian-path distances-matrix initial-vertex)
(define vertices (range (array-end distances-matrix 0)))
(define vertices-to-visit (remove initial-vertex vertices))
(define possible-paths
(map (lambda (permutation)
(append (list initial-vertex) permutation))
(permutations vertices-to-visit)))
(argmin ((curry path-length) distances-matrix) possible-paths))
;; solution to part one of the puzzle
(define (solution1)
; prepare a distance matrix specifying a complete graph where vertices are
; waypoints in the given duct map and edge weights are distances between them
(define waypoints '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7))
(define distances-matrix (distances-between-waypoints duct-map waypoints))
; in this graph, find the length of the shortest Hamiltonian path beginning
; with vertex 0 (corresponds to waypoint #\0)
(define shortest-path (shortest-hamiltonian-path distances-matrix 0))
(path-length distances-matrix shortest-path))
;; returns the length of the shortest path beginning and ending with the given
;; vertex and visiting all other vertices in between in a complete graph
;; specified by the given distance matrix
; XXX a cycle doesn't really need an initial vertex
(define (shortest-hamiltonian-cycle distances-matrix initial-vertex)
(define vertices (range (array-end distances-matrix 0)))
(define vertices-to-visit (remove initial-vertex vertices))
(define possible-paths
(map (lambda (permutation)
(append (list initial-vertex) permutation (list initial-vertex)))
(permutations vertices-to-visit)))
(argmin ((curry path-length) distances-matrix) possible-paths))
;; solution to part two of the puzzle
(define (solution2)
; prepare a distance matrix specifying a complete graph where vertices are
; waypoints in the given duct map and edge weights are distances between them
(define waypoints '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7))
(define distances-matrix (distances-between-waypoints duct-map waypoints))
; in this graph, find the length of the shortest Hamiltonian cycle
(define shortest-path (shortest-hamiltonian-cycle distances-matrix 0))
(path-length distances-matrix shortest-path))