From b4f8e937c70a7ba8f906d056dde5d881f7693609 Mon Sep 17 00:00:00 2001 From: efim Date: Tue, 13 Dec 2022 05:54:07 +0000 Subject: [PATCH] day 11 refactor into scratch and code --- day11-scratch.lisp | 224 +++++++++++++++++++++++++++++++++++++++++++++ day11.lisp | 217 ++++--------------------------------------- 2 files changed, 243 insertions(+), 198 deletions(-) create mode 100644 day11-scratch.lisp diff --git a/day11-scratch.lisp b/day11-scratch.lisp new file mode 100644 index 0000000..28d6ee7 --- /dev/null +++ b/day11-scratch.lisp @@ -0,0 +1,224 @@ + +;; 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)) +(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 diff --git a/day11.lisp b/day11.lisp index 86e75db..d203d23 100644 --- a/day11.lisp +++ b/day11.lisp @@ -1,102 +1,17 @@ ;; 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 - (require 'cl-ppcre) -(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 - (defun parse-integer-or-symbol (str) (let ((maybe-int (parse-integer str :junk-allowed t))) (if maybe-int maybe-int (intern (string-upcase str))))) -(parse-integer-or-symbol "7:") -(parse-integer-or-symbol "qer") - (defun line-to-sexp (line) (mapcar (lambda (str) (mapcar #'parse-integer-or-symbol (cl-ppcre:split " " (string-trim " " str)))) (cl-ppcre:split "\\n" line))) -(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 @@ -108,18 +23,6 @@ (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*)) - (defclass monkey () ((order-number :reader order-number :initarg :number) (inventory :accessor inventory :initarg :inventory) @@ -138,10 +41,6 @@ (defun new-from-old-function (operation operand1 operand2) (eval `(lambda (old) (,operation ,operand1 ,operand2)))) ; unhyginic macro -(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 (defun monkey-struct-to-instance (monkey-struct) @@ -161,65 +60,15 @@ :test test-fun :operation operation-fun :inventory inventory-list :number ordering-number)))) -(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))) + ;; (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)) -(monkey-one-item-action *test-monkey-instance*) -(inspection-counter *test-monkey-instance*) - -;; ok, one step seems to work. -;; now the full Turn will return hashmap from monkey-num to list of values which to be appended to the end, so i too should append to end \ prepend + reverse (ql:quickload 'alexandria) (defun monkey-turn (monkey) @@ -233,26 +82,6 @@ thrown-hash)) ;; returns hashmap of which values go where -(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)) - (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 @@ -264,39 +93,31 @@ (setf (inventory (aref monkey-array target-monkey-num)) (append (inventory (aref monkey-array target-monkey-num)) items-list))) turn-result)))) -;; and now yolo try to run 20 rounds on test monkey array? +(progn + (defparameter *11-test-structs* + (coerce (mapcar #'monkey-struct-to-instance (mapcar #'line-to-sexp *11-test-input*)) 'vector)) + (monkeys-round *11-test-structs*) -;; wait. I also need to cound "how many items did each monkey inspected" -;; let's keep it for tomorrow, ugh + (loop + for i from 1 to 20 + do (monkeys-round *11-test-structs*)) -;; 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 - -(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)) #'>) + (sort (mapcar #'inspection-counter (coerce *11-test-structs* 'list)) #'>)) ;; PART 1 on input data -(defparameter *11-input-paragraphs* (cl-ppcre:split "\\n\\n" (uiop:read-file-string "day11-input.txt"))) +(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)) + (defparameter *11-input-structs* + (coerce (mapcar #'monkey-struct-to-instance (mapcar #'line-to-sexp *11-input-paragraphs*)) 'vector)) -(loop - for i from 1 to 20 - do (monkeys-round *11-input-structs*)) + (loop + for i from 1 to 10000 + do (progn (monkeys-round *11-input-structs*) + (format t "turn ~a~%" i))) -(sort (mapcar #'inspection-counter (coerce *11-input-structs* 'list)) #'>) -;; 329 305 -(* 329 305) -(apply #'* (subseq (sort (mapcar #'inspection-counter (coerce *11-input-structs* 'list)) #'>) 0 2)) + (apply #'* (subseq (sort (mapcar #'inspection-counter (coerce *11-input-structs* 'list)) #'>) 0 2))) ;;; PART 2. +