Add day 12
[adventofcode2020.git] / src / day12.lisp
diff --git a/src/day12.lisp b/src/day12.lisp
new file mode 100644 (file)
index 0000000..0dbfdfc
--- /dev/null
@@ -0,0 +1,93 @@
+(asdf:load-system :adventofcode2020)
+(in-package #:adventofcode2020)
+(named-readtables:in-readtable fn-reader)
+
+(defun parse-action (str)
+  (cl-ppcre:register-groups-bind
+    ((λ(char _ 0) action) (#'parse-integer scalar)) ("([NSEWLRF])([0-9]+)" str)
+    (list action scalar)))
+
+(defstruct ship 
+  (heading #\E)
+  (ns-coord 0)
+  (ew-coord 0)
+  (waypoint-ns 1)
+  (waypoint-ew 10))
+
+(defun apply-turn-heading (heading degree)
+  (let ((directions #(#\N #\E #\S #\W))
+        (heading-delta (/ degree 90)))
+    (-<> 
+      (position heading directions)
+      (+ heading-delta)
+      (mod 4)
+      (aref directions <>))))
+
+(defun apply-turn-space (x y degree)
+  (if (= degree 0) (list x y)
+      (destructuring-bind (x y) (apply-turn-space x y (- degree (* (signum degree) 90)))
+        (if (> degree 0)
+            (list y (* -1 x))
+            (list (* -1 y) x)))))
+
+(defun apply-action-ship (ship act-pair)
+  (destructuring-bind (action scalar) act-pair
+    (let ((new-ship (copy-structure ship)))
+      (case action
+        (#\N (setf (ship-ns-coord new-ship) (+ (ship-ns-coord ship) scalar)))
+        (#\S (setf (ship-ns-coord new-ship) (- (ship-ns-coord ship) scalar)))
+        (#\E (setf (ship-ew-coord new-ship) (+ (ship-ew-coord ship) scalar)))
+        (#\W (setf (ship-ew-coord new-ship) (- (ship-ew-coord ship) scalar)))
+        (#\L (setf (ship-heading new-ship) (apply-turn-heading (ship-heading ship) (* -1 scalar))))
+        (#\R (setf (ship-heading new-ship) (apply-turn-heading (ship-heading ship) (* +1 scalar))))
+        (#\F (setf new-ship (apply-action-ship new-ship (list (ship-heading ship) scalar)))))
+      new-ship)))
+
+(defun apply-action-waypoint (ship act-pair)
+  (destructuring-bind (action scalar) act-pair
+    (let ((new-ship (copy-structure ship)))
+      (case action
+        (#\N (setf (ship-waypoint-ns new-ship) (+ (ship-waypoint-ns ship) scalar)))
+        (#\S (setf (ship-waypoint-ns new-ship) (- (ship-waypoint-ns ship) scalar)))
+        (#\E (setf (ship-waypoint-ew new-ship) (+ (ship-waypoint-ew ship) scalar)))
+        (#\W (setf (ship-waypoint-ew new-ship) (- (ship-waypoint-ew ship) scalar)))
+        (#\L (destructuring-bind (x y) (apply-turn-space (ship-waypoint-ew ship) (ship-waypoint-ns ship) (* -1 scalar))
+               (setf (ship-waypoint-ew new-ship) x)
+               (setf (ship-waypoint-ns new-ship) y)))
+        (#\R (destructuring-bind (x y) (apply-turn-space (ship-waypoint-ew ship) (ship-waypoint-ns ship) (* +1 scalar))
+               (setf (ship-waypoint-ew new-ship) x)
+               (setf (ship-waypoint-ns new-ship) y)))
+        (#\F (setf (ship-ns-coord new-ship) (+ (ship-ns-coord ship) (* scalar (ship-waypoint-ns ship))))
+             (setf (ship-ew-coord new-ship) (+ (ship-ew-coord ship) (* scalar (ship-waypoint-ew ship))))))
+      new-ship)))
+
+
+(day 12 input
+  (let ((actions (mapcar #'parse-action (list-from input))))
+    (part1 (let ((final-ship (reduce #'apply-action-ship actions :initial-value (make-ship))))
+             (manhattan-distance (list 0 0) (list (ship-ew-coord final-ship) 
+                                                  (ship-ns-coord final-ship)))))
+    (part2 (let ((final-ship (reduce #'apply-action-waypoint actions :initial-value (make-ship))))
+             (manhattan-distance (list 0 0) (list (ship-ew-coord final-ship) 
+                                                  (ship-ns-coord final-ship)))))))
+
+(def-suite day12)
+(in-suite day12)
+
+(defvar *simple-actions* 
+  '("F10" "N3" "F7" "R90" "F11"))
+
+(test apply-turns-heading
+  (is (equal #\S (apply-turn-heading #\E 90)))
+  (is (equal #\W (apply-turn-heading #\E 180)))
+  (is (equal #\N (apply-turn-heading #\E 270)))
+  (is (equal #\N (apply-turn-heading #\E -90)))
+  (is (equal #\W (apply-turn-heading #\E -180))))
+
+(test apply-actions-ship
+  (is (equalp
+        (make-ship :heading #\S :ns-coord -8 :ew-coord 17)
+        (reduce #'apply-action-ship (mapcar #'parse-action *simple-actions*)
+                :initial-value (make-ship)))))
+
+(run! 'day12)