--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/data/2019/12.txt Thu Dec 12 20:02:09 2019 -0500
@@ -0,0 +1,4 @@
+<x=3, y=2, z=-6>
+<x=-13, y=18, z=10>
+<x=-8, y=-1, z=13>
+<x=5, y=10, z=4>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/2019/days/day-12.lisp Thu Dec 12 20:02:09 2019 -0500
@@ -0,0 +1,77 @@
+(defpackage :advent/2019/12 #.cl-user::*advent-use*)
+(in-package :advent/2019/12)
+
+(defclass* moon () (pos vel))
+
+(defun make-moon (x y z)
+ (make-instance 'moon
+ :pos (vector x y z)
+ :vel (vector 0 0 0)))
+
+(defun parse-moons (data)
+ (mapcar (curry #'apply #'make-moon) data))
+
+(defun apply-gravity (moon other)
+ (map-into (vel moon) (lambda (v m o)
+ (+ v (signum (- o m))))
+ (vel moon)
+ (pos moon)
+ (pos other)))
+
+(defun apply-all-gravity (moons)
+ (alexandria:map-permutations (curry #'apply #'apply-gravity)
+ moons :length 2 :copy nil))
+
+(defun vincf (vec delta)
+ (map-into vec #'+ vec delta))
+
+(defun apply-velocity (moon)
+ (vincf (pos moon) (vel moon)))
+
+(defun apply-all-velocity (moons)
+ (map nil #'apply-velocity moons))
+
+(defun tick (moons &optional (n 1))
+ (do-repeat n
+ (apply-all-gravity moons)
+ (apply-all-velocity moons)))
+
+(defmethod energy ((moon moon))
+ (* (summation (pos moon) :key #'abs)
+ (summation (vel moon) :key #'abs)))
+
+(defmethod energy ((moons sequence))
+ (summation moons :key #'energy))
+
+(defun velocity-zeroed-p (moons axis)
+ (iterate (for moon :in moons)
+ (always (zerop (aref (vel moon) axis)))))
+
+(defun part2 (moons)
+ ;; Clever trick from https://www.reddit.com/r/adventofcode/comments/e9nqpq/day_12_part_2_2x_faster_solution/
+ (iterate
+ (for tick :from 1)
+ (tick moons)
+ (finding-first tick :such-that (velocity-zeroed-p moons 0) :into x)
+ (finding-first tick :such-that (velocity-zeroed-p moons 1) :into y)
+ (finding-first tick :such-that (velocity-zeroed-p moons 2) :into z)
+ (until (and x y z))
+ (returning (* 2 (lcm x y z)))))
+
+(define-problem (2019 12) (data read-lines-of-numbers-and-garbage) (14780 279751820342592)
+ (values
+ (let ((moons (parse-moons data)))
+ (tick moons 1000)
+ (energy moons))
+ (part2 (parse-moons data))))
+
+
+#; Scratch --------------------------------------------------------------------
+
+(defparameter *a* (make-moon '(0 0 0)))
+(defparameter *b* (make-moon '(1 0 -2)))
+
+(run '((-1 0 2)
+ (2 -10 -7)
+ (4 -8 8)
+ (3 5 -1)))