aboutsummaryrefslogtreecommitdiff
path: root/day19.scm
blob: 374994ce1e976583e6128ab33dce8f21a92998f0 (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
(define-module (day19)
  #:use-module (ice-9 rdelim)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-158))

(define +allowed-chars+ (char-set #\w #\u #\b #\r #\g))

(define (string->rope-index s)
  (let split ((start 0) (acc '()))
    (define end (string-contains s ", " start))
    (define next-s (substring s start (or end (string-length s))))
    (define next-acc (cons next-s acc))
    (unless (string-every +allowed-chars+ next-s)
      (error "Unrecognized rope color in spec (expected: w,u,b,r,g)." next-s))
    (if end
        (split (+ end 2) next-acc)
        (list->vector (sort! next-acc string<?)))))

(define* (prefix-index ropes s #:optional
                       (sstart 0) (send (string-length s))
                       (rstart 0) (rend (vector-length ropes)))
  (cond ((>= rstart rend) #f)
        ((= rend (+ rstart 1)) rstart)
        (else (let* ((rmid (quotient (+ rstart rend) 2))
                     (rs (vector-ref ropes rmid)))
                (if (string<= rs s 0 (string-length rs) sstart send)
                    (prefix-index ropes s sstart send rmid rend)
                    (prefix-index ropes s sstart send rstart rmid))))))

(define (generate-prefixes ropes s sstart)
  (make-coroutine-generator
   (lambda (yield)
     (let loop ((index (vector-length ropes)) (send (string-length s)))
       (define next (prefix-index ropes s sstart send 0 index))
       (when next
         (let* ((ns (vector-ref ropes next))
                (part (string-prefix-length s ns sstart send)))
           (unless (zero? part)
             (when (= part (string-length ns)) (yield ns))
             (loop next (+ sstart part)))))))))

(define (rope-count-matches ropes line)
  (define dp (make-vector (+ (string-length line) 1) 1))
  (let loop! ((i (- (string-length line) 1)))
    (define (matches-with-prefix p)
      (vector-ref dp (+ i (string-length p))))
    (unless (negative? i)
      (vector-set! dp i
                   (generator-fold + 0
                                   (gmap matches-with-prefix
                                         (generate-prefixes ropes line i))))
      (loop! (- i 1))))
  (vector-ref dp 0))

(define (generate-towel-counts port)
  (define ropestring (read-line port))
  (when (eof-object? ropestring) (error "File is empty!"))
  (define ropes (string->rope-index ropestring))
  (define blank (read-line port))
  (unless (equal? "" blank) (error "Expected a blank line." blank))
  (define (count-matches line)
    (unless (string-every +allowed-chars+ line)
      (error "Unrecognized rope color in line (expected: w,u,b,r,g)." line))
    (rope-count-matches ropes line))
  (gmap count-matches
        (gtake-while (lambda (s) (not (string=? "" s)))
                     (lambda () (read-line port)))))

(define* (part1 #:optional (port (current-input-port)))
  (generator-count positive? (generate-towel-counts port)))

(define* (part2 #:optional (port (current-input-port)))
  (generator-fold + 0 (generate-towel-counts port)))