about summary refs log tree commit diff
path: root/day24.rkt
blob: 6b56406e52008765a64a3e9abba23520ca26809d (plain) (blame)
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
#lang racket

(require data/heap)

(define field
  (for/list ([line (file->lines "day24")])
    (for/list ([char line])
      char)))

(define width (length (first field)))
(define height (length field))

(define period (lcm (- width 2) (- height 2)))

(define pos (mutable-set))

(for ([(line y) (in-indexed field)])
  (for ([(c x) (in-indexed line)])
    (match c
      [#\# (for ([z (in-range 0 period)])
             (set-add! pos (vector x y z)))]
      [#\< (for ([z (in-range 0 period)])
             (set-add! pos (vector (+ 1 (modulo (- x 1 z) (- width 2))) y z)))]
      [#\> (for ([z (in-range 0 period)])
             (set-add! pos (vector (+ 1 (modulo (- x 1 (- z)) (- width 2))) y z)))]

      [#\^ (for ([z (in-range 0 period)])
             (set-add! pos (vector x (+ 1 (modulo (- y 1 z) (- height 2))) z)))]
      [#\v (for ([z (in-range 0 period)])
             (set-add! pos (vector x (+ 1 (modulo (- y 1 (- z)) (- height 2))) z)))]
      [#\. (void)])))

(define source (index-of (first field) #\.))
(define target (index-of (last field) #\.))

;; close behind source and target
(for ([z (in-range 0 period)])
  (set-add! pos (vector source -1 z))
  (set-add! pos (vector target (length field) z)))

(define (first<=? a b)
  (<= (first a) (first b)))

(define (distance pos source target period)
  (define distances (make-hash))
  (define queue (make-heap first<=?))

  (hash-set! distances source 0)
  (heap-add! queue (list 0 source))

  (let loop ()
    (if (zero? (heap-count queue))
      #f
      (let ([item (heap-min queue)])
        (heap-remove-min! queue)
        (if (equal? (vector-take (second item) 2) target)
          (first item)
          (let ([ndist (+ 1 (first item))]
                [z (modulo (+ (vector-ref (second item) 2) 1) period)])
            (for ([x (list -1 1  0 0 0)]
                  [y (list  0 0 -1 1 0)])
              (let ([neighbor (vector (+ x (vector-ref (second item) 0))
                                      (+ y (vector-ref (second item) 1))
                                      z)])
                (unless (set-member? pos neighbor)
                  (let [(dist (hash-ref distances neighbor 999999))]
                    (when (> dist ndist)
                      (hash-set! distances neighbor ndist)
                      (heap-add! queue (list ndist neighbor)))))))
            (loop)))))))

(let* ([a (distance pos (vector source 0 0) (vector target (- height 1)) period)]
       [b (distance pos (vector target (- height 1) a) (vector source 0) period)]
       [c (distance pos (vector source 0 (+ a b)) (vector target (- height 1)) period)])
  (values a                             ;; 332
          (+ a b c)))                   ;; 942