-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path06.ss
executable file
Β·346 lines (298 loc) Β· 9.63 KB
/
06.ss
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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
#!/usr/bin/env scheme --script
(load "utils.ss")
(load "struct.ss")
;; --- Day 6: Universal Orbit Map ---
; You've landed at the Universal Orbit Map facility on Mercury. Because
; navigation in space often involves transferring between orbits, the orbit
; maps here are useful for finding efficient routes between, for example, you
; and Santa. You download a map of the local orbits (your puzzle input).
;
; Except for the universal Center of Mass (COM), every object in space is in
; orbit around exactly one other object. An orbit looks roughly like this:
;
; \
; \
; |
; |
; AAA--> o o <--BBB
; |
; |
; /
; /
;
; In this diagram, the object BBB is in orbit around AAA. The path that BBB
; takes around AAA (drawn with lines) is only partly shown. In the map data,
; this orbital relationship is written AAA)BBB, which means "BBB is in orbit
; around AAA".
;
; Before you use your map data to plot a course, you need to make sure it
; wasn't corrupted during the download. To verify maps, the Universal Orbit Map
; facility uses orbit count checksums - the total number of direct orbits (like
; the one shown above) and indirect orbits.
;
; Whenever A orbits B and B orbits C, then A indirectly orbits C. This chain
; can be any number of objects long: if A orbits B, B orbits C, and C orbits D,
; then A indirectly orbits D.
;
; For example, suppose you have the following map:
;
; COM)B
; B)C
; C)D
; D)E
; E)F
; B)G
; G)H
; D)I
; E)J
; J)K
; K)L
;
; Visually, the above map of orbits looks like this:
;
; G - H J - K - L
; / /
; COM - B - C - D - E - F
; \
; I
;
; In this visual representation, when two objects are connected by a line, the
; one on the right directly orbits the one on the left.
;
; Here, we can count the total number of orbits as follows:
;
; D directly orbits C and indirectly orbits B and COM, a total of 3 orbits.
; L directly orbits K and indirectly orbits J, E, D, C, B, and COM, a total of 7 orbits.
; COM orbits nothing.
;
; The total number of direct and indirect orbits in this example is 42.
;
; What is the total number of direct and indirect orbits in your map data?
(define-structure orbit name direct)
;; Orbit is (make-orbit Symbol Orbit|(listof Orbit))
;; interp. (make-orbit name direct)
;; - name is Symbol
;; - direct is one of:
;; - Orbit
;; - (listof Orbit) if multiple direct orbits on object
;; Example using Orbit struct
(define J (make-orbit 'J
(list (make-orbit 'K
(list (make-orbit 'L '()))))))
(define E (make-orbit 'E
(list J
(make-orbit 'F '()))))
(define I (make-orbit 'I '()))
(define D (make-orbit 'D
(list I E)))
(define C (make-orbit 'C
(list D)))
(define G (make-orbit 'G
(list (make-orbit 'H '()))))
(define B (make-orbit 'B
(list G C)))
(define orbits2 (make-orbit 'COM
(list B)))
;; Orbit Symbol -> Orbit | False
;; Recursively looks for orbit with given name and returns either:
;; reference to an orbit or false
(define (find-orbit orbit name)
(define (fn-for-orbit o n)
(if (eq? (orbit-name o) n)
o
(fn-for-loo (orbit-direct o) n)))
(define (fn-for-loo loo n)
(cond [(null? loo) #f]
[else
(or (fn-for-orbit (car loo) n)
(fn-for-loo (cdr loo) n))]))
(fn-for-orbit orbit name))
;; Orbit Symbol -> Boolean
(define (has-orbit-direct? orbit name)
(member name (map orbit-name (orbit-direct orbit))))
(test (has-orbit-direct? orbits2 'B) '(B))
(test (has-orbit-direct? B 'L) #f)
(test (has-orbit-direct? B 'G) '(G C))
;; Orbit -> String -> Orbit | False
;; Given string of format "{name}){next}" find orbit with symbol name
;; and if found mutate its orbit-direct list bt adding orbit with symbol `next`,
;; return #f if left orbit not found.
(define (add-mapping orbit)
(lambda (mapping)
(let* [(m (string-split mapping #\)))
(name (string->symbol (car m)))
(next (make-orbit (string->symbol (cadr m)) '()))
(target (find-orbit orbit name))]
(cond [(false? target) target]
[(has-orbit-direct? target (orbit-name next)) #f]
[else
(and (set-orbit-direct! target
(cons next
(orbit-direct target)))
#t)]))))
;; Orbit (listof String) -> Orbit
;; Mutate orbits by adding all mappings to it
(define (build-orbits source mappings)
(for-each (add-mapping source) mappings))
(define example-mappings
"COM)B
B)C
C)D
D)E
E)F
B)G
G)H
D)I
E)J
J)K
K)L")
(define orbits3 (make-orbit 'COM '()))
(build-orbits orbits3 (string-split example-mappings #\newline))
(assert (equal? orbits2 orbits3))
;; Orbit -> Number
;; Count total number of direct orbits (i.e. node count)
(define (direct orbit)
;; rsf is Integer; result of direct nodes visited so far
;; todo is (listof Orbit)
(define (fn-for-o o todo rsf)
(fn-for-todo (append (orbit-direct o) todo)
(add1 rsf)))
(define (fn-for-todo todo rsf)
(cond [(null? todo) rsf]
[else
(fn-for-o (car todo)
(cdr todo)
rsf)]))
(fn-for-o orbit '() -1))
(test (direct (make-orbit 'COM '())) 0)
(test (direct (make-orbit 'COM
(list (make-orbit 'B '()))))
1)
(test (direct (make-orbit 'COM
(list (make-orbit 'B
(list (make-orbit 'C '()))))))
2)
;; Orbit -> Number
;; Count the number of direct and indirect orbits in tree;
;; Whenever A orbits B and B orbits C, then A indirectly orbits C.
(define (direct+indirect o)
(define (iter o acc)
(apply + acc
(map (lambda (o) (iter o (add1 acc))) (orbit-direct o))))
(iter o 0))
(test (direct+indirect orbits3) 42)
(define input-mappings (read-file "inputs/06.txt"))
;; If left orbit not found, mapping added to end of queue.
;; Assumes input is valid & will finish.
(define (build-orbits-revolving orbit mappings)
(cond [(null? mappings) "done building orbits"]
[else
(let [(added? ((add-mapping orbit) (car mappings)))]
(if added?
(build-orbits-revolving orbit
(cdr mappings))
(build-orbits-revolving orbit
(snoc (car mappings) (cdr mappings)))))]))
;; Setup orbit tree from input
(define O (make-orbit 'COM '()))
(build-orbits-revolving O input-mappings)
(test (direct O) (length input-mappings))
(print
(direct+indirect O))
;; --- Part Two ---
; Now, you just need to figure out how many orbital transfers you (YOU) need to
; take to get to Santa (SAN).
;
; You start at the object YOU are orbiting; your destination is the object SAN
; is orbiting. An orbital transfer lets you move from any object to an object
; orbiting or orbited by that object.
;
; For example, suppose you have the following map:
;
; COM)B
; B)C
; C)D
; D)E
; E)F
; B)G
; G)H
; D)I
; E)J
; J)K
; K)L
; K)YOU
; I)SAN
;
; Visually, the above map of orbits looks like this:
;
; YOU
; /
; G - H J - K - L
; / /
; COM - B - C - D - E - F
; \
; I - SAN
;
; In this example, YOU are in orbit around K, and SAN is in orbit around I. To
; move from K to I, a minimum of 4 orbital transfers are required:
;
; K to J
; J to E
; E to D
; D to I
;
; Afterward, the map of orbits looks like this:
;
; G - H J - K - L
; / /
; COM - B - C - D - E - F
; \
; I - SAN
; \
; YOU
;
; What is the minimum number of orbital transfers required to move from the
; object YOU are orbiting to the object SAN is orbiting? (Between the objects
; they are orbiting - not between YOU and SAN.)
;; Orbit Symbol -> (listof Symbol)
;; Find path to target node; empty if not found
(define (path-to-node start target)
(define (fn-for-o current path todo)
(cond [(eq? (orbit-name current) target) path]
[(null? (orbit-direct current)) '()]
[else
(apply append '()
(map (lambda (o)
(fn-for-todo (cons (orbit-name current) path)
(cons o todo)))
(orbit-direct current)))]))
;; (listof Symbol) (listof Orbit) -> (listof Symbol)
(define (fn-for-todo path todo)
(cond [(null? todo) path]
[else
(fn-for-o (car todo)
path
(cdr todo))]))
(reverse (fn-for-o start '() '())))
(test (dfs orbits2 'B) '(COM))
(test (dfs orbits2 'COM) '())
;; (listof X) (listof Y) -> (listof X - Y)
(define (set-diff a b)
(cond [(null? a) '()]
[(member (car a) b) (set-diff (cdr a) b)]
[else (cons (car a) (set-diff (cdr a) b))]))
;; Test from example
((add-mapping orbits3) "K)YOU")
((add-mapping orbits3) "I)SAN")
(test
(let [(to-you (path-to-node orbits3 'YOU))
(to-san (path-to-node orbits3 'SAN))]
(+ (length (set-diff to-you to-san))
(length (set-diff to-san to-you))))
4)
;; Solve part 2:
(define to-you (path-to-node O 'YOU))
(define to-san (path-to-node O 'SAN))
(print
(+ (length (set-diff to-you to-san))
(length (set-diff to-san to-you))))