aboutsummaryrefslogtreecommitdiffstats
path: root/AoC2022/11/solver.el
blob: 6b0c4363185f260df5e5c06f340d996c2e11cfe0 (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
;;; solver.el --- Day 11 -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2022 Óscar Nájera
;;
;; Author: Óscar Nájera <hi@oscarnajera.com>
;; Maintainer: Óscar Nájera <hi@oscarnajera.com>
;; Created: December 11, 2022
;; Modified: December 11, 2022
;;
;; This file is not part of GNU Emacs.
;;
;;; Commentary:
;;
;;  Day 11
;;
;;; Code:

(require 'subr-x)
(require 'cl-lib)

(cl-defstruct (monkey (:constructor monkey--create)
                      (:copier nil))
  "Monkey container"
  id
  items
  worry-level
  divisor
  throw-to)

(defun solver-monkey-make (id items operation divisor true-target false-target)
  (let ((op (if (string-prefix-p "*" operation) #'* #'+))
        (operand (unless (string-suffix-p "old" operation)
                   (string-to-number (substring operation 1))))
        (div (string-to-number divisor))
        (true (string-to-number true-target))
        (false (string-to-number false-target)))
    (monkey--create
     :id (string-to-number id)
     :items (nreverse (mapcar #'string-to-number (split-string items  ", ")))
     :worry-level (lambda (item) (funcall op item (or operand item))) ;; value item
     :divisor div
     :throw-to (lambda (item)
                 (thread-first item
                               (mod  div) ;; throw input
                               (=  0)     ;; divisble
                               (if true false))))))

(defun solver-parse-monkey-entry ()
  (when
      (re-search-forward (rx bol "Monkey " (group digit) ":" (+ space)
                             "Starting items: " (group (+ (1+ digit) (? ", "))) (+ space)
                             "Operation: new = old " (group (1+ any)) (+ space)
                             "Test: divisible by " (group (1+ digit)) (+ space)
                             "If true: throw to monkey " (group digit) (+ space)
                             "If false: throw to monkey " (group digit) eol)
                         nil t)
    (apply #'solver-monkey-make
           (mapcar #'match-string (number-sequence 1 6)))))

(defun solver-monkey-business (works)
  (let ((ranks (sort works #'>)))
    (* (aref ranks 0) (aref ranks 1))))

(defun solver-get-monkeys (filename)
  (with-temp-buffer
    (insert-file-contents filename)
    (goto-char (point-min))
    (cl-loop for monkey = (solver-parse-monkey-entry) while monkey collect monkey)))

(defun solver-monkey-rounds (rounds relaxation &rest monkeys)
  (let ((monkey-work (make-vector (length monkeys) 0)))
    ;; play round
    (dotimes (_ rounds)
      (dolist (monkey monkeys)
        (let ((items (nreverse (monkey-items monkey))))
          (setf (monkey-items monkey) nil)
          (cl-incf (aref monkey-work (monkey-id monkey)) (length items))
          (mapc (lambda (item)
                  (let* ((item-concern (funcall relaxation (funcall (monkey-worry-level monkey) item)))
                         (target-monkey (funcall (monkey-throw-to monkey) item-concern)))
                    (push item-concern (monkey-items (cl-find target-monkey monkeys :key #'monkey-id)))))
                items))
        ;; (message "monkey %d - items: %s\n all: %S" (monkey-id monkey) (monkey-items monkey)
        ;;          (mapcar #'monkey-items monkeys))
        ))
    monkey-work))

(defun solver-constant-slice (monkeys)
  (cons (lambda (x) (floor x 3)) monkeys))

(defun solver-monkey-field (monkeys)
  (let ((prime-field (apply #'* (mapcar #'monkey-divisor monkeys))))
    (cons (lambda (x) (mod x prime-field)) monkeys)))

(defun solver (filename rounds relaxer)
  (thread-last
    filename
    (solver-get-monkeys)
    (funcall relaxer)
    (apply #'solver-monkey-rounds rounds)
    (solver-monkey-business)))

(ert-deftest test-solver ()
  (should (= 10605 (solver "eg-in" 20 #'solver-constant-slice)))
  (should (= 50830 (solver "input" 20 #'solver-constant-slice)))
  (should (= 2713310158 (solver "eg-in" 10000 #'solver-monkey-field)))
  (should (= 14399640002 (solver "input" 10000 #'solver-monkey-field))))