advent-of-code-2023

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

commit ce60b45a738eb442af4a85b4cee06d5948941272
parent f79571f7ca069e3ac1deb706941bfd32e705a34f
Author: Lukas Henkel <lh@entf.net>
Date:   Fri, 22 Dec 2023 17:16:06 +0100

Day 22 task 2

Diffstat:
Msrc/day-22.lisp | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mt/day-22.lisp | 5+++--
2 files changed, 62 insertions(+), 16 deletions(-)

diff --git a/src/day-22.lisp b/src/day-22.lisp @@ -4,21 +4,23 @@ (in-package #:aoc/day-22) (defclass sand-block () - ((p-1 + ((id + :initarg :id) + (p-1 :initarg :p-1 :reader sand-block-p-1) (p-2 :initarg :p-2 :reader sand-block-p-2))) -(defun make-sand-block (p-1 p-2) - (make-instance 'sand-block :p-1 p-1 :p-2 p-2)) +(defun make-sand-block (id p-1 p-2) + (make-instance 'sand-block :id id :p-1 p-1 :p-2 p-2)) (defmethod print-object ((block sand-block) stream) (print-unreadable-object (block stream :type t) - (with-slots (p-1 p-2) + (with-slots (id p-1 p-2) block - (format stream "~A ~A" p-1 p-2)))) + (format stream "~A: ~A ~A" id p-1 p-2)))) (defmethod sand-block-bottom-z ((block sand-block)) (with-slots (p-1) @@ -40,7 +42,7 @@ block (setf (third p-2) new-z))) -(defun parse-line (line) +(defun parse-line (line index) (loop with p-1 = (list 0 0 0) with p-2 = (list 0 0 0) for current-index in (list 0 1 2 @@ -52,12 +54,13 @@ (parse-integer line :start pos :junk-allowed t) (setf (nth current-index obj) n) (setf pos end)) - finally (return (make-sand-block p-1 p-2)))) + finally (return (make-sand-block (code-char (+ index 65)) p-1 p-2)))) (defun parse-input (input) (loop for line = (read-line input nil) + for i from 0 while line - collect (parse-line line))) + collect (parse-line line i))) (defun collides-p (block-1 block-2) (labels ((intersects-p (from-1 to-1 from-2 to-2) @@ -90,22 +93,64 @@ do (setf (sand-block-bottom-z block) new-z) finally (return blocks))) +(defun block-rests-on (block others) + (loop with resting-z = (1- (sand-block-bottom-z block)) + for other in others + when (and (= (sand-block-top-z other) resting-z) + (collides-p block other)) + collect other)) + +(defun blocks-resting-on (block others) + (loop with resting-z = (1+ (sand-block-top-z block)) + for other in others + when (and (= (sand-block-bottom-z other) resting-z) + (collides-p block other)) + collect other)) + (defun task-1 (blocks) (loop with safe-to-disintegrate = (make-hash-table) for (block . others) on blocks - for resting-z = (1- (sand-block-bottom-z block)) - for resting-on = (loop for other in others - when (and (= (sand-block-top-z other) resting-z) - (collides-p block other)) - collect other) + for resting-on = (block-rests-on block others) when (= (length resting-on) 1) do (setf (gethash (first resting-on) safe-to-disintegrate) t) finally (return (loop for block in blocks when (not (gethash block safe-to-disintegrate)) sum 1)))) +(defun task-2 (blocks) + (let ((c-block-rests-on (make-hash-table)) + (c-blocks-resting-on (make-hash-table))) + (labels ((c-block-rests-on (block) + (or (gethash block c-block-rests-on) + (setf (gethash block c-block-rests-on) + (block-rests-on block blocks)))) + (c-blocks-resting-on (block) + (or (gethash block c-blocks-resting-on) + (setf (gethash block c-blocks-resting-on) + (blocks-resting-on block blocks)))) + (unsupported-p (block destroyed) + (let* ((rests-on (c-block-rests-on block)) + (rests-on (remove-if (lambda (block) + (gethash block destroyed)) + rests-on))) + (null rests-on))) + (destroy (layer &optional (destroyed (make-hash-table))) + (loop for block in layer + do (setf (gethash block destroyed) t)) + (let ((next-layer (loop with result = (make-hash-table) + for block in layer + do (loop for resting-on in (c-blocks-resting-on block) + when (unsupported-p resting-on destroyed) + do (setf (gethash resting-on result) t)) + finally (return (hash-table-keys result))))) + (+ (length layer) + (if next-layer (destroy next-layer destroyed) 0))))) + (loop for block in blocks + sum (max 0 (1- (destroy (list block)))))))) + (defun day-22 (input) (let* ((blocks (parse-input input)) (blocks (sort blocks #'< :key #'sand-block-bottom-z)) (blocks (apply-gravity blocks))) - (task-1 (nreverse blocks)))) + (values (task-1 (reverse blocks)) + (task-2 blocks)))) diff --git a/t/day-22.lisp b/t/day-22.lisp @@ -4,7 +4,7 @@ (define-test test-day-22 () - (multiple-value-bind (task-1) + (multiple-value-bind (task-1 task-2) (aoc:run-day 22 "1,0,1~1,2,1 0,0,2~2,0,2 0,2,3~2,2,3 @@ -12,4 +12,5 @@ 2,0,5~2,2,5 0,1,6~2,1,6 1,1,8~1,1,9") - (assert= 5 task-1))) + (assert= 5 task-1) + (assert= 7 task-2)))