;; monkeys https://adventofcode.com/2022/day/11 (defparameter *test-monkey-line* "Monkey 0: Starting items: 79, 98, 101, 66 Operation: new = old * 19 Test: divisible by 23 If true: throw to monkey 2 If false: throw to monkey 3") ;; ;; so. i'd want to parse this string into list and it be a valid macro to return a monkey class ;; and have "turn" and "round" methods over the array of monkeys (cl-ppcre:split " " (string-upcase *test-monkey-line*)) (cl-ppcre:split "\\n" (string-upcase *test-monkey-line*)) (defparameter num-plus-newline "9 ") (format t num-plus-newline) (parse-integer num-plus-newline) ; junk in string "9\\n" (mapcar (lambda (str) (cl-ppcre:split " " (string-trim str) )) (cl-ppcre:split "\\n" (cl-ppcre:regex-replace-all ":" *test-monkey-line* " "))) ;; and now to have all numbers be translated into numbers, and works into symbols ;; to get single list? ;; or list per line i suppose ;; from "macro and compilation.lisp" ;; but, we coult treat arguments as lists where we define marco (defmacro mix-and-match-2 ((x1 y1) (x2 y2)) `(list '(,x1 ,y1) '(,x1 ,y2) '(,x2 ,y1) '(,x2 ,y2))) (mix-and-match-2 (fred wilma) (tony bony)) ;; so could have defmacro that takes in 4 lists, each is a line parsed (string-trim " :" " :hello: worls: ") (mapcar (lambda (str) (mapcar #'parse-integer-or-symbol (cl-ppcre:split " " (string-trim " " str)))) (cl-ppcre:split "\\n" *test-monkey-line*)) ;; i could first for each line to (string-trim ":," lala) if convertable to number convert to number ;; and then all stringp upcase and intern (parse-integer "7:hello" :junk-allowed t) ; cool (parse-integer-or-symbol "7:") (parse-integer-or-symbol "qer") (defparameter *test-monkey-sexp* (line-to-sexp *test-monkey-line*)) ((MONKEY 0) (STARTING |ITEMS:| 79 98 101 66) (|OPERATION:| NEW = OLD * 19) (|TEST:| DIVISIBLE BY 23) (IF |TRUE:| THROW TO MONKEY 2) (IF |FALSE:| THROW TO MONKEY 3)) (defmacro monkey-struct-to-instance (((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))) `(list 'monkey ,ordering-number 'inventory ',inventory-list 'operation '(lambda (item) (,operation item ,operation-number)) 'test ,divisible-number)) (monkey-struct-to-instance *test-monkey-sexp*) (monkey-struct-to-instance ((MONKEY 0) (STARTING |ITEMS:| 79 98 101 66) (|OPERATION:| NEW = OLD * 19) (|TEST:| DIVISIBLE BY 23) (IF |TRUE:| THROW TO MONKEY 2) (IF |FALSE:| THROW TO MONKEY 3))) (defmacro list-in-list (((first second) (third fourth))) `(list ,second ,fourth)) (list-in-list ((1 2) ("hello" "world"))) (nsubst *test-monkey-sexp*) ;; maybe do another macro on top of macro then? (defmacro put-struct-into-macro (my-macro var) `(,my-macro ,(eval var))) (put-struct-into-macro monkey-struct-to-instance *test-monkey-sexp*) ;; LOL, this is certainly not the way to go ;; after getting help from CL Matrix: ;; i need to use DESTRUCTURING-BIND (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))) (monkey-struct-to-instance *test-monkey-sexp*) ;; and there's METABANG-BIND facility ;; now - have monkey class. what would connect the different data about monkey ;; and after that - initialize the array. and write functions like "step" on the monkey and "round" on the array? ;; first - let's read in a paragraph at a time? (defparameter *11-test-input* (cl-ppcre:split "\\n\\n" (uiop:read-file-string "day11-test.txt"))) ;; yay. thank you Cookbook : https://lispcookbook.github.io/cl-cookbook/files.html Reading Files (mapcar #'monkey-struct-to-instance (mapcar #'line-to-sexp *11-test-input*)) (funcall (new-from-old-function '+ 2 'old) 7) ;; here doing eval and defun, because macro just been inserting operand1 instead of symbod to which it references? ;; this is not good, but ok =C (rem 7 3) (mod 7 3) (monkey-struct-to-instance *test-monkey-sexp*) (defparameter *test-monkey-instance* (make-instance 'monkey :test (lambda (num) (if (= num 4) 1 2)) :operation (lambda (num) (+ num 1)) :inventory '(1 2 3) :number 4)) (funcall (test *test-monkey-instance*) 6) ;; and i don't really need the ordering-number, but ok (defparameter *11-test-structs* (coerce (mapcar #'monkey-struct-to-instance (mapcar #'line-to-sexp *11-test-input*)) 'vector)) ;; now i want Turn and Round ;; turn - monkey iterates over all inventory. ;; inspects - ;; - apply Operation ;; - the worry / 3 ; default by player ;; - test -> return to which monkey to send ;; ;; i probably want to not tie up "turn" with array of monkeys ;; and only reference the array when codiyng up ;; round - each monkey gets a turn. ;; monkeys receive thrown items immediately when they are thrown (lambda (worry) (* worry 13)) (funcall (slot-value (aref *11-test-structs* 2) 'operation) 5) (let ((old 2)) (eval (funcall '* old old))) (funcall (test (aref *11-test-structs* 0)) 23) ;; wow. some operations are new = old * old. wow. now that's not cool ;; but can be done i think ;; yup. only with macros i guess? and unhyginic? ;; and it works, but again with eval. to create lambda from the quoted description of lambda ;; and we're back to doing the turn? ;; or do i implement methods for monkey inspect and test? ugh (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)))) (defparameter *test-monkey-instance* (make-instance 'monkey :test (lambda (num) (if (= num 4) 9 8)) :operation (lambda (num) (+ num 1)) :inventory '(100 60 30) :number 4 :mod 5)) (monkey-one-item-action *test-monkey-instance*) (inspection-counter *test-monkey-instance*) (defparameter *test-monkey-turn* (monkey-turn *test-monkey-instance*)) (alexandria:hash-table-alist *test-monkey-turn*) ; looks ok, but it still should be reversed? (alexandria:maphash-values #'reverse *test-monkey-turn*) (reverse (gethash 8 *test-monkey-turn*)) (maphash (lambda (key value-list) (setf (gethash key *test-monkey-turn*) (reverse (gethash key *test-monkey-turn*)))) *test-monkey-turn*) ;; so MAPHASH and MAPHASH-VALUES do not change hashmap, ugh (defparameter *test-hash* (make-hash-table :test 'equal)) (defparameter *test-hash* '(1)) (push 11 (gethash "hello" *test-hash*)) (push 11 *test-hash*) (setf (gethash "hello" *test-hash*) (push 11 (gethash "hello" *test-hash*))) (setf (gethash "why" *test-hash*) 11) ;; wtf. why setf doesn't change content of hash key? ;; well, that's because I use `eq for comparison. hello ;; now have function for MONKEY-TURN, returning hashmap of transferred items (append '(1 2 3) '(5 6)) ;; and now yolo try to run 20 rounds on test monkey array? ;; wait. I also need to cound "how many items did each monkey inspected" ;; let's keep it for tomorrow, ugh ;; on the next task, i'll start separating my daily scratch and final code ;; it's too much noise between the code ;; ok, added that (sort (mapcar #'inspection-counter (coerce *11-input-structs* 'list)) #'>) ;; 329 305 (* 329 305) ;;; PART 2. ;; let's just hardcode change to "releaf level change" ;; find another way to keep your worry levels manageable. ;; otherwise turns take really long time to compute. ;; what's important is to retain all TEST - so when i make numbers smaller, they have to keep all same MOD for all same test values. ;; so, what? let's go to sleep ;; now. if i read in all those "divisible by" so. what i want is to preserve MOD x ;; so, could get lowest common multiple and get mod from that? and set it in all monkeys, ;; or better - define it as class \ static attribute (defparameter *11-input-paragraphs* (cl-ppcre:split "\\n\\n" (uiop:read-file-string "day11-input.txt"))) (defparameter *11-input-structs* nil) (defparameter *11-input-structs* (coerce (mapcar #'monkey-struct-to-instance (mapcar #'line-to-sexp *11-input-paragraphs*)) 'vector)) (loop for monkey across *11-input-structs* do (when (not (= 0 (mod (mod-reductor-2 monkey) (monkey-mod monkey)))) (setf (slot-value monkey 'mod-reductor-2) (* (mod-reductor-2 monkey) (monkey-mod monkey))))) (mod 1 3) (setf (slot-value *test-monkey-instance* 'mod-reductor-2) 1) ;; yep. ;; now need to take mod by this one? and then it will preserve mod by any of previous, right? (mod (+ 9699690 177) 5) (mod 177 5) ;; now substitute my worry by mod over "common multiple" and run 20k cycles (mod 4 7) (progn (defparameter *11-test-input* (cl-ppcre:split "\\n\\n" (uiop:read-file-string "day11-test.txt"))) (defparameter *11-test-structs* (coerce (mapcar #'monkey-struct-to-instance (mapcar #'line-to-sexp *11-test-input*)) 'vector)) ;; oh, i need to reset and recalculate the lowest common multiple for test input. ;; yup (setf (slot-value *test-monkey-instance* 'mod-reductor-2) 1) (loop for monkey across *11-test-structs* do (when (not (= 0 (mod (mod-reductor-2 monkey) (monkey-mod monkey)))) (setf (slot-value monkey 'mod-reductor-2) (* (mod-reductor-2 monkey) (monkey-mod monkey))))) (loop for i from 1 to 10000 do (progn (monkeys-round *11-test-structs*) (format t "turn ~a~%" i))) (print (apply #'* (subseq (sort (mapcar #'inspection-counter (coerce *11-test-structs* 'list)) #'>) 0 2))) (setf (slot-value *test-monkey-instance* 'mod-reductor-2) 1)) ;; yeah