aboutsummaryrefslogtreecommitdiff
path: root/2022/puzzle-11.lisp
blob: 1a55a966f85de7cefc769f550c83965401d49b85 (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
109
110
111
112
113
114
115
116
(load "lib")

(defparameter input (uiop:read-file-string "11-input"))
(defparameter lines (get-lines input))

(defun lines->segments (lines)
  (--> (split-by-completely lines "" #'string=)
       (mapcar (lambda (x)
            (--> (loop for i in x
                       collect
                       (string-trim '(#\Space) i))))
          it)))

(defun segment->monkey (segment)
  (destructuring-bind (name starting op test clause-1 clause-2) segment
    (declare (ignore name))
    `(:items
      ,(--> (subseq starting 16)
            (string-to-clist it)
            (split-by-completely it #\,)
            (mapcar (lambda (lst)
                 (--> (clist-to-string lst)
                      (string-trim '(#\Space) it)
                      (parse-integer it)))
               it))
      :inspect
      ,(--> (subseq op 21)
            (format nil "(~a old)" it)
            (with-input-from-string (s it)
              (read s))
            (eval `(lambda (old) ,it)))
      :test
      ,(let ((test (parse-integer (subseq test 19)))
             (clause-1 (parse-integer (subseq clause-1 25)))
             (clause-2 (parse-integer (subseq clause-2 26))))
         (lambda (n)
           (if (= (mod n test) 0)
               clause-1
               clause-2)))
      :divisor ,(parse-integer (subseq test 19)))))

(defmacro get-monkey (monkeys ind)
  `(nth ,ind ,monkeys))

(defmacro get-monkey-items (monkeys ind)
  `(getf (get-monkey ,monkeys ,ind) :items))

(defun monkey-increment-alist (monkey-alist monkey-ind)
  (setf (cdr (assoc monkey-ind monkey-alist))
        (+ 1 (cdr (assoc monkey-ind monkey-alist)))))

(defun monkey-send-item (monkeys item recipient)
  (setf (get-monkey-items monkeys recipient)
        (append (get-monkey-items monkeys recipient)
                (list item))))

(defun monkey-process-item (inspect item)
  (floor (/ (funcall inspect item) 3)))

(defun monkey-do-turn (monkeys monkey-alist monkey-ind)
  (let* ((monkey  (nth monkey-ind monkeys))
         (items   (getf monkey :items))
         (inspect (getf monkey :inspect))
         (test    (getf monkey :test)))
    (loop for item in items
          for proper-item    = (monkey-process-item inspect item)
          for recipient      = (funcall test proper-item)
          do (monkey-send-item monkeys proper-item recipient)
          do (monkey-increment-alist monkey-alist monkey-ind))
    (setf (get-monkey-items monkeys monkey-ind) nil)
    monkeys))

(defun monkeys-do-round (monkeys monkey-alist)
  (loop for monkey in monkeys
        for i from 0
        do (monkey-do-turn monkeys monkey-alist i)))

(defun monkey-business (monkey-alist)
  (--> (lambda (x y) (> (cdr x) (cdr y)))
       (sort monkey-alist it)
       (subseq it 0 2)
       (mapcar #'cdr it)
       (reduce #'* it)))

(let* ((monkeys (--> (lines->segments lines)
                     (mapcar #'segment->monkey it)))
       (monkey-alist (loop for monkey in monkeys
                           for i from 0
                           collect `(,i . 0))))

  (loop for i from 1 to 20
        do (monkeys-do-round monkeys monkey-alist))

  (format t "Round 1: ~a~%" (monkey-business monkey-alist)))

(defvar *monkey-divisor* nil)

(defun monkey-process-item (inspect item)
  (mod (funcall inspect item) *monkey-divisor*))

(let* ((monkeys (--> (lines->segments lines)
                     (mapcar #'segment->monkey it)))
       (monkey-alist (loop for monkey in monkeys
                           for i from 0
                           collect `(,i . 0))))

  ;; To ensure we don't go too hard.
  (setq *monkey-divisor* (reduce #'*
                            (loop for monkey in monkeys
                                  for divisor = (getf monkey :divisor)
                                  collect divisor)))

  (loop for i from 1 to 10000
        do (monkeys-do-round monkeys monkey-alist))

  (format t "Round 2: ~a~%" (monkey-business monkey-alist)))