summaryrefslogtreecommitdiffstats
path: root/day19.lisp
diff options
context:
space:
mode:
Diffstat (limited to 'day19.lisp')
-rw-r--r--day19.lisp144
1 files changed, 144 insertions, 0 deletions
diff --git a/day19.lisp b/day19.lisp
new file mode 100644
index 0000000..8011426
--- /dev/null
+++ b/day19.lisp
@@ -0,0 +1,144 @@
+(defstruct problem workflows parts)
+
+(defstruct part x m a s)
+
+(defstruct rule var op value res)
+
+(defun read-rule (string)
+ (if (not (position #\: string))
+ (make-rule :res (subseq string 0 (1- (length string))))
+ (multiple-value-bind (value ind) (parse-integer string :start 2 :junk-allowed t)
+ (make-rule :var (aref string 0)
+ :op (aref string 1)
+ :value value
+ :res (subseq string (1+ ind))))))
+
+(defun split-by-char (char string &key (start 0))
+ (loop for i = start then (1+ j)
+ as j = (position char string :start i)
+ collect (subseq string i j)
+ while j))
+
+(defun read-workflow (string)
+ (let* ((aux (position #\{ string))
+ (name (subseq string 0 aux))
+ (rules (mapcar #'read-rule (split-by-char #\, string :start (1+ aux)))))
+ (cons name rules)))
+
+(defun read-workflows (stream)
+ (let ((ht (make-hash-table :test #'equalp)))
+ (loop for line = (read-line stream nil)
+ while (> (length line) 0)
+ do (let ((workflow (read-workflow line)))
+ (setf (gethash (car workflow) ht) (cdr workflow))))
+ ht))
+
+(defun read-part (string)
+ (let* ((x 0) (m 0) (a 0) (s 0))
+ (loop for i = 0 then (1+ j)
+ as j = (position #\= string :start i)
+ while j
+ do (let ((num (parse-integer string :start (1+ j) :junk-allowed t))
+ (chr (aref string (1- j))))
+ (cond ((equal chr #\x) (setf x num))
+ ((equal chr #\m) (setf m num))
+ ((equal chr #\a) (setf a num))
+ ((equal chr #\s) (setf s num)))))
+ (make-part :x x :m m :a a :s s)))
+
+(defun read-parts (stream)
+ (loop for line = (read-line stream nil)
+ while line
+ collect (read-part line)))
+
+(defun read-problem (filespec)
+ (with-open-file (stream filespec)
+ (make-problem :workflows (read-workflows stream) :parts (read-parts stream))))
+
+(defun part-accepted-p (part workflows)
+ (labels ((f (rules)
+ (loop for rule in rules
+ as rvar = (rule-var rule)
+ as rop = (rule-op rule)
+ as rvalue = (rule-value rule)
+ as rres = (rule-res rule)
+ if (let ((pvalue (cond ((equal rvar #\x) (part-x part))
+ ((equal rvar #\m) (part-m part))
+ ((equal rvar #\a) (part-a part))
+ ((equal rvar #\s) (part-s part)))))
+ (or (not rvar)
+ (and (equal rop #\<) (< pvalue rvalue))
+ (and (equal rop #\>) (> pvalue rvalue))))
+ return (cond ((equalp rres "A") t)
+ ((equalp rres "R") nil)
+ (t (f (gethash rres workflows)))))))
+ (f (gethash "in" workflows))))
+
+(defun solve1 (filespec)
+ (let ((problem (read-problem filespec)))
+ (loop for p in (problem-parts problem)
+ if (part-accepted-p p (problem-workflows problem))
+ sum (+ (part-x p) (part-m p) (part-a p) (part-s p)))))
+
+(defun range-size (range)
+ (assert (<= (car range) (cdr range)))
+ (1+ (- (cdr range) (car range))))
+
+(defun subrange (value op range)
+ (if
+ (equal op #\<)
+ (when (< (car range) value)
+ (cons (car range) (min (cdr range) (1- value))))
+ (when (> (cdr range) value)
+ (cons (max (car range) (1+ value)) (cdr range)))))
+
+(defun inv-subrange (value op range)
+ (if
+ (equal op #\<)
+ (subrange (1- value) #\> range)
+ (subrange (1+ value) #\< range)))
+
+(defun range-combinations (workflows rulename x m a s)
+ (cond ((equalp rulename "A")
+ (* (range-size x) (range-size m) (range-size a) (range-size s)))
+ ((equalp rulename "R") 0)
+ (t (let ((rules (gethash rulename workflows)))
+ (loop for rule in rules
+ as rvar = (rule-var rule)
+ as rop = (rule-op rule)
+ as rvalue = (rule-value rule)
+ as rres = (rule-res rule)
+ as range = (cond
+ ((equal rvar #\x) x)
+ ((equal rvar #\m) m)
+ ((equal rvar #\a) a)
+ ((equal rvar #\s) s))
+ as sub = (when rvalue (subrange rvalue rop range))
+ if sub
+ sum (range-combinations
+ workflows
+ rres
+ (if (equal rvar #\x) sub x)
+ (if (equal rvar #\m) sub m)
+ (if (equal rvar #\a) sub a)
+ (if (equal rvar #\s) sub s))
+ if (not rvar)
+ sum (range-combinations workflows rres x m a s)
+ ;; update subrange
+ do (cond
+ ((equal rvar #\x) (setf x (inv-subrange rvalue rop range)))
+ ((equal rvar #\m) (setf m (inv-subrange rvalue rop range)))
+ ((equal rvar #\a) (setf a (inv-subrange rvalue rop range)))
+ ((equal rvar #\s) (setf s (inv-subrange rvalue rop range)))))))))
+
+(defun solve2 (filespec)
+ (let* ((problem (read-problem filespec))
+ (workflows (problem-workflows problem)))
+ (range-combinations workflows "in" '(1 . 4000) '(1 . 4000) '(1 . 4000) '(1 . 4000))))
+
+(print (solve1 "data/19/example.txt")) ;; 19114
+(print (solve1 "data/19/input.txt")) ;; 495298
+(print (solve2 "data/19/example.txt")) ;; 167409079868000
+(print (solve2 "data/19/input.txt")) ;; 132186256794011
+
+