ADDED   day10-output.png
Index: day10-output.png
==================================================================
--- day10-output.png
+++ day10-output.png
cannot compute difference between binary files

ADDED   day10.rkt
Index: day10.rkt
==================================================================
--- day10.rkt
+++ day10.rkt
@@ -0,0 +1,79 @@
+#lang racket
+
+(require pict rackunit)
+
+(struct point (coords speed) #:transparent)
+
+;; Convert one line of the input to a point
+(define (string->point str)
+  (define str-nums
+    (map string->number 
+         (rest (regexp-match #px"position=<\\s*([-]*\\d+),\\s*([-]*\\d+)> velocity=<\\s*([-]*\\d+),\\s*([-]*\\d+)>" str))))
+  (point (take str-nums 2) (drop str-nums 2)))
+
+(define input (map string->point (file->lines "day10-input.txt")))
+(define example (map string->point (file->lines "day10-example.txt")))
+
+;; Move a point according to its velocity
+(define (point-tick p)
+  (match-define (point coords speeds) p)
+  (point (map + coords speeds) speeds))
+
+;; Returns the area of the smallest rectangular grid needed
+;; to hold all the given points in their current positions
+(define (bounding-square-area points)
+  (match-define (list xs ys) (apply map list (map point-coords points)))
+  (* (add1 (- (apply max xs) (apply min xs)))
+     (add1 (- (apply max ys) (apply min ys)))))
+
+;; Iterate to find the locations of all the points when they converge on the smallest area.
+;; Returns the list point coordinates at such a state, as well as area of the bounding square
+;; and the number of “seconds” (iterations) it took to get there.
+(define (find-convergence starting-points)
+  (define starting-size (bounding-square-area starting-points))
+  (let loop ([last-points starting-points]
+             [last-size starting-size]
+             [seconds 0])
+    (define new-points (map point-tick last-points))
+    (define new-size (bounding-square-area new-points))
+    (cond [(> new-size last-size) (values (map point-coords last-points) last-size seconds)]
+          [else (loop new-points new-size (add1 seconds))])))
+
+;; Translates a list of coordinates, making them relative to a (0,0) starting-point
+(define (translate-coords coord-list)
+  (match-define (list xs ys) (apply map list coord-list))
+  (define min-x (apply min xs))
+  (define min-y (apply min ys))
+  (map (lambda (coord) (map - coord (list min-x min-y))) coord-list))
+
+;; Returns a pict with a box drawn at each of the coordinates
+(define (coords->pict coord-list)
+  (match-define (list xs ys) (apply map list coord-list))
+  (define max-x (apply max xs))
+  (define max-y (apply max ys))
+  (define pixel-size 10)
+  (define (render-pixels dc dx dy)
+    (send dc set-brush "orange" 'solid)
+    (send dc set-pen "black" 1 'solid)
+    (for ([c (in-list coord-list)])
+         (match-define (list x y) c)
+         (send dc draw-rectangle (* x pixel-size) (* y pixel-size) pixel-size pixel-size)))
+  
+  (unsafe-dc render-pixels (* pixel-size (add1 max-x)) (* pixel-size (add1 max-y))))
+
+;; Part 1: What is the message eventually spelled out?
+;; Use your eyeballs for this one.
+(define (day10-part1)
+  (define-values (coord-list size secs) (find-convergence input))
+  (define result (coords->pict (translate-coords coord-list)))
+  (send (pict->bitmap result) save-file "day10-output.png" 'png)
+  result)
+
+;; Part 2: How many “seconds” will it take for the message to be spelled out?
+(define (day10-part2)
+  (define-values (c sz secs) (find-convergence input))
+  secs)
+
+(module+ test
+  (check-equal? (time (day10-part2)) 10605)) ; Correct answer for part 2
+;; Did not bother writing a test for part 1