day19.scm (7572B)
1 ; Copyright (c) 2023, Natacha Porté 2 ; 3 ; Permission to use, copy, modify, and distribute this software for any 4 ; purpose with or without fee is hereby granted, provided that the above 5 ; copyright notice and this permission notice appear in all copies. 6 ; 7 ; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 ; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 ; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 ; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 ; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 ; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 ; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 15 (import (chicken io) (chicken sort) (chicken string) 16 comparse 17 srfi-1 18 srfi-14 19 srfi-69) 20 21 ;;;;;;;;;;;;;;;;; 22 ;; Input parsing 23 24 (define (as-number parser) 25 (bind (as-string parser) 26 (lambda (s) 27 (result (string->number s))))) 28 29 (define spaces 30 (one-or-more (is #\space))) 31 32 (define digit 33 (in char-set:digit)) 34 35 (define digits 36 (as-number (one-or-more digit))) 37 38 (define state-name 39 (as-string (one-or-more (in char-set:letter)))) 40 41 (define category-name 42 (in #\x #\m #\a #\s)) 43 44 (define category-assoc 45 (sequence* ((name category-name) 46 (_ (is #\=)) 47 (value digits)) 48 (result (list name value)))) 49 50 (define conditional-transition 51 (sequence* ((category category-name) 52 (operator (in #\< #\>)) 53 (value digits) 54 (_ (is #\:)) 55 (target state-name)) 56 (result (list category operator value target)))) 57 58 (define conditional-transitions 59 (sequence* ((first conditional-transition) 60 (rest (zero-or-more 61 (preceded-by (is #\,) conditional-transition)))) 62 (result (cons first rest)))) 63 64 (define process-line 65 (sequence* ((label state-name) 66 (_ (is #\{)) 67 (transitions conditional-transitions) 68 (_ (is #\,)) 69 (fallback state-name) 70 (_ (char-seq "}\n"))) 71 (result (list label transitions fallback)))) 72 73 (define part-line 74 (sequence* ((_ (is #\{)) 75 (first category-assoc) 76 (rest (zero-or-more (preceded-by (is #\,) category-assoc))) 77 (_ (char-seq "}\n"))) 78 (result (cons first rest)))) 79 80 (define all-data 81 (sequence* ((processes (one-or-more process-line)) 82 (_ (is #\newline)) 83 (parts (one-or-more part-line))) 84 (result (list processes parts)))) 85 86 (define data (parse all-data (read-string))) 87 (define verbose (< (length (cadr data)) 10)) 88 (when verbose (write-line (conc "Input: " data))) 89 90 ;;;;;;;;;;;;;;;;; 91 ;; First Puzzle 92 93 (define process-hash 94 (let ((result (make-hash-table))) 95 (let loop ((todo (car data))) 96 (if (null? todo) 97 result 98 (begin 99 (hash-table-set! result (caar todo) (cdar todo)) 100 (loop (cdr todo))))))) 101 102 (define (part-matches? condition part) 103 (let* ((cat-name (car condition)) 104 (operator (cadr condition)) 105 (ref-value (caddr condition)) 106 (part-value (cadr (assv cat-name part)))) 107 (case operator 108 ((#\<) (< part-value ref-value)) 109 ((#\>) (> part-value ref-value)) 110 (else (assert #f "Unknown operator " operator))))) 111 112 (define (apply-process process part) 113 (let loop ((conds (car process)) 114 (fallback (cadr process))) 115 (cond ((null? conds) fallback) 116 ((part-matches? (car conds) part) (cadddr (car conds))) 117 (else (loop (cdr conds) fallback))))) 118 119 (define (process-part part) 120 (let loop ((state "in")) 121 (if (= 1 (string-length state)) 122 state 123 (loop (apply-process (hash-table-ref process-hash state) part))))) 124 125 (define (part-score part) 126 (if (equal? (process-part part) "R") 127 0 128 (apply + (map cadr part)))) 129 130 (write-line (conc "First puzzle: " (apply + (map part-score (cadr data))))) 131 132 ;;;;;;;;;;;;;;;;; 133 ;; Second Puzzle 134 135 (define max-val 4000) 136 (define min-val 1) 137 138 (define (cut-with condition range) 139 (let ((operator (cadr condition)) 140 (value (caddr condition))) 141 (case operator 142 ((#\<) (if (< (car range) value) 143 (cons (car range) (min (sub1 value) (cdr range))) 144 '())) 145 ((#\>) (if (> (cdr range) value) 146 (cons (max (add1 value) (car range)) (cdr range)) 147 '())) 148 (else (assert #f "Bad condition " condition))))) 149 150 (define (cut-without condition range) 151 (let ((operator (cadr condition)) 152 (value (caddr condition))) 153 (case operator 154 ((#\>) (if (< (car range) value) 155 (cons (car range) (min value (cdr range))) 156 '())) 157 ((#\<) (if (> (cdr range) value) 158 (cons (max value (car range)) (cdr range)) 159 '())) 160 (else (assert #f "Bad condition " condition))))) 161 162 (define (cut-volume cut condition volume) 163 (let ((x-range (car volume)) 164 (m-range (cadr volume)) 165 (a-range (caddr volume)) 166 (s-range (cadddr volume))) 167 (case (car condition) 168 ((#\x) (list (cut condition x-range) m-range a-range s-range)) 169 ((#\m) (list x-range (cut condition m-range) a-range s-range)) 170 ((#\a) (list x-range m-range (cut condition a-range) s-range)) 171 ((#\s) (list x-range m-range a-range (cut condition s-range))) 172 (else (assert #f "Bad condition " condition))))) 173 174 (define (volume-valid? volume) 175 (not (or (null? (car volume)) 176 (null? (cadr volume)) 177 (null? (caddr volume)) 178 (null? (cadddr volume))))) 179 180 (define (apply-process-2 process volume) 181 (let loop ((conds (car process)) 182 (fallback (cadr process)) 183 (cur-vol volume) 184 (acc '())) 185 (if (or (null? conds) (not (volume-valid? cur-vol))) 186 (filter! (lambda (item) (volume-valid? (cadr item))) 187 (cons (list fallback cur-vol) acc)) 188 (loop (cdr conds) 189 fallback 190 (cut-volume cut-without (car conds) cur-vol) 191 (cons (list (cadddr (car conds)) 192 (cut-volume cut-with (car conds) cur-vol)) 193 acc))))) 194 195 (define (range-size range) 196 (- (cdr range) (car range) -1)) 197 198 (define (volume-size vol) 199 (assert (= 4 (length vol))) 200 (* (range-size (car vol)) 201 (range-size (cadr vol)) 202 (range-size (caddr vol)) 203 (range-size (cadddr vol)))) 204 205 (define (answer-2-iter state count next-states) 206 (let loop ((todo (apply-process-2 (hash-table-ref process-hash (car state)) 207 (cadr state))) 208 (acc count) 209 (result next-states)) 210 (if (null? todo) 211 (list result acc) 212 (loop (cdr todo) 213 (if (equal? (caar todo) "A") 214 (+ acc (volume-size (cadar todo))) 215 acc) 216 (if (> (string-length (caar todo)) 1) 217 (cons (car todo) result) 218 result))))) 219 220 (define (answer-2 state-list acc) 221 (if (null? state-list) 222 acc 223 (let ((iter (answer-2-iter (car state-list) acc (cdr state-list)))) 224 (answer-2 (car iter) (cadr iter))))) 225 226 (define full-volume (list (cons min-val max-val) 227 (cons min-val max-val) 228 (cons min-val max-val) 229 (cons min-val max-val))) 230 231 (write-line (conc "Second puzzle: " 232 (answer-2 (list (list "in" full-volume)) 0)))