;;6:51 ;;7:49 ;; (ql:quickload '(fiveam cl-ppcre arrows)) (defstruct part x m a s) (defun part-sum (part) (with-slots (x m a s) part (+ x m a s))) (defun solve (filename) (let ((workflows (make-hash-table)) parts) (loop for line in (uiop:read-file-lines filename) do (cond ((string-equal line "")) ((eq #\{ (aref line 0)) (cl-ppcre:register-groups-bind ((#'parse-integer x m a s)) ("{x=\(\\d+\),m=\(\\d+\),a=\(\\d+\),s=\(\\d+\)}" line) (push (make-part :x x :m m :a a :s s) parts))) ((multiple-value-bind (name rule) (parse-rule line) (setf (gethash name workflows) rule))))) (reduce #'+ parts :key (alexandria:curry #'test-part workflows)))) (defun parse-tests (line-rule) (loop for test in (cl-ppcre:split "," line-rule) collect (cl-ppcre:register-groups-bind ((#'read-from-string prop op val target)) ("\(\\w\)\(<|>\)\(\\d+\):\(\\w+\)" test) (list op prop val target)))) (fiveam:test parts (fiveam:is (equal '((< S 537 GD) (> X 2440 R)) (parse-tests "s<537:gd,x>2440:R" )))) (fiveam:test solutions (fiveam:is (= 19114 (solve "eg-in"))) (fiveam:is (= 446517 (solve "input")))) (defun parse-rule (line) (cl-ppcre:register-groups-bind ((#'read-from-string name) (#'parse-tests tests) (#'read-from-string default)) ("\(\\w+\){\(.*\),\(\\w+\)}" line) (values name (lambda (part) (or (loop for (op prop val target) in tests when (funcall op (slot-value part prop) val) do (return target)) default))))) (defun test-part (workflows part &optional (start 'in)) (let ((out (funcall (gethash start workflows) part))) (cond ((eq 'A out) (part-sum part)) ((eq 'R out) 0) ((test-part workflows part out))))) (fiveam:run!)