diff --git a/day19-scratch.lisp b/day19-scratch.lisp new file mode 100644 index 0000000..1ff984d --- /dev/null +++ b/day19-scratch.lisp @@ -0,0 +1,214 @@ +;; https://adventofcode.com/2022/day/19 + +;; whelp. do i do somehow DP? generic over parameters of blueprints somehow +;; we could potentially calculate optimal step in forward time, but i'm not sure how. +;; and backward time - no easy way to know which robots will be available? +;; +;; intuitive idea, have some state of (resource1, recource2, robot1, robot2) +;; and have 2 types of steps, one - production is simple +;; then we can have different types of actions - build robot1 \ build robot2 - parallel these out +;; somehow prune ideally, but then move forward in time and get max of these. i guess it's ok plan + +;; so. +;; - each robot collects 1 of it's own resource per minute +;; so function to update state with new resources is common +;; - cost of one new robot, consumed immediately, robot is produced in 1 minute +;; costs are different and listed in the bluerint +;; i suppose overall format of the blueprint is the same, only numbers change. +;; so could hardcode the translation +;; +;; and i guess i'd have one class of "blueprint" and several instances, +;; each instance does the updating of the state. + +(in-package :day-19) + +(defclass state () + ((geodes :initform 0 :initarg :geodes) + (ore :initform 0 :initarg :ore) + (clay :initform 0 :initarg :clay) + (obsidian :initform 0 :initarg :obsidian) + (geodes-robot :initform 0 :initarg :geodes-robot) + (ore-robot :initform 1 :initarg :ore-robot) + (clay-robot :initform 0 :initarg :clay-robot) + (obsidian-robot :initform 1 :initarg :obsidian-robot))) + +(defclass blueprint () + ((ore-robot-cost :initarg :ore) + (clay-robot-cost :initarg :clay) + (obsidian-robot-cost :initarg :obsidian) + (geode-robot-cost :initarg :geode))) +;; wrote like this initially + +(make-instance 'state) + +;; i'd like to have code across building robots, checking whether robot can be build +;; to be shared. would i want to do it with classes? +;; state could have hashmaps from symbol to the amount + +;; do i want to have plists? +(defparameter *my-plist* '(:ore 1 :clay 15)) +*my-plist* +(getf *my-plist* :ore) +(getf *my-plist* :clay) +(setf (getf *my-plist* :clay) 3) +;; i guess i like that +;; then i could have robot costs as plists as well? +;; i could iterate over the plist with destructuring + +;; and blueprint can just be plist of plists +;; we don't really need generic method dispatch +;; but for state - i'd kind of want to have static field +;; to share found max and pruning + +(loop + for (resource cost) on '(:ore 3 :clay 14) by #'cddr + ;; for (resource cost) in (getf *test-blueprint* :obsidian) + collect (list (* 2 cost) 'is 'cost resource) ) +;; yes. comprehension is possible + +(defparameter *test-state* (make-instance 'state)) +(setf (getf (resources *test-state*) :ore 0) 5) +(setf (getf (robots *test-state*) :ore 0) 2) +(setf (getf (robots *test-state*) :obsidian 0) 5) + +;; now let's check my function for can-create +(print *test-state*) +(can-create-robot *test-blueprint* :ore *test-state*) +(can-create-robot *test-blueprint* :clay *test-state*) +(can-create-robot *test-blueprint* :geode *test-state*) +(can-create-robot *test-blueprint* :obsidian *test-state*) + +;; yay, i guess +;; +;; i had the error of putting quoted lists into the quoted list +;; +;; and now function to create new state with resources for a particular robot deducted? +;; and i guess with that robot already increased. +;; that function would signify passing of one turn \ minute + +(setq *test-state* (make-instance 'state)) +(setf (getf (resources *test-state*) :ore 0) 3) +(defparameter *another-state* + (create-robot *test-blueprint* :clay *test-state*)) + +;; well, that seems to work. +;; now i'd need to create blueprints from the lines. +;; then for each of the blueprint, calculate maximum of geodes. +;; multiply with the :id and sum. +;; +;; ok, i guess. i'd want a function that takes in blueprint. +;; gets initial state. and recurses searching for maximum +;; maybe even saving into blueprint as well. + +;; how would that recursion look? +;; ore is added at the end of the minute. +;; resources for building is taken out at the beginning of the minute +;; built bot is added at the end of the minute +;; +;; so, i could +;; - calculate resources to be added +;; - for each possible (on old resources) bot build +;; recurse with bot cost deducted and new resources added + +;; so next functions would be +;; - resources-to-be-collected :: just the plist of additional resources that would be +;; generated in 1 turn +;; and +;; - add-resources :: modifying operation that would update state +*test-state* +(calc-resources-to-be-collected *test-state*) +;; lol. + +(add-resources '(:spagetty 1 :tuna 2) *test-state*) +;; and it works + +;; so, now only main loop i suppose. and maybe-maybe later-later pruning + +(get-possible-bot-builds *test-blueprint* *test-state*) + +(defmethod find-max-geod (blueprints (s state) minute) + ;; (format t "in step for ~a; with ~a~%" minute s) + (if (= 25 minute) + (getf (resources s) :geode 0) + (progn + (let* ((will-collect-this-minute (calc-resources-to-be-collected s)) + (max-if-building + (loop + for bot-type in *add-types* + for state-with-new-bot = (create-robot blueprints bot-type s) + when state-with-new-bot + maximize (progn + (add-resources will-collect-this-minute state-with-new-bot) + (find-max-geod blueprints state-with-new-bot (1+ minute))))) + (if-not-building (let ((state-copy (copy-state s))) + ;; (break) + (add-resources will-collect-this-minute state-copy) + (find-max-geod blueprints state-copy (1+ minute))))) + (max (or max-if-building 0) if-not-building))))) + +;; Blueprint 1: +;; Each ore robot costs 4 ore. +;; Each clay robot costs 2 ore. +;; Each obsidian robot costs 3 ore and 14 clay. +;; Each geode robot costs 2 ore and 7 obsidian. + +;; Blueprint 2: +;; Each ore robot costs 2 ore. +;; Each clay robot costs 3 ore. +;; Each obsidian robot costs 3 ore and 8 clay. +;; Each geode robot costs 3 ore and 12 obsidian. +;; do i just test this? +(setq *test-blueprint* '(:ore (:ore 4) + :clay (:ore 2) + :obsidian (:ore 3 :clay 14) + :geode (:ore 2 :obsidian 7))) +(setq *test-state* (make-instance 'state)) + +(print (find-max-geod *test-blueprint* *test-state* 1)) +;; => 0 +;; that's because i have no ability to "wait" +;; whoops + +;; so. do i want, um. add one more attempted call after the loop in the iteration? + +;; now. the looping is serious. +;; would it work for me to order keys geode first + +;; now we seem to get geodes first, yay +;; maybe just run without printing? + +;; let's check manually that when i do state copy, the plists are independent? +(setq *test-state* (make-instance 'state)) +*test-state* +(setq *another-state* (copy-state *test-state*)) +(incf (getf (resources *another-state*) :ore 0)) +*another-state* +(add-resources '(:seeds 151) *another-state*) +(incf (getf (robots *another-state*) :obsidian 0)) + +;; oh, i didn't check that state returned from the "create bot" is independent + +(setq *test-state* (make-instance 'state)) +(add-resources '(:ore 10) *test-state*) + +(setq *another-state* (create-robot *test-blueprint* :clay *test-state*)) +;; ugh. resources stays shared. +;; WTF, why + +;; manually create new list, i guess then then do set to the 'copied state'? +;; this is unpleasant +;; so, i guess use (copy-list + +;; ok. the numbers seem ok. but this is long. +;; how do i trim this? +;; if on step 10 there's already a state with 3 obsidian machines. +;; does this state would always be ahead of +;; state on step 10 with 1 obsidian machine? +;; +;; it seems so! +;; for which reason? because if the state got 3 geode machines, it will be able to get more? +;; i suppose only when i reach case where each new step can add one more geod machine +;; only then i can guess the state is actually domeeneering? +;; but only over states with same amount of steps? + +;; ok. let's commit what i have right now and try to trim? diff --git a/day19.lisp b/day19.lisp new file mode 100644 index 0000000..38f6e53 --- /dev/null +++ b/day19.lisp @@ -0,0 +1,58 @@ +;; https://adventofcode.com/2022/day/19 +(defpackage :day-19 + (:use :cl)) +(in-package :day-19) + +(defparameter *all-types* '(:geode :obsidian :clay :ore)) + +(defclass state () + ((resources :accessor resources :initform nil :initarg :resources) + (robots :accessor robots :initform (list :ore 1) :initarg :robots))) + +(defmethod print-object ((obj state) stream) + (print-unreadable-object (obj stream :type t) + (with-slots (resources robots) + obj + (format stream "collected: ~a, with robots: ~a" + resources robots)))) + +;; example of blueprint: +(defparameter *test-blueprint* + '(:id 1 + :ore (:ore 4) + :clay (:ore 2) + :obsidian (:ore 3 :clay 14) + :geode (:ore 2 :obsidian 7))) + +;; thank you blambert & stackoverflow +;; https://stackoverflow.com/questions/11067899/is-there-a-generic-method-for-cloning-clos-objects +;; oh, but this is shallow copy and lists reused. crap + +(defmethod copy-state ((s state)) + (make-instance 'state :resources (copy-list (resources s)) + :robots (copy-list (robots s)))) + +(defmethod can-create-robot (blueprints type (s state)) + (let ((this-robot-costs (getf blueprints type))) + (loop for (resource amount) on this-robot-costs by #'cddr + always (>= (getf (resources s) resource 0) amount)))) + +(defmethod create-robot (blueprints type (s state)) + (when (can-create-robot blueprints type s) + (let ((this-robot-costs (getf blueprints type)) + (copied-state (copy-state s))) + (loop for (resource amount) on this-robot-costs by #'cddr + do (incf (getf (resources copied-state) resource 0) (- amount))) + (incf (getf (robots copied-state) type 0)) + copied-state))) + +(defmethod calc-resources-to-be-collected ((s state)) + (robots s)) + +(defmethod add-resources (new-resources (s state)) + (loop for (resource amount) on new-resources by #'cddr + do (incf (getf (resources s) resource 0) amount))) + +(defmethod get-possible-bot-builds (blueprints (s state)) + (remove-if-not (lambda (robot-type) (can-create-robot blueprints robot-type s)) + *all-types*))