#lang racket (require advent-of-code threading) (struct posn (x y) #:transparent) (struct part (n posns) #:transparent) (define (make-board port) (for*/hash ([(row y) (in-indexed (port->lines port))] [(col x) (in-indexed (string->list row))] #:unless (equal? col #\.)) (define v (cond [(string->number (string col))] [(equal? col #\*) 'gear] [else 'other])) (values (posn x y) v))) (define board (~> (open-aoc-input (find-session) 2023 3 #:cache #true) make-board)) (define (posn (for/hash ([(k v) (in-hash b)] #:when (f v)) (values k v)) hash->list (sort posn> part-posns (append-map neighbors) remove-duplicates)) (define (symbol-in-neighbors b pt acc) (~>> pt to-neighbors (ormap (λ (p) (let ([lookup (hash-ref b p #f)]) (or (equal? lookup 'gear) (equal? lookup 'other))))) ((λ (bool) (if bool (+ acc (part-n pt)) acc))))) ;; part 1 (define parts (~>> board (find-cells integer?) group-into-parts)) (foldl (curry symbol-in-neighbors board) 0 parts) ;; part 2 (define gears (~>> board (find-cells (curry equal? 'gear)) (map car))) (define parts-with-neighbors (map (λ (pt) (struct-copy part pt [posns (to-neighbors pt)])) parts)) (define (find-parts-near-gear pts gear) (filter-map (λ (pt) (and (findf (curry equal? gear) (part-posns pt)) (part-n pt))) pts)) (~>> gears (filter-map (λ~>> (find-parts-near-gear parts-with-neighbors) ((λ (ns) (if (= (length ns) 2) (* (first ns) (second ns)) #f))))) (apply +))