;;; https://gitlab.com/Syndamia/senzill (require :senzill) (use-package :senzill.math) (use-package :senzill.collections) (use-package :senzill.io) (defparameter +rounds+ 20) (defclass monkey () ((items :initarg :items :accessor monkey-items) (inspections :initarg :inspections :accessor monkey-inspections) (operation :initarg :operation :reader monkey-operation) (test :initarg :test :reader monkey-test) (throw-true :initarg :throw-true :reader monkey-throw-true) (throw-false :initarg :throw-false :reader monkey-throw-false)) (:default-initargs :items '() :inspections 0 :operation (lambda (old) old) :test (lambda (worry-level) worry-level) :throw-true 0 :throw-false 0)) (defmethod inspect-and-throw ((m monkey) (monkey-true monkey) (monkey-false monkey)) (setf (first (monkey-items m)) (floor (/ (funcall (monkey-operation m) (first (monkey-items m))) 3))) (++1 (monkey-inspections m)) (if (funcall (monkey-test m) (first (monkey-items m))) (push-back (pop (monkey-items m)) (monkey-items monkey-true)) (push-back (pop (monkey-items m)) (monkey-items monkey-false)))) (ask-for-stream (prog-input) (let ((monkeys '()) (buffer-items '()) (buffer-operation NIL) (buffer-test NIL) (buffer-throw-t -1) (buffer-throw-f -1)) (flet ((create-operation-function (inpt) (eval `(lambda (old) (funcall ,(if (char= (char inpt 23) #\*) #'* #'+) ; In the input, there is only * and + old ,(if (char= (char inpt 25) #\o) 'old (parse-integer inpt :start 25)))))) (create-test-function (inpt) (eval `(lambda (worry-level) (zerop (% worry-level ,(parse-integer inpt :start 21))))))) ; In the input we only test for divisibility (doread-lines (inpt :read-line-options (prog-input NIL)) (cond ((zerop (length inpt))) ; Do nothing on empty lines ((char= (char inpt 0) #\M) ; Monkey #: (push-back (make-instance 'monkey :items buffer-items :operation buffer-operation :test buffer-test :throw-true buffer-throw-t :throw-false buffer-throw-f) monkeys)) ((char= (char inpt 2) #\S) ; Starting items: (setf buffer-items (loop for itms = (subseq inpt 18) then (subseq itms (+ 2 (position #\, itms))) collect (parse-integer itms :junk-allowed T) while (position #\, itms)))) ((char= (char inpt 2) #\O) ; Operation: (setf buffer-operation (create-operation-function inpt))) ((char= (char inpt 2) #\T) ; Test: (setf buffer-test (create-test-function inpt))) ((char= (char inpt 7) #\t) ; If true: (setf buffer-throw-t (parse-integer inpt :start 29))) ((char= (char inpt 7) #\f) ; If false: (setf buffer-throw-f (parse-integer inpt :start 30))))) ;; On very first line we add a monkey with default buffer values (pop monkeys) ;; The loop doesn't add the last monkey, since there is no new "Monkey #:" line (push-back (make-instance 'monkey :items buffer-items :operation buffer-operation :test buffer-test :throw-true buffer-throw-t :throw-false buffer-throw-f) monkeys) (dotimes (r +rounds+) (loop for m in monkeys do (loop for item in (monkey-items m) do (inspect-and-throw m (nth (monkey-throw-true m) monkeys) (nth (monkey-throw-false m) monkeys))))) (loop for m in monkeys maximize (monkey-inspections m) into biggest finally ;;; This loop is unnecessary, inefficient and stupid, but I'm extra lazy today (loop for m in monkeys if (< (monkey-inspections m) biggest) maximize (monkey-inspections m) into second-biggest finally (print (* biggest second-biggest)))))))