advent-of-code-2023

My solutions to AoC 2023
git clone git://git.entf.net/advent-of-code-2023
Log | Files | Refs

commit 8c78fb5cb90087550a894bd7d8d992922ece70ce
parent ca198cf627cb5c39d486e44e55c3ccfbd16b23c7
Author: Lukas Henkel <lh@entf.net>
Date:   Wed, 20 Dec 2023 20:59:09 +0100

Day 20 task 1

Diffstat:
Ainput/20.txt | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/day-20.lisp | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
At/day-20.lisp | 20++++++++++++++++++++
3 files changed, 189 insertions(+), 0 deletions(-)

diff --git a/input/20.txt b/input/20.txt @@ -0,0 +1,58 @@ +%dm -> jz, ct +&ft -> rx +%hc -> kh +%kg -> gd, sl +&sl -> bs, bq, ts, zd, gk, xx, lp +%rf -> xk, jz +%zd -> kg +%dp -> ql, pq +%ss -> pq, mb +&rr -> hc, lt, kh, fv, tc, vg +%hv -> nh +%nh -> pq, xm +&jz -> bn, vz, xz +%vf -> nc, sl +%gk -> pc +%bs -> gk +&pq -> tb, qh, ls, hv, ql +%jb -> rr, hc +%fv -> cd, rr +&vz -> ft +%mm -> jr, rr +%vg -> mm +%cd -> tc, rr +&bq -> ft +%xg -> pb, jz +%xx -> zd +%ls -> tb, pq +%pb -> jz, rz +%tc -> vp +%kh -> ck +%lp -> xx +%tb -> ss +%qk -> jz, xg +%xz -> dm +%jr -> rb, rr +%mb -> hv, pq +&qh -> ft +&lt -> ft +%rb -> rr +%pc -> hr, sl +%hr -> vf, sl +%gd -> bs, sl +%xm -> dp, pq +%ct -> jz, rj +%ck -> vg, rr +%cr -> jz, rf +%bn -> xz, jz +%vp -> rr, jb +%hl -> pq, vj +%ts -> lp, sl +%rz -> jz, cr +%ql -> tr +%xk -> jz +%vj -> pq +%tr -> pq, hl +broadcaster -> ls, fv, ts, bn +%nc -> sl +%rj -> jz, qk diff --git a/src/day-20.lisp b/src/day-20.lisp @@ -0,0 +1,111 @@ +(defpackage #:aoc/day-20 + (:use #:cl #:aoc/utils) + (:export #:day-20)) +(in-package #:aoc/day-20) + +(defclass module () + ((label + :initarg :label + :reader module-label) + (targets + :initarg :targets + :accessor module-targets) + (connected + :initform nil + :accessor module-connected))) + +(defmethod print-object ((module module) stream) + (print-unreadable-object (module stream :type t) + (format stream "~A" (module-label module)))) + +(defclass flip-flop (module) + ((state + :initform nil))) + +(defmethod print-object ((module flip-flop) stream) + (print-unreadable-object (module stream :type t) + (with-slots (state) + module + (format stream "~A (~A)" (module-label module) + (if state "on" "off"))))) + +(defclass conjunction (module) + ((states + :initform (make-hash-table)))) + +(defclass noop-module (module) + ()) + +(defmethod pulse ((module module) sender type) + (with-slots (targets) + module + (loop for target in targets + collect (list module target type)))) + +(defmethod pulse ((module flip-flop) sender type) + (with-slots (state) + module + (when (eq type :low) + (setf state (not state)) + (call-next-method module sender (if state :high :low))))) + +(defmethod pulse ((module conjunction) sender type) + (with-slots (states connected) + module + (setf (gethash sender states) type) + (call-next-method module sender (if (loop for from in connected + for mem = (gethash from states) + always (eq mem :high)) + :low + :high)))) + +(defmethod pulse ((module noop-module) sender type)) + +(defun parse-line (line) + (let* ((pos-source-end (position #\Space line)) + (source-type (case (aref line 0) + (#\% 'flip-flop) + (#\& 'conjunction) + (t 'module))) + (source-label (subseq line + (if (eq source-type 'module) 0 1) + pos-source-end)) + (target-labels (loop for pos from (+ pos-source-end 4) below (length line) + for end = (or (position #\, line :start pos) + (length line)) + collect (subseq line pos end) + do (setf pos (1+ end))))) + (make-instance source-type :label source-label :targets target-labels))) + +(defun parse-input (input) + (let ((modules (make-hash-table :test 'equal))) + (loop for line = (read-line input nil) + while line + for module = (parse-line line) + do (setf (gethash (module-label module) modules) module)) + (loop for module being the hash-value of modules + do (setf (module-targets module) + (loop for target-label in (module-targets module) + for target-module = (or (gethash target-label modules) + (setf (gethash target-label modules) + (make-instance 'noop-module + :label target-label))) + do (push module (module-connected target-module)) + collect target-module))) + modules)) + +(defun day-20 (input) + (let* ((modules (parse-input input)) + (broadcast (gethash "broadcaster" modules))) + (loop with low-count = 0 + with high-count = 0 + repeat 1000 + do (loop with next = (list (list nil broadcast :low)) + while next + for next-next = (loop for (from to type) in next + do (ecase type + (:low (incf low-count)) + (:high (incf high-count))) + nconc (pulse to from type)) + do (setf next next-next)) + finally (return (* low-count high-count))))) diff --git a/t/day-20.lisp b/t/day-20.lisp @@ -0,0 +1,20 @@ +(defpackage #:aoc-test/day-20 + (:use #:cl #:lisp-unit2)) +(in-package #:aoc-test/day-20) + +(define-test test-day-20 + () + (multiple-value-bind (task-1) + (aoc:run-day 20 "broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a") + (assert= 32000000 task-1)) + (multiple-value-bind (task-1) + (aoc:run-day 20 "broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> output") + (assert= 11687500 task-1)))