;; 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) (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) (with-slots (resources robots) obj (format stream "collected: ~a, with robots: ~a" resources robots)))) ;; example of blueprint: (defparameter *test-blueprint* '(: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)) :minute (minute 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))) ;; 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) (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))))))))