99 lines
4.0 KiB
Common Lisp
99 lines
4.0 KiB
Common Lisp
;; 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))))))))
|