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