diff --git a/10.rkt b/10.rkt new file mode 100644 index 0000000..4477c12 --- /dev/null +++ b/10.rkt @@ -0,0 +1,81 @@ +#lang racket + +(define (parse-input inp) + (string-split (string-trim inp))) + +(define input (parse-input (file->string "input.10.txt"))) +(define size (max (length input) (string-length (first input)))) + +(struct pos [x y] #:transparent) + +(define (input->positions inp) + (apply append + (for/list ([line inp] [y (in-range (length inp))]) + (for/list ([char line] [x (in-range (string-length line))] #:when (equal? char #\#)) + (pos x y))))) + +(define asteroids (apply set (input->positions input))) + +(define (pos-on-line a b c) + (define (pos-on-line+ a b c) + (let ([crs (- (* (- (pos-y c) (pos-y a)) (- (pos-x b) (pos-x a))) + (* (- (pos-x c) (pos-x a)) (- (pos-y b) (pos-y a))))] + [dot (+ (* (- (pos-x c) (pos-x a)) (- (pos-x b) (pos-x a))) + (* (- (pos-y c) (pos-y a)) (- (pos-y b) (pos-y a))))] + [sqdist (+ (sqr (- (pos-x b) (pos-x a))) + (sqr (- (pos-y b) (pos-y a))))]) + (cond [(not (zero? crs)) #f] + [(negative? dot) #f] + [(<= dot sqdist)]))) + (or (equal? b c) (pos-on-line+ a b c) (pos-on-line+ a c b))) + +(define (collide-line-pos start to pos-set) + (for/set ([pos pos-set] #:when (pos-on-line start to pos)) + pos)) + +(define (count-reachable-asteroids pos-set start) + (define pos-set/nostart (set-remove pos-set start)) + (define (count-reachable+ pos-set start) + (cond [(set-empty? pos-set) 0] + [else (begin + (define any (set-first pos-set)) + (define reachables (collide-line-pos start any pos-set)) + (add1 (count-reachable+ (set-subtract pos-set reachables) start)))])) + (count-reachable+ pos-set/nostart start)) + +;; Part 1 +;; The super naive way lol +(define best (argmax (curry count-reachable-asteroids asteroids) (set->list asteroids))) +(count-reachable-asteroids asteroids best) + +(define (dist-between p1 p2) + (sqrt (+ (sqr (- (pos-x p1) (pos-x p2))) (sqr (- (pos-y p1) (pos-y p2)))))) + +;; intentionally flip y and x for fun and profit +;; also negative x +;; (this is fuckery to get the sweep to start at 0 and go the normal direction) +(define (angle-to center p) + (define dx (- (pos-x center) (pos-x p))) + (define dy (- (pos-y p) (pos-y center))) + (+ (* 2 pi) (atan dx dy))) + +(define sorted (sort (set->list (set-remove asteroids best)) < #:key (curry angle-to best))) +(define grouped (group-by (curry angle-to best) sorted)) + +(define (min-of-group group) + (argmin (curry dist-between best) group)) + +(define (destroy-asteroids grouped n) + (cond [(zero? n) (min-of-group (first grouped))] + [else + (let* ([this-group (first grouped)] + [min-asteroid (min-of-group this-group)] + [rest-of-group (remove min-asteroid this-group)]) + (cond [(empty? rest-of-group) (destroy-asteroids (rest grouped) (sub1 n))] + [else (destroy-asteroids + (append (rest grouped) (list rest-of-group)) (sub1 n))]))])) + +;; I am like +;; 200 billion % not sure why this is 198 and not 199 (ie, 200 but starting at zero) +;; but it rly do be like that sometime +(destroy-asteroids grouped 198) \ No newline at end of file