Commit Diff


commit - 4f33aa74e3fc0f489f1d20fe970de7370eb84f9f
commit + a757f2ccecb5b65c1c76e6099f2ae82a0d934f08
blob - /dev/null
blob + e40bd4584bf8c92f748141991cd5f47c0bf6a694 (mode 644)
--- /dev/null
+++ input/17.txt
@@ -0,0 +1,5 @@
+Register A: 23999685
+Register B: 0
+Register C: 0
+
+Program: 2,4,1,1,7,5,1,5,0,3,4,4,5,5,3,0
blob - /dev/null
blob + 36f4d0d9cc3b26f7b345fd09053b10a3c62ff345 (mode 644)
--- /dev/null
+++ src/day-17.lisp
@@ -0,0 +1,92 @@
+(defpackage #:aoc/day-17
+  (:use #:cl #:aoc/utils)
+  (:export
+   #:make-registers
+   #:run-program
+   #:day-17))
+(in-package #:aoc/day-17)
+
+(defun parse-register (line)
+  (parse-integer line :start (1+ (position #\: line))))
+
+(defun make-registers (a &optional (b 0) (c 0))
+  (make-array 3
+              :element-type 'fixnum
+              :initial-contents (list a b c)))
+
+(defun parse-input (input)
+  (let ((a (parse-register (read-line input)))
+        (b (parse-register (read-line input)))
+        (c (parse-register (read-line input)))
+        (program-line (progn
+                        (read-line input)
+                        (read-line input))))
+    (values (make-registers a b c)
+            (coerce (read-number-list program-line :start (1+ (position #\: program-line)))
+                    'vector))))
+
+(declaim (inline combo-operand)
+         (ftype (function (fixnum (simple-array fixnum (3))) fixnum) combo-operand))
+
+(defun combo-operand (operand registers)
+  (if (<= operand 3)
+      operand
+      (if (<= operand 7)
+          (aref registers (- operand 4))
+          (error "Invalid operand ~A for combo operator" operand))))
+
+(defun run-program (registers program)
+  (loop with output = nil
+        for ip from 0 below (length program) by 2
+        for instruction = (aref program ip)
+        for operand = (aref program (1+ ip))
+        do (case instruction
+             (0 (setf (aref registers 0)
+                      (floor (aref registers 0)
+                             (expt 2 (combo-operand operand registers)))))
+             (1 (setf (aref registers 1)
+                      (logxor (aref registers 1)
+                              operand)))
+             (2 (setf (aref registers 1)
+                      (mod (combo-operand operand registers) 8)))
+             (3 (when (not (zerop (aref registers 0)))
+                  (setf ip (- operand 2))))
+             (4 (setf (aref registers 1)
+                      (logxor (aref registers 1)
+                              (aref registers 2))))
+             (5 (push (mod (combo-operand operand registers) 8) output))
+             (6 (setf (aref registers 1)
+                      (floor (aref registers 0)
+                             (expt 2 (combo-operand operand registers)))))
+             (7 (setf (aref registers 2)
+                      (floor (aref registers 0)
+                             (expt 2 (combo-operand operand registers))))))
+        finally (return output)))
+
+(defun reset-registers (registers original-registers register-a)
+  (setf (aref registers 0) register-a
+        (aref registers 1) (aref original-registers 1)
+        (aref registers 2) (aref original-registers 2)))
+
+(defun task-2 (registers code)
+  (let ((original-registers (copy-seq registers)))
+    (labels ((%solve (register-a depth)
+               (loop for n from 0 below 8
+                     for nra = (+ register-a n)
+                     do (reset-registers registers original-registers nra)
+                        (let* ((res (run-program registers code))
+                               (value (nth depth res)))
+                          (when (= value (aref code (- (length code) 1 depth)))
+                            (when (= depth (1- (length code)))
+                              (return-from %solve nra))
+                            (let ((solved-register-a (%solve (* nra 8) (1+ depth))))
+                              (when solved-register-a
+                                (return-from %solve solved-register-a))))))))
+      (%solve 1 0))))
+
+(defun day-17 (input)
+  (multiple-value-bind (registers code)
+      (parse-input input)
+    (values
+     (format nil "~{~A~^,~}" (nreverse (run-program (copy-seq registers) code)))
+     (task-2 registers code))))
blob - /dev/null
blob + 52665687fc58e4471b012f298f9155f03148cad2 (mode 644)
--- /dev/null
+++ t/day-17.lisp
@@ -0,0 +1,31 @@
+(defpackage #:aoc-test/day-17
+  (:use #:cl #:lisp-unit2 #:aoc/day-17))
+(in-package #:aoc-test/day-17)
+
+(define-test test-day-17
+    ()
+  (let ((registers (make-registers 0 0 9))
+        (program (vector 2 6)))
+    (aoc/day-17:run-program registers program)
+    (assert= 1 (aref registers 1)))
+  (let ((registers (make-registers 10 0 0))
+        (program (vector 5 0 5 1 5 4)))
+    (assert-equal (list 0 1 2)
+                  (nreverse (run-program registers program))))
+  (let ((registers (make-registers 2024 0 0))
+        (program (vector 0 1 5 4 3 0)))
+    (assert-equal (list 4 2 5 6 7 7 7 7 3 1 0)
+                  (nreverse (run-program registers program)))
+    (assert= 0 (aref registers 0)))
+  (let ((registers (make-registers 0 29 0))
+        (program (vector 1 7)))
+    (run-program registers program)
+    (assert= 26 (aref registers 1)))
+  (let ((registers (make-registers 0 2024 43690))
+        (program (vector 4 0)))
+    (run-program registers program)
+    (assert= 44354 (aref registers 1)))
+  (let ((registers (make-registers 729))
+        (program (vector 0 1 5 4 3 0)))
+    (assert-equal (list 4 6 3 5 6 3 5 2 1 0)
+                  (nreverse (run-program registers program)))))