From 5682ca91b7367ddfb23d7f281bdc63bebb994cc5 Mon Sep 17 00:00:00 2001 From: Oscar Najera Date: Wed, 11 Jan 2023 18:51:24 +0100 Subject: AGG Mutable state --- AoC2022/19/solver.lisp | 108 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 AoC2022/19/solver.lisp (limited to 'AoC2022/19/solver.lisp') diff --git a/AoC2022/19/solver.lisp b/AoC2022/19/solver.lisp new file mode 100644 index 0000000..0c67060 --- /dev/null +++ b/AoC2022/19/solver.lisp @@ -0,0 +1,108 @@ +(ql:quickload '(fiveam uiop cl-ppcre)) + + +(defun material (string) + (cond + ((string= string "ore") :ore) + ((string= string "clay") :clay) + ((string= string "obsidian") :obsidian) + ((string= string "geode") :geode))) + +(defun parse-robot-recipe (line) + (cl-ppcre:register-groups-bind ((#'material robot) (#'parse-integer fq) (#'material fm) (#'parse-integer sq) (#'material sm)) + ("(\\w+) robot costs (\\d+) (\\w+)(?: and (\\d+) (\\w+))?" line) + (append + (list robot fm fq) + (when sm (list sm sq))))) + +(defun start-resources () + (list :ore 0 :clay 0 :obsidian 0 :geode 0)) +(defun start-bots () + (list :ore 1 :clay 0 :obsidian 0 :geode 0)) + +(defun can-build-robot-p (requirements available) + (loop for (material amount) on requirements by #'cddr + always (<= amount (getf available material 0)))) + +(defun consume (requirements available) + (loop for (material amount) on requirements by #'cddr + do (decf (getf available material) amount)) + available) + +(fiveam:test requirements + (fiveam:is-false (can-build-robot-p '(:ore 6) '(:ore 5))) + (fiveam:is-false (can-build-robot-p '(:ore 6) '(:clay 5))) + (fiveam:is-true (can-build-robot-p '(:ore 2) '(:ore 5))) + (fiveam:is-true (can-build-robot-p '(:ore 2) '(:clay 2 :ore 5))) + (fiveam:is (equal '(:ore 2) (consume '(:ore 3) '(:ore 5))))) + +(defun material-collect (bots available-materials) + (loop for (material amount) on bots by #'cddr + nconc (list material (+ (getf available-materials material 0) amount)))) + +(defun bot-requirements (type recipes) + (cdr (assoc type recipes :test #'eq))) + +(defun plist-merge (func p1 p2 &optional default) + "Merge 2 flat plists P1 & P2 by FUNC. Use DEFAULT when value is missing on one list." + (flet ((keys (plist) (loop for key in plist by #'cddr collect key))) + (loop for key in (union (keys p1) (keys p2)) + nconc (list key (funcall func (getf p1 key default) (getf p2 key default)))))) + +(defun max-costs (blueprint) + (reduce + (lambda (acc item) + (plist-merge #'max acc item 0)) + (mapcar #'cdr blueprint))) + +(defun can-build (recipes available-materials) + (mapcan (lambda (bot-recipes) + (when (can-build-robot-p (cdr bot-recipes) available-materials) + (list (car bot-recipes)))) + recipes)) + +(defun should-build (blueprints robots) + (let ((max-cost (max-costs blueprints))) + (loop for (type amount) on robots by #'cddr + when (or (eq type :geode) + (<= amount (getf max-cost type 0))) + collect type))) + +(defun try! (build-next robots resources blueprints) + (let ((req (bot-requirements build-next blueprints))) + (when (can-build-robot-p req resources) + (consume req resources) + (incf (getf robots build-next 0))))) + + +(defun next-step (build-next robots resources blueprints time-left) + (if (zerop time-left) + (list robots resources) + (let ((new-robots (try! build-next robots resources blueprints))) + (mapcar + (lambda (next) + (next-step next robots (material-collect robots resources) blueprints (1- time-left))) + (if new-robots + (should-build blueprints robots) + (list build-next)))) + )) + +(let ((blueprints + (cdar + (mapcar + (lambda (l) + (let ((blueprint (uiop:split-string l :separator ":."))) + (cons (car blueprint) + (mapcar #'parse-robot-recipe (butlast (cdr blueprint)))))) + (uiop:read-file-lines "eg-in")))) + ;; (materials '(:ore 8 :clay 20)) + (materials (start-resources)) + (bots (start-bots)) + ;; (bots '(:geode 1 :ore 8 :clay 20)) + (time-left 5)) + + + (next-step :clay bots materials blueprints time-left) + + + ) -- cgit v1.2.3