(ql:quickload '(fiveam str arrows))
(defparameter eg-input "LLR
AAA = (BBB, BBB)
BBB = (AAA, ZZZ)
ZZZ = (ZZZ, ZZZ)")
(defun parse-input (lines)
(let ((graph (make-hash-table :test #'equal)))
(destructuring-bind (instructions blank . nodes) lines
(declare (ignorable blank))
(dolist (line nodes)
(destructuring-bind (node . leaves)
(remove-if #'str:emptyp
(cl-ppcre:split "[=,() ]" line))
(setf (gethash node graph) leaves)))
(values instructions graph))))
(defun traverse (instructions graph location terminatep)
(loop
for i from 0
do (arrows:-<>
(ecase (aref instructions (mod i (length instructions)))
(#\L #'first)
(#\R #'second))
(funcall <> (gethash location graph))
(setf location <>))
count i until (funcall terminatep location)))
(defun solver1 (lines)
(multiple-value-bind (instructions graph)
(parse-input lines)
(traverse instructions graph "AAA" (lambda (p) (equal "ZZZ" p)))))
(defparameter eg-input2 "LR
11A = (11B, XXX)
11B = (XXX, 11Z)
11Z = (11B, XXX)
22A = (22B, XXX)
22B = (22C, 22C)
22C = (22Z, 22Z)
22Z = (22B, 22B)
XXX = (XXX, XXX)")
(defun solver2 (lines)
(multiple-value-bind (instructions graph)
(parse-input lines)
(arrows:->>
(remove-if-not (lambda (s) (str:ends-with-p "A" s))
(alexandria:hash-table-keys graph))
(mapcar (lambda (start)
(traverse instructions graph start (lambda (p) (str:ends-with-p "Z" p)))))
(apply #'lcm))))
(fiveam:test solutions
(fiveam:is (= 6 (solver1 (uiop:split-string eg-input :separator '(#\Newline)))))
(fiveam:is (= 6 (solver2 (uiop:split-string eg-input2 :separator '(#\Newline)))))
(fiveam:is (= 19199 (solver1 (uiop:read-file-lines "input"))))
(fiveam:is (= 13663968099527(solver2 (uiop:read-file-lines "input")))))
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))))
|