Add day 11
[adventofcode2020.git] / src / day11.lisp
diff --git a/src/day11.lisp b/src/day11.lisp
new file mode 100644 (file)
index 0000000..f5641dc
--- /dev/null
@@ -0,0 +1,162 @@
+(asdf:load-system :adventofcode2020)
+(in-package #:adventofcode2020)
+(named-readtables:in-readtable fn-reader)
+
+(defun parse-layout (layout)
+  (make-array (list (length layout) (length (car layout)))
+              :initial-contents layout))
+
+(defun get-surr (game)
+  (lambda (i j)
+    (if (char= #\. (aref game i j)) -1
+      (->>
+        (map-product λ(list (funcall _0 i) (funcall _1 j))
+                     (list #'identity #'1+ #'1-)
+                     (list #'identity #'1+ #'1-)) 
+        (remove-if-not λ(apply #'array-in-bounds-p game _)) 
+        (mapcar λ(apply #'aref game _))
+        (count #\#)))))
+
+(defun get-sightlines (game)
+  (let ((functions (map-product #'list
+                    (list #'identity #'1+ #'1-)
+                    (list #'identity #'1+ #'1-))))
+    (lambda (i j)
+      (if (char= #\. (aref game i j)) -1
+          (loop for (f g) in functions
+                counting 
+                 (do ((fi (funcall f i) (funcall f fi))
+                      (gj (funcall g j) (funcall g gj)))
+                     ((or (not (array-in-bounds-p game fi gj))
+                          (not (char= #\. (aref game fi gj)))
+                          (and (= i fi) (= j gj)))
+                      (when (array-in-bounds-p game fi gj)
+                        (char= #\# (aref game fi gj))))))))))
+
+(defun step-game (game &key (surr #'get-surr))
+  (destructuring-bind (row col) (array-dimensions game)
+    (let* ((get-surr (funcall surr game))
+           (seat-limit (if (eq surr #'get-surr) 5 6))
+           (calc-next (lambda (i j)
+                        (let ((occ (funcall get-surr i j))
+                              (val (aref game i j))) 
+                          (cond ((=  occ 0) #\#) 
+                                ((>= occ seat-limit) #\L)
+                                (t val))))))
+      (->> 
+        (loop for i below row collecting 
+              (loop for j below col collecting (list i j)))
+        (mapcar λ(mapcar (curry #'apply calc-next) _)) 
+        (parse-layout)))))
+
+(defun fixed-point (game &key (surr #'get-surr))
+  (let ((step (step-game game :surr surr)))
+    (if (equalp game step) game
+        (fixed-point step :surr surr))))
+
+(defun count-occupied (game)
+  (loop for i below (array-dimension game 0)
+        summing (loop for j below (array-dimension game 1)
+                      counting (char= #\# (aref game i j)))))
+
+(day 11 input
+  (let ((game (parse-layout (list-from input))))
+    (part1 (count-occupied (fixed-point game :surr #'get-surr)))
+    (part2 (count-occupied (fixed-point game :surr #'get-sightlines)))))
+
+(def-suite day11)
+(in-suite day11)
+
+(defvar *simple-layout*
+  '("L.LL.LL.LL"
+    "LLLLLLL.LL"
+    "L.L.L..L.."
+    "LLLL.LL.LL"
+    "L.LL.LL.LL"
+    "L.LLLLL.LL"
+    "..L.L....."
+    "LLLLLLLLLL"
+    "L.LLLLLL.L" 
+    "L.LLLLL.LL"))
+
+(test get-sightlines
+  (is (equal 8 (funcall (get-sightlines 
+                          (parse-layout '(".......#."
+                                          "...#....."
+                                          ".#......."
+                                          "........."
+                                          "..#L....#"
+                                          "....#...."
+                                          "........."
+                                          "#........"
+                                          "...#....."))) 4 3)))
+  (is (equal 0 (funcall (get-sightlines 
+                          (parse-layout '("............."
+                                          ".L.L.#.#.#.#."
+                                          "............."))) 1 1)))
+  (is (equal 0 (funcall (get-sightlines 
+                          (parse-layout '(".##.##."
+                                          "#.#.#.#"
+                                          "##...##"
+                                          "...L..."
+                                          "##...##"
+                                          "#.#.#.#"
+                                          ".##.##."))) 3 3))))
+
+(test step-game
+  (is (equalp
+        (parse-layout '("#.LL.L#.##"
+                        "#LLLLLL.L#"
+                        "L.L.L..L.."
+                        "#LLL.LL.L#"
+                        "#.LL.LL.LL"
+                        "#.LLLL#.##"
+                        "..L.L....."
+                        "#LLLLLLLL#"
+                        "#.LLLLLL.L"
+                        "#.#LLLL.##"))
+        (step-game (step-game (parse-layout *simple-layout*)))))
+  (is (equalp
+        (parse-layout '("#.LL.LL.L#"
+                        "#LLLLLL.LL"
+                        "L.L.L..L.."
+                        "LLLL.LL.LL"
+                        "L.LL.LL.LL"
+                        "L.LLLLL.LL"
+                        "..L.L....."
+                        "LLLLLLLLL#"
+                        "#.LLLLLL.L"
+                        "#.LLLLL.L#"))
+        (step-game (step-game (parse-layout *simple-layout*) 
+                              :surr #'get-sightlines) 
+                   :surr #'get-sightlines))))
+
+(test fix
+  (is (equalp
+        (parse-layout '("#.#L.L#.##"
+                        "#LLL#LL.L#"
+                        "L.#.L..#.."
+                        "#L##.##.L#"
+                        "#.#L.LL.LL"
+                        "#.#L#L#.##"
+                        "..L.L....."
+                        "#L#L##L#L#"
+                        "#.LLLLLL.L"
+                        "#.#L#L#.##"))
+        (fixed-point (parse-layout *simple-layout*))))
+  (is (equalp
+        (parse-layout '("#.L#.L#.L#"
+                        "#LLLLLL.LL"
+                        "L.L.L..#.."
+                        "##L#.#L.L#"
+                        "L.L#.LL.L#"
+                        "#.LLLL#.LL"
+                        "..#.L....."
+                        "LLL###LLL#"
+                        "#.LLLLL#.L"
+                        "#.L#LL#.L#"))
+        (fixed-point (parse-layout *simple-layout*)
+                     :surr #'get-sightlines))))
+
+
+(run! 'day11)