94 lines
3.1 KiB
Common Lisp
94 lines
3.1 KiB
Common Lisp
(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)
|