From bf52645d934d4617c07dde0f151e600cab9fccac Mon Sep 17 00:00:00 2001 From: efim Date: Sat, 24 Dec 2022 10:11:53 +0000 Subject: [PATCH] this is "optimization" --- day19-scratch.lisp | 181 ++++++++++++++++++++++++++++++++++++++++++++- day19.lisp | 52 +++++++++++-- 2 files changed, 225 insertions(+), 8 deletions(-) diff --git a/day19-scratch.lisp b/day19-scratch.lisp index 1ff984d..3e632ad 100644 --- a/day19-scratch.lisp +++ b/day19-scratch.lisp @@ -134,7 +134,7 @@ (let* ((will-collect-this-minute (calc-resources-to-be-collected s)) (max-if-building (loop - for bot-type in *add-types* + for bot-type in (get-possible-bot-builds blueprints s) for state-with-new-bot = (create-robot blueprints bot-type s) when state-with-new-bot maximize (progn @@ -164,7 +164,7 @@ :geode (:ore 2 :obsidian 7))) (setq *test-state* (make-instance 'state)) -(print (find-max-geod *test-blueprint* *test-state* 1)) +;; (print (find-max-geod *test-blueprint* *test-state* 1)) ;; => 0 ;; that's because i have no ability to "wait" ;; whoops @@ -212,3 +212,180 @@ ;; but only over states with same amount of steps? ;; ok. let's commit what i have right now and try to trim? + +;; how could i compare with that "cur-max" and update that cur-max? +;; no, i don't understand + +(format t "some result ~a~%" (find-max-geod *test-blueprint* *test-state* 1)) + +;; well, yes one optimizaiton - stop building robots, when resource is to the top of +;; max daily expense +;; that would reduce a lot +;; 1) +;; would be nice to put these into (possible-robots-to-build) +;; so that it would also filtered out unnecessary new robots +;; +;; 2) +;; "keep global current max state" how would i compare and check if it's impossible to beat? +;; with "even if i could to build geod machine every day for rest N days" + +*test-blueprint* +*test-state* +(add-resources '(:ore 10) *test-state*) +(incf (getf (robots *test-state*) :ore) 2) +(any-use-of-creating-robot *test-blueprint* *test-state* :ore) +(any-use-of-creating-robot *test-blueprint* *test-state* :clay) +(any-use-of-creating-robot *test-blueprint* *test-state* :obsidian) +(any-use-of-creating-robot *test-blueprint* *test-state* :geode) + +(max-need *test-blueprint* *test-state* :ore) +(max-need *test-blueprint* *test-state* :clay) +(max-need *test-blueprint* *test-state* :obsidian) +(max-need *test-blueprint* *test-state* :geode) + +;; and this is not good for :geode, we want as much as possible + +(get-possible-bot-builds *test-blueprint* *test-state*) + +;; and now let's add static "max state"? +;; i'd need comparison like "can catch up" with that found max "is dominated by" +;; and also way to update that maximal state? +;; +;; so, what will that be? +;; comparison for update, should it also be if i found a state that dominates? +;; only for satiated states? +;; +;; if day is same or more +;; but the amount of geode robots and geodes is smaller? +(is-satiated-p *test-blueprint* *test-state*) +(incf (getf (robots *test-state*) :obsidian 0) 20) +;; seems to work, +;; but i already want to utilize some test framework + +;; whelp. i do want to test this +;; 4 14 7 to be satisfied +(is-satiated-p + *test-blueprint* + (make-instance 'state :robots '(:ore 4 :clay 14 :obsidian 7 :geode 2))) + +(is-satiated-p + *test-blueprint* + (make-instance 'state :resources '(:ore 4 :clay 14 :obsidian 7 :geode 2))) ; not, need robots + +(is-satiated-p + *test-blueprint* + (make-instance 'state :robots '(:ore 4 :clay 14 :obsidian 7 :geode 2))) + +;; now for checking is-dominated. ugh. +(a-dominates-b-p + *test-blueprint* + (make-instance 'state :robots '(:ore 4 :clay 14 :obsidian 7 :geode 2)) + (make-instance 'state :robots '(:ore 4 :clay 14 :obsidian 7 :geode 2))) + +;; both satiated, but second bigger +(a-dominates-b-p + *test-blueprint* + (make-instance 'state :robots '(:ore 5 :clay 17 :obsidian 7 :geode 2)) + (make-instance 'state :robots '(:ore 6 :clay 18 :obsidian 7 :geode 2))) +;; +;; both satiated, but second not always bigger +(a-dominates-b-p + *test-blueprint* + (make-instance 'state :robots '(:ore 5 :clay 17 :obsidian 9 :geode 2)) + (make-instance 'state :robots '(:ore 6 :clay 18 :obsidian 8 :geode 2))) + +;; +;; first not satiated, even though second is bigger - nil +(a-dominates-b-p + *test-blueprint* + (make-instance 'state :robots '(:ore 2 :clay 17 :obsidian 9 :geode 2)) + (make-instance 'state :robots '(:ore 6 :clay 18 :obsidian 8 :geode 2))) + +;; and that's not right. if we have on same amount of steps +;; reference as satiated and checking as not - wouldn't keep up +;; so big check should be whether steps are even in imagination permit +;; ugh. that would mean putting minute\step into state. + +;; um, let's not do it right now? +;; for both satiated is a very weak check, but let's try it like this? + +(setq *test-state* (make-instance 'state)) +(setf (cur-found-max *test-state*) (make-instance 'state)) + +;; so whelp +;; should have committed after doing the "build makes sence list" +;; +;; my problems are because "is dominated by" is not simmetrical to "dominates" +;; and i want both +;; +;; now in the loop set first satiated as domination +;; after that compare if our set dominates that one and swap +;; and compare if it's dominated by that one and prune + +;; so, only if ref earlier than checked state. +;; and then - if checked not saitated, or by all resources less than +;; but i do want tests + +;; both satiated, but second bigger +(a-dominates-b-p + *test-blueprint* + (make-instance 'state :robots '(:ore 6 :clay 18 :obsidian 7 :geode 2) :minute 2) + (make-instance 'state :robots '(:ore 6 :clay 18 :obsidian 7 :geode 2) :minute 3)) +;; should be NIL +;; now want to check for different amount of steps. +;; so if same resources but first is earlier - it dominates +(minute (make-instance 'state :robots '(:ore 6 :clay 18 :obsidian 7 :geode 2) :minute 2)) +;; i was putting :minute 2 into :robots plist, cool, no thanks to you types +(minute (make-instance 'state :robots '(:ore 6 :clay 18 :obsidian 7 :geode 2) :minute 2)) + +;; ok. this also seems well. +;; then main loop? on fisrt satiated set as `cur-found-max` +;; and after that always check - + +(defmethod find-max-geod-2 (blueprints (s state)) + ;; (declare (optimize (debug 3))) + (format t "in step for ~a; with ~a~%" (minute s) s) + (when (and + (not (cur-found-max s)) ; first pruning max + (is-satiated-p blueprints s)) + (setf (cur-found-max s) s)) + + (cond + ((= 25 (minute s)) ; exit condition fully calculated + (getf (resources s) :geode 0)) + ((and (cur-found-max s) (a-dominates-b-p blueprints (cur-found-max s) s) + 0)) ; pruning this branch + (t ; default check + (progn + (when (and (cur-found-max s) + (a-dominates-b-p blueprints s (cur-found-max s))) + (setf (cur-found-max s) s)) ; set current state as domineering + (let* ((will-collect-this-minute (calc-resources-to-be-collected s)) + (possible-bot-builds (get-possible-bot-builds blueprints s)) + (max-if-building + (when possible-bot-builds + (loop + for bot-type in possible-bot-builds + 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) + (incf (minute state-with-new-bot)) + (find-max-geod-2 blueprints state-with-new-bot ))))) + (if-not-building + (let ((state-copy (copy-state s))) + ;; (break) + (add-resources will-collect-this-minute state-copy) + (incf (minute state-copy)) + (find-max-geod-2 blueprints state-copy )))) + ;; (break) + ;; (format t "would build ~a~%" possible-bot-builds) + (max (or max-if-building 0) if-not-building)))))) + +(setq *test-state* (make-instance 'state)) +(setf (cur-found-max *test-state*) nil) +(print (find-max-geod-2 *test-blueprint* *test-state*)) + +;; so, why doesn't it ever builds obsidian? +*test-blueprint* +;; because is "has to build something if it can" whops diff --git a/day19.lisp b/day19.lisp index 38f6e53..5205148 100644 --- a/day19.lisp +++ b/day19.lisp @@ -7,7 +7,10 @@ (defclass state () ((resources :accessor resources :initform nil :initarg :resources) - (robots :accessor robots :initform (list :ore 1) :initarg :robots))) + (robots :accessor robots :initform (list :ore 1) :initarg :robots) + (minute :accessor minute :initarg :minute :initform 1 ) + (cur-found-max :initform nil :accessor cur-found-max :allocation :class) ; would be nice to add types + )) (defmethod print-object ((obj state) stream) (print-unreadable-object (obj stream :type t) @@ -18,8 +21,7 @@ ;; example of blueprint: (defparameter *test-blueprint* - '(:id 1 - :ore (:ore 4) + '(:ore (:ore 4) :clay (:ore 2) :obsidian (:ore 3 :clay 14) :geode (:ore 2 :obsidian 7))) @@ -30,7 +32,8 @@ (defmethod copy-state ((s state)) (make-instance 'state :resources (copy-list (resources s)) - :robots (copy-list (robots s)))) + :robots (copy-list (robots s)) + :minute (minute s))) (defmethod can-create-robot (blueprints type (s state)) (let ((this-robot-costs (getf blueprints type))) @@ -53,6 +56,43 @@ (loop for (resource amount) on new-resources by #'cddr do (incf (getf (resources s) resource 0) amount))) +;; robot is unnecessary if resouce it brings is alreay produced +;; at amount of maximal possible per-turn expence +(defmethod max-need (blueprints (s state) resource-type) + (loop + for (la blueprint) on blueprints by #'cddr + ;; do (print blueprint) + maximize (getf blueprint resource-type 0))) + +(defmethod any-use-of-creating-robot (blueprints (s state) robot-type) + (if (eq :geode robot-type) + t ; always reason to build more geode robots + (let ((max-need (max-need blueprints s robot-type)) + (state-production (getf (robots s) robot-type 0))) + ;; (format t "comparing need ~a with prod ~a" max-need state-production) + (> max-need state-production)))) + (defmethod get-possible-bot-builds (blueprints (s state)) - (remove-if-not (lambda (robot-type) (can-create-robot blueprints robot-type s)) - *all-types*)) + (remove-if-not (lambda (robot-type) + (any-use-of-creating-robot blueprints s robot-type)) + (remove-if-not (lambda (robot-type) + (can-create-robot blueprints robot-type s)) + *all-types*))) + +;; true when no longer need to build secondary robots +(defmethod is-satiated-p (blueprints (s state)) + (loop for type in '(:ore :clay :obsidian) + never (any-use-of-creating-robot blueprints s type))) + +(defmethod a-dominates-b-p (blueprints (a state) (b state)) + ;; (declare (optimize (debug 3))) + (when (is-satiated-p blueprints a) ; when not a satiated - don't know + (and + (<= (minute a) (minute b)) ; a earlier than b + (or + (not (is-satiated-p blueprints b)) + (loop for resource-type in *all-types* ; for both satiated compare all resources + always (and (>= (getf (resources a) resource-type 0) + (getf (resources b) resource-type 0)) + (>= (getf (robots a) resource-type 0) + (getf (robots b) resource-type 0))))))))