aboutsummaryrefslogtreecommitdiffstats
path: root/AoC2022/19/solver.lisp
blob: 0c67060bf0d13206ab3a9b1ce9b869a2bce6e0f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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)


  )