#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)