Commit Diff


commit - f79571f7ca069e3ac1deb706941bfd32e705a34f
commit + ce60b45a738eb442af4a85b4cee06d5948941272
blob - 891e4083e826751722a10a92617d043e43068a76
blob + 7dea2714b5d633a8927b55b9972df697cc3bd469
--- src/day-22.lisp
+++ 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))))
blob - 922b75223a874e320effffa076a1c21459ae9a02
blob + 28d76610fdf1986fc0fadbe3f4255aab9e50a672
--- t/day-22.lisp
+++ 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)))