(ql:quickload :cl-ppcre) (defclass monkey () ((items :accessor items :initarg :items :type list) (op :accessor op :initarg :op :type function) (test :accessor test :initarg :test :type function) (actions :accessor actions :initarg :actions :type list) (inspections :accessor inspections :initarg :inspections :type number))) (defparameter *regexp* "Monkey (\\d+):\\n Starting items: ([0-9, ]+)\\n Operation: new = old ([\*\+]) (\\d+|\\w+)\\n Test: divisible by (\\d+)\\n If true: throw to monkey (\\d+)\\n If false: throw to monkey (\\d+)") (defun str-to-list (str) "Parses comma-separated string of integers in STR into a list of integers." (loop :for item :in (ppcre:split ", " str) :collect (parse-integer item))) (defun make-op (operation operand) "Returns a function matching the description given by STR." (if (equal operand "old") (if (equal operation "*") (lambda (old) (* old old)) (lambda (old) (+ old old))) (if (equal operation "*") (lambda (old) (* old (parse-integer operand))) (lambda (old) (+ old (parse-integer operand)))))) (defun make-test (str) "Returns a function testing for divisibility by the number given in STR." (lambda (level) (zerop (mod level (parse-integer str))))) (defun throw-item (monkeys src dest) "Removes the first item held by the monkey at position SRC in MONKEYS and adds it to the end of the items held by the monkey at position DEST in MONKEYS." (progn (setf (items (nth dest monkeys)) (append (items (nth dest monkeys)) (list (car (items (nth src monkeys)))))) (setf (items (nth src monkeys)) (cdr (items (nth src monkeys)))))) (defun parse-input (input) "Parses INPUT into a list of monkey objects." (loop :for monkey :in (ppcre:split "\\n\\n" (uiop:read-file-string input)) :collect (ppcre:do-register-groups (id items operation operand test true false) (*regexp* monkey) (return (make-instance 'monkey :items (str-to-list items) :op (make-op operation operand) :test (make-test test) :actions (list (parse-integer id) (parse-integer true) (parse-integer id) (parse-integer false)) :inspections 0))))) (defun run-round (monkeys div) (let ((level 0)) (loop :for monkey :in monkeys :do (loop :for item :in (items monkey) :do (progn (setf (inspections monkey) (1+ (inspections monkey))) (setq level (funcall (op monkey) item)) (setq level (floor level div)) (setf (items monkey) (cons level (cdr (items monkey)))) (if (funcall (test monkey) level) (throw-item monkeys (first (actions monkey)) (second (actions monkey))) (throw-item monkeys (third (actions monkey)) (fourth (actions monkey))))))) monkeys)) (defun solution (input div rounds) "Run ROUNDS number of rounds on INPUT and use divisor DIV." (let ((monkeys (parse-input input))) (loop :for round :upto (1- rounds) :do (run-round monkeys div)) (reduce '* (subseq (sort (loop :for monkey :in monkeys :collect (inspections monkey)) '>) 0 2)))) (solution "./input" 3 20) (solution "./input" 1 10000)