diff --git a/24.rkt b/24.rkt index 431f724..b4f5e11 100644 --- a/24.rkt +++ b/24.rkt @@ -4,23 +4,20 @@ ;; solution for day 24 -; (define d->r degrees->radians) -; (define (i->e x) (inexact->exact (round x))) +;; constraint: x+y+z = 0 +;; this could be formalized with a contract but part2 is already slow enough tbh (struct hex [x y z] #:transparent) (define (hex+ a b) (hex (+ (hex-x a) (hex-x b)) (+ (hex-y a) (hex-y b)) (+ (hex-z a) (hex-z b)))) -(define *dirs* (hash - 'e (hex 1 -1 0) - 'ne (hex 1 0 -1) - 'nw (hex 0 1 -1) - 'w (hex -1 1 0) - 'sw (hex -1 0 1) - 'se (hex 0 -1 1))) +(define *dirs* (hash 'e (hex 1 -1 0) 'ne (hex 1 0 -1) 'nw (hex 0 1 -1) + 'w (hex -1 1 0) 'sw (hex -1 0 1) 'se (hex 0 -1 1))) (define (paths->tiles input) + ;; white is #f + ;; black is #t (define tiles (make-hash)) (for ([path (in-list input)]) (define posn @@ -29,38 +26,43 @@ (hash-update! tiles posn not #f)) tiles) -(define (part1 input) - ;; white is #f - ;; black is #t - (define tiles (paths->tiles input)) +(define (count-black tiles) (for/sum ([(_ v) (in-hash tiles)] #:when v) 1)) +(define (part1 input) + (count-black (paths->tiles input))) + (define (part2 input) (define tiles (paths->tiles input)) - (define (step tiles) + (define neighbors-cache (make-hash)) + + (define (step tiles radius) (define new-tiles (make-hash)) - (define minx (apply min (map hex-x (hash-keys tiles)))) - (define maxx (apply max (map hex-x (hash-keys tiles)))) - (define miny (apply min (map hex-y (hash-keys tiles)))) - (define maxy (apply max (map hex-y (hash-keys tiles)))) - (define minz (apply min (map hex-z (hash-keys tiles)))) - (define maxz (apply max (map hex-z (hash-keys tiles)))) - (for* ([x (in-range (sub1 minx) (+ maxx 2))] [y (in-range (sub1 miny) (+ maxy 2))] [z (in-range (sub1 minz) (+ maxz 2))] #:when (zero? (+ x y z))) + (for* ([x (in-range (- radius) (add1 radius))] [y (in-range (- radius) (add1 radius))] + [z (in-range (- radius) (add1 radius))] #:when (zero? (+ x y z))) (define thispos (hex x y z)) (define thistile (hash-ref tiles thispos #f)) - (define num-blacks (for/sum ([t (in-list (hash-values *dirs*))] #:when (hash-ref tiles (hex+ thispos t) #f)) 1)) + (define neighbors (hash-ref! neighbors-cache thispos + (thunk (for/list ([t (in-list (hash-values *dirs*))]) + (hex+ thispos t))))) + (define num-blacks + (for/sum ([neigh (in-list neighbors)] #:when (hash-ref tiles neigh #f)) 1)) (hash-set! new-tiles thispos - (cond - [(and thistile (or (zero? num-blacks) (> num-blacks 2))) #f] - [thistile #t] - [(= num-blacks 2) #t] - [else #f]))) + (match* (thistile num-blacks) + [(#t (or 0 (not (or 1 2)))) #f] + [(#t _) #t] + [(#f 2) #t] + [(#f _) #f]))) new-tiles) - (define end (for/fold ([tiles tiles]) ([i (in-range 100)]) (displayln i) (step tiles))) - - (for/sum ([(_ v) (in-hash end)] #:when v) 1)) + (define radius (apply max (map #{max (abs (hex-x %)) (abs (hex-y %)) (abs (hex-z %))} + (hash-keys tiles)))) + (begin0 + (count-black + (for/fold ([tiles tiles]) ([i (in-range 100)] [radius (in-range (add1 radius) +inf.0)]) + (printf "\r\x1b[K~a/100..." i) (flush-output) (step tiles radius))) + (printf "\r\1b[Kdone\n"))) ;; parse input file (define (parse fname)