aboutsummaryrefslogtreecommitdiff
path: root/day18.scm
blob: b32c60a138f38f36a3e2792573e7d2c986bfcbe5 (plain)
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
(define-module (day18)
  #:use-module (ice-9 match)
  #:use-module (ice-9 q)
  #:use-module (ice-9 rdelim)
  #:use-module (ice-9 regex)
  #:use-module (srfi srfi-9)
  #:use-module (srfi srfi-11))

(define +line-regex+ (make-regexp "^([0-9]+),([0-9]+)$"))

(define (read-coord port)
  (define line (read-line port))
  (if (eof-object? line)
      (values #f #f)
      (let ((mtch (regexp-exec +line-regex+ line)))
        (if mtch
            (values (string->number (match:substring mtch 1))
                    (string->number (match:substring mtch 2)))
            (error "Expecting pair of integer coordinates 'a,b'." line)))))

(define* (make-map
          #:key (port (current-input-port)) (maze-size 71) (steps 1024))
  (define mp (make-typed-array 'b #f maze-size maze-size))
  (define (iter left)
    (unless (zero? left)
      (let-values (((i j) (read-coord port)))
        (unless i (error "Insufficient input."))
        (array-set! mp #t i j))
      (iter (- left 1))))
  (iter steps)
  mp)

(define-record-type <node> (make-node i j dist) node?
                    (i node-i)
                    (j node-j)
                    (dist node-dist))

(define (solve mp)
  (define size (car (array-dimensions mp)))
  (define visited (make-typed-array 'b #f size size))
  (array-copy! mp visited)
  (define q (make-q))
  (enq! q (make-node (- size 1) (- size 1) 0))
  (define (maybe-add! i j dist)
    (when (and (array-in-bounds? mp i j) (not (array-ref visited i j)))
      (enq! q (make-node i j dist))
      (array-set! visited #t i j)))
  (define (add-next! node)
    (match-let ((($ <node> i j dist) node))
      (for-each (lambda (di dj) (maybe-add! (+ i di) (+ j dj) (+ dist 1)))
                '(1 -1 0 0) '(0 0 1 -1))))
  (let bfs ()
    (if (q-empty? q)
        #f                              ; Not connected
        (match-let (((and node ($ <node> i j dist)) (deq! q)))
          (if (= 0 i j)
              dist
              (begin (add-next! node) (bfs)))))))

(define (part1 port)
  (solve (make-map #:port port)))

(define* (find-cutter
          #:key (port (current-input-port)) (maze-size 71))
  (define mp (make-typed-array 'b #f maze-size maze-size))
  (let iter ()
    (let-values (((i j) (read-coord port)))
      (if i
          (begin (array-set! mp #t i j)
                 (if (solve mp) (iter) (cons i j)))
          #f))))

(define (part2 port)
  (find-cutter #:port port))