From b44974b60da898f2896ecb8c62b51d9daa854ff5 Mon Sep 17 00:00:00 2001 From: Matteo Landi Date: Mon, 6 Jan 2025 15:48:31 +0100 Subject: [PATCH] Clean up 2024/15 and add input file --- src/2024/day15.enc | 1 + src/2024/day15.lisp | 269 +++++++++++++++++--------------------------- 2 files changed, 105 insertions(+), 165 deletions(-) create mode 100644 src/2024/day15.enc diff --git a/src/2024/day15.enc b/src/2024/day15.enc new file mode 100644 index 0000000..4ad5dac --- /dev/null +++ b/src/2024/day15.enc @@ -0,0 +1 @@ +(:C "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" :I "UkvOLrvIl800PxVHQRdody4qx5hCy4SBm0fDJDJq9eo=" :G "/vOvtg0hsG3vrMf6oJnj3g==") \ No newline at end of file diff --git a/src/2024/day15.lisp b/src/2024/day15.lisp index dd931cb..5bd1cc2 100644 --- a/src/2024/day15.lisp +++ b/src/2024/day15.lisp @@ -1,174 +1,113 @@ (defpackage :aoc/2024/15 #.cl-user::*aoc-use*) (in-package :aoc/2024/15) -#; -(sb-ext:gc :full t) - -(defun move-straight (dir pos) (mapcar #'+ pos dir)) -(defun rotate-cw (dir) (list (second dir) (- (first dir)))) -#+#:excluded (defun rotate-ccw (dir) (list (- (second dir)) (first dir))) +(defstruct (warehouse (:conc-name nil)) + grid robot) (defun parse-input (&optional (strings (uiop:read-file-lines #P"src/2024/day15.txt"))) - (destructuring-bind (map movements) (split-sequence:split-sequence "" strings :test 'string=) - (flet ((parse-map (strings) - (let ((map (make-hash-table :test 'equal)) - (robot nil)) - (doseq ((i s) (enumerate strings)) - (doseq ((j ch) (enumerate s)) - (setf (gethash (list i j) map) ch) - (if (char= ch #\@) (setf robot (list i j))))) - (list map robot))) - (parse-movements (strings) - (looping - (dolist (s strings) - (doseq (ch s) - (collect! (ecase ch - (#\^ (list -1 0)) - (#\> (list 0 1)) - (#\v (list 1 0)) - (#\< (list 0 -1))))))))) - (list (parse-map map) (parse-movements movements))))) -#+#:excluded (parse-input) - -(defun move-all (&optional (input (parse-input))) - (destructuring-bind ((map (ri rj)) movements) input - (doseq ((di dj) movements) - (labels ((move (i j &aux (pos (list i j))) - (let1 pos1 (list (+ i di) (+ j dj)) - #+#:excluded (dbgl pos1 (gethash pos1 map)) - (cond ((char= (gethash pos1 map) #\.) (rotatef (gethash pos1 map) - (gethash pos map)) - t) - ((char= (gethash pos1 map) #\O) (when (move (+ i di) (+ j dj)) - (rotatef (gethash pos1 map) - (gethash pos map)) - t)) - (t (assert (char= (gethash pos1 map) #\#))))))) - #+#:excluded (dbgl ri rj di dj) - (if (move ri rj) - (setf ri (+ ri di) rj (+ rj dj))))) - (result map))) -(move-all (parse-input)) - -(defun result (map) + (destructuring-bind (warehouse moves) (split-sequence:split-sequence "" strings :test 'string=) + (list (prog1-let x (make-warehouse :grid (make-hash-table :test 'equal)) + (doeseq (i s warehouse) + (doeseq (j ch s) + (setf (gethash (list i j) ~x.grid) ch) + (if (char= ch #\@) (setf ~x.robot (list i j)))))) + (looping + (dolist (s moves) + (doseq (ch s) + (collect! (ecase ch + (#\^ (list -1 0)) + (#\> (list 0 1)) + (#\v (list 1 0)) + (#\< (list 0 -1)))))))))) + + +(defun moving-vertically? (dir) (/= ~dir.car 0)) +(defun moving-left? (dir) (< ~dir.cadr 0)) +(defun moving-right? (dir) (> ~dir.cadr 0)) + +(defun move-straight (pos dir) + (list (+ ~pos.car ~dir.car) (+ ~pos.cadr ~dir.cadr))) +(defun right-of (pos) (list ~pos.car (1+ ~pos.cadr))) +(defun left-of (pos) (list ~pos.car (1- ~pos.cadr))) + + +(defun can-move? (warehouse dir) + (recursively ((pos ~warehouse.robot)) + (let1 next (move-straight pos dir) + (ecase (gethash next ~warehouse.grid) + (#\. t) + (#\# nil) + (#\O (recur next)) + (#\[ (if (moving-vertically? dir) + (and (recur next) + (recur (right-of next))) + (recur next))) + (#\] (if (moving-vertically? dir) + (and (recur next) + (recur (left-of next))) + (recur next))))))) + +(defun move-robot! (warehouse dir) + (assert (can-move? warehouse dir)) + (flet ((swap (pos1 pos2) + (rotatef (gethash pos1 ~warehouse.grid) + (gethash pos2 ~warehouse.grid)))) + (recursively ((pos ~warehouse.robot)) + (let1 next (move-straight pos dir) + (ecase (gethash next ~warehouse.grid) + (#\. (swap pos next)) + (#\O (recur next) ; push small box out + (swap pos next)) ; move into the empty space + (#\[ (cond + ((moving-vertically? dir) + (recur next) ; push first half-box out + (recur (right-of next)) ; push second half-box out + (swap pos next) ; move into the empty space + ) + (t + (recur next) ; make box out + (swap pos next)))) ; move into the empty space + (#\] (cond + ((moving-vertically? dir) + (recur next) ; push first half-box out + (recur (left-of next)) ; push second half-box out + (swap pos next) ; move into the empty space + ) + (t + (recur next) ; make room + (swap pos next) ; move into the empty space + )))) + next)))) + +(defun push-boxes (&optional (input (parse-input))) + (destructuring-bind (w moves) input + (doseq (dir moves) + (when (can-move? w dir) + (setf ~w.robot (move-robot! w dir)))) + w)) + + +(defun result (warehouse) (looping - (dohash ((i j) ch map) - (when (char= ch #\O) + (dohash ((i j) ch ~warehouse.grid) + (when (find ch "O[") (sum! (+ (* i 100) j)))))) -(defun parse-input (&optional (strings (uiop:read-file-lines #P"src/2024/day15.txt"))) - (destructuring-bind (map movements) (split-sequence:split-sequence "" strings :test 'string=) - (flet ((parse-map (strings) - (let ((map (make-hash-table :test 'equal)) - (robot nil)) - (doseq ((i s) (enumerate strings)) - (doseq ((j ch) (enumerate s)) - (ecase ch - (#\# (setf (gethash (list i (* j 2)) map) ch - (gethash (list i (1+ (* j 2))) map) ch)) - (#\. (setf (gethash (list i (* j 2)) map) ch - (gethash (list i (1+ (* j 2))) map) ch)) - (#\O (setf (gethash (list i (* j 2)) map) #\[ - (gethash (list i (1+ (* j 2))) map) #\])) - (#\@ (setf (gethash (list i (* j 2)) map) #\@ - (gethash (list i (1+ (* j 2))) map) #\.))) - (if (char= ch #\@) (setf robot (list i (* j 2)))))) - (list map robot))) - (parse-movements (strings) - (looping - (dolist (s strings) - (doseq (ch s) - (collect! (ecase ch - (#\^ (list -1 0)) - (#\> (list 0 1)) - (#\v (list 1 0)) - (#\< (list 0 -1))))))))) - (list (parse-map map) (parse-movements movements))))) -#+#:excluded (parse-input) - -(defun move-all (&optional (input (parse-input))) - (destructuring-bind ((map (ri rj)) movements) input - (doseq ((di dj) movements) - (labels ((moving-vertically? () (/= di 0)) - (moving-left? () (< dj 0)) - (moving-right? () (> dj 0)) - (straight-from (pos) (list (+ (car pos) di) (+ (cadr pos) dj))) - (right-of (pos) (list (car pos) (1+ (cadr pos)))) - (left-of (pos) (list (car pos) (1- (cadr pos)))) - (swap (pos1 pos2) (rotatef (gethash pos1 map) (gethash pos2 map)) t) - (can-move? (pos) - (cond ((char= (gethash pos map) #\.) t) - ((char= (gethash pos map) #\#) nil) - ((char= (gethash pos map) #\@) (can-move? (straight-from pos))) - ((char= (gethash pos map) #\[) - (cond ((moving-vertically?) (and (can-move? (straight-from pos)) - (can-move? (right-of (straight-from pos))))) - (t (can-move? (straight-from pos))))) - ((char= (gethash pos map) #\]) - (cond ((moving-vertically?) (and (can-move? (straight-from pos)) - (can-move? (left-of (straight-from pos))))) - (t (can-move? (straight-from pos))))) - (t (error "Cannot be here")))) - (move (pos) - (cond ((char= (gethash pos map) #\.) #+#:excluded (error "NEVER .")) - ((char= (gethash pos map) #\#) (error "NEVER #")) - ((char= (gethash pos map) #\@) (move (straight-from pos)) - (swap pos (straight-from pos))) - ((char= (gethash pos map) #\[) - (cond ((moving-vertically?) (move (straight-from pos)) - (move (right-of (straight-from pos))) - (swap pos (straight-from pos)) - (swap (right-of pos) (straight-from (right-of pos)))) - (t (move (straight-from pos)) - (swap pos (straight-from pos))))) - ((char= (gethash pos map) #\]) - (cond ((moving-vertically?) (move (straight-from pos)) - (move (left-of (straight-from pos))) - (swap pos (straight-from pos)) - (swap (left-of pos) (straight-from (left-of pos)))) - (t (move (straight-from pos)) - (swap pos (straight-from pos))))) - (t (error "Cannot be here"))))) - ; (dbgl ri rj di dj) - (assert (char= (gethash (list ri rj) map) #\@)) - ; (display map) - ; (break) - (when (can-move? (list ri rj)) - (move (list ri rj)) - (setf ri (+ ri di) rj (+ rj dj))))) - #+#:excluded (display map) - (dbg (result map)) - #+#:excluded (display map))) -(move-all (parse-input)) - -(defun display (map &aux (h 10) (w (* h 2))) - (dotimes (i h) - (dotimes (j w) - (pr (gethash (list i j) map))) - (pr #\Newline))) - -(defun result (map &aux (h 50) (w (* h 2))) - (looping - (dotimes (i h) - (dotimes (j w) - (when (char= (gethash (list i j) map) #\[) - (let1 di (min i #+#:excluded (- h i 1)) - (let1 dj (min j #+#:excluded (- w j 2)) - #+#:excluded (dbgl i j h w di dj) - ; (break) - (sum! (+ (* 100 di) dj))))))))) - -; 19125000 too high -; 18872500 too high - -(define-solution (2024 15) (input parse-input) - (values (looping - (doseq ((a b prize) input) - (awhen (solve a b prize) - (sum! it)))) - (looping - (doseq ((a b (px py)) input) - (awhen (solve a b (list (+ px 10000000000000) (+ py 10000000000000))) - (sum! it)))))) -(define-test (2024 15) (39290 73458657399094)) +(defun massage-input (&optional (strings (uiop:read-file-lines #P"src/2024/day15.txt"))) + (mapcar (fn (s) + (~> s + (cl-ppcre:regex-replace-all "#" ~ "##") + (cl-ppcre:regex-replace-all "\\." ~ "..") + (cl-ppcre:regex-replace-all "O" ~ "[]") + (cl-ppcre:regex-replace-all "@" ~ "@."))) + strings)) +#+#:excluded (massage-input) + + +(define-solution (2024 15) (strings) + (values (~> strings parse-input push-boxes result) + (~> strings massage-input parse-input push-boxes result))) +#+#:excluded (time (solution-run)) + +(define-test (2024 15) (1406392 1429013))