;; monkeys https://adventofcode.com/2022/day/11 (require 'cl-ppcre) (defun parse-integer-or-symbol (str) (let ((maybe-int (parse-integer str :junk-allowed t))) (if maybe-int maybe-int (intern (string-upcase str))))) (defun line-to-sexp (line) (mapcar (lambda (str) (mapcar #'parse-integer-or-symbol (cl-ppcre:split " " (string-trim " " str)))) (cl-ppcre:split "\\n" line))) (defun monkey-struct-to-instance (monkey-struct) (destructuring-bind ((monkey ordering-number) (starting items &rest inventory-list) (operation new eq old operation operation-number) (test divisible by divisible-number) (test tru th to monk true-monkey-number) (test fals th to monk false-monkey-number)) monkey-struct `(list 'monkey ,ordering-number 'inventory ',inventory-list 'operation '(lambda (item) (,operation item ,operation-number)) 'test ,divisible-number))) (defclass monkey () ((order-number :reader order-number :initarg :number) (inventory :accessor inventory :initarg :inventory) (operation :reader operation :initarg :operation) (test :reader test :initarg :test) (inspection-counter :reader inspection-counter :initform 0))) (defmethod print-object ((obj monkey) stream) (print-unreadable-object (obj stream :type t) (with-accessors ((order-number order-number ) (inventory inventory) (inspection-counter inspection-counter)) obj (format stream "~a, with objects: ~a; count: ~a" order-number inventory inspection-counter)))) (defun new-from-old-function (operation operand1 operand2) (eval `(lambda (old) (,operation ,operand1 ,operand2)))) ; unhyginic macro (defun monkey-struct-to-instance (monkey-struct) (destructuring-bind ((monkey ordering-number) (starting items &rest inventory-list) (operation new eq operand1 operation-sign operand2) (test divisible by divisible-number) (test tru th to monk true-monkey-number) (test fals th to monk false-monkey-number)) monkey-struct (let ((operation-fun (new-from-old-function operation-sign operand1 operand2)) (test-fun (lambda (item-worry) (if (= 0 (mod item-worry divisible-number)) true-monkey-number false-monkey-number)))) (make-instance 'monkey :test test-fun :operation operation-fun :inventory inventory-list :number ordering-number)))) (defun monkey-one-item-action (monkey) (let ((item-worry (first (inventory monkey) ))) (when item-worry (setf (inventory monkey) (cdr (inventory monkey))) (setq item-worry (funcall (operation monkey) item-worry)) ;; (setq item-worry (floor (/ item-worry 3))) (incf (slot-value monkey 'inspection-counter)) ;; returning (target-monkey-num thrown-item) (list (funcall (test monkey) item-worry) item-worry)))) (ql:quickload 'alexandria) (defun monkey-turn (monkey) (let ((thrown-hash (make-hash-table :test 'equal))) (loop for i from 1 to (length (inventory monkey)) do (let ((throw-result (monkey-one-item-action monkey))) ;; (format t "~s ~%" throw-result) (setf (gethash (first throw-result) thrown-hash) (push (second throw-result) (gethash (first throw-result) thrown-hash))))) (maphash (lambda (key value-list) (setf (gethash key thrown-hash) (reverse (gethash key thrown-hash)))) thrown-hash) thrown-hash)) ;; returns hashmap of which values go where (defun monkeys-round (monkey-array) ;; for each monkey from array: ;; do it's turn, then for each record in resulting hashmap append list of transferred items to correct monkey (loop for monkey across monkey-array do (let ((turn-result (monkey-turn monkey))) (maphash (lambda (target-monkey-num items-list) (setf (inventory (aref monkey-array target-monkey-num)) (append (inventory (aref monkey-array target-monkey-num)) items-list))) turn-result)))) (progn (defparameter *11-test-structs* (coerce (mapcar #'monkey-struct-to-instance (mapcar #'line-to-sexp *11-test-input*)) 'vector)) (monkeys-round *11-test-structs*) (loop for i from 1 to 20 do (monkeys-round *11-test-structs*)) (sort (mapcar #'inspection-counter (coerce *11-test-structs* 'list)) #'>)) ;; PART 1 on input data (progn (defparameter *11-input-paragraphs* (cl-ppcre:split "\\n\\n" (uiop:read-file-string "day11-input.txt"))) (defparameter *11-input-structs* (coerce (mapcar #'monkey-struct-to-instance (mapcar #'line-to-sexp *11-input-paragraphs*)) 'vector)) (loop for i from 1 to 10000 do (progn (monkeys-round *11-input-structs*) (format t "turn ~a~%" i))) (apply #'* (subseq (sort (mapcar #'inspection-counter (coerce *11-input-structs* 'list)) #'>) 0 2))) ;;; PART 2.