day 11 refactor into scratch and code

This commit is contained in:
efim 2022-12-13 05:54:07 +00:00
parent 59c9937950
commit b4f8e937c7
2 changed files with 243 additions and 198 deletions

224
day11-scratch.lisp Normal file
View File

@ -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

View File

@ -1,102 +1,17 @@
;; monkeys https://adventofcode.com/2022/day/11 ;; 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) (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) (defun parse-integer-or-symbol (str)
(let ((maybe-int (parse-integer str :junk-allowed t))) (let ((maybe-int (parse-integer str :junk-allowed t)))
(if maybe-int (if maybe-int
maybe-int maybe-int
(intern (string-upcase str))))) (intern (string-upcase str)))))
(parse-integer-or-symbol "7:")
(parse-integer-or-symbol "qer")
(defun line-to-sexp (line) (defun line-to-sexp (line)
(mapcar (lambda (str) (mapcar (lambda (str)
(mapcar #'parse-integer-or-symbol (cl-ppcre:split " " (string-trim " " str)))) (mapcar #'parse-integer-or-symbol (cl-ppcre:split " " (string-trim " " str))))
(cl-ppcre:split "\\n" line))) (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) (defun monkey-struct-to-instance (monkey-struct)
(destructuring-bind (destructuring-bind
@ -108,18 +23,6 @@
(test fals th to monk false-monkey-number)) monkey-struct (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))) `(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 () (defclass monkey ()
((order-number :reader order-number :initarg :number) ((order-number :reader order-number :initarg :number)
(inventory :accessor inventory :initarg :inventory) (inventory :accessor inventory :initarg :inventory)
@ -138,10 +41,6 @@
(defun new-from-old-function (operation operand1 operand2) (defun new-from-old-function (operation operand1 operand2)
(eval `(lambda (old) (eval `(lambda (old)
(,operation ,operand1 ,operand2)))) ; unhyginic macro (,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) (defun monkey-struct-to-instance (monkey-struct)
@ -161,65 +60,15 @@
:test test-fun :operation operation-fun :test test-fun :operation operation-fun
:inventory inventory-list :number ordering-number)))) :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) (defun monkey-one-item-action (monkey)
(let ((item-worry (first (inventory monkey) ))) (let ((item-worry (first (inventory monkey) )))
(when item-worry (when item-worry
(setf (inventory monkey) (cdr (inventory monkey))) (setf (inventory monkey) (cdr (inventory monkey)))
(setq item-worry (funcall (operation monkey) item-worry)) (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)) (incf (slot-value monkey 'inspection-counter))
;; returning (target-monkey-num thrown-item) ;; returning (target-monkey-num thrown-item)
(list (funcall (test monkey) item-worry) item-worry)))) (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) (ql:quickload 'alexandria)
(defun monkey-turn (monkey) (defun monkey-turn (monkey)
@ -233,26 +82,6 @@
thrown-hash)) thrown-hash))
;; returns hashmap of which values go where ;; 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) (defun monkeys-round (monkey-array)
;; for each monkey from 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 ;; 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))) (setf (inventory (aref monkey-array target-monkey-num)) (append (inventory (aref monkey-array target-monkey-num)) items-list)))
turn-result)))) 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" (loop
;; let's keep it for tomorrow, ugh 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 (sort (mapcar #'inspection-counter (coerce *11-test-structs* 'list)) #'>))
;; 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)) #'>)
;; PART 1 on input data ;; 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* (defparameter *11-input-structs*
(coerce (mapcar #'monkey-struct-to-instance (mapcar #'line-to-sexp *11-input-paragraphs*)) 'vector)) (coerce (mapcar #'monkey-struct-to-instance (mapcar #'line-to-sexp *11-input-paragraphs*)) 'vector))
(loop (loop
for i from 1 to 20 for i from 1 to 10000
do (monkeys-round *11-input-structs*)) do (progn (monkeys-round *11-input-structs*)
(format t "turn ~a~%" i)))
(sort (mapcar #'inspection-counter (coerce *11-input-structs* 'list)) #'>) (apply #'* (subseq (sort (mapcar #'inspection-counter (coerce *11-input-structs* 'list)) #'>) 0 2)))
;; 329 305
(* 329 305)
(apply #'* (subseq (sort (mapcar #'inspection-counter (coerce *11-input-structs* 'list)) #'>) 0 2))
;;; PART 2. ;;; PART 2.