You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
114 lines
3.3 KiB
114 lines
3.3 KiB
#lang racket |
|
(require "lib/common.rkt") |
|
|
|
;; board : [Vectorof Integer] |
|
;; seen : [Setof Integer] |
|
(struct bingo (board [seen #:mutable]) #:transparent) |
|
|
|
;; boards are 5x5 |
|
(define (board-ref vec i j) |
|
(vector-ref vec (+ j (* 5 i)))) |
|
|
|
;; Input -> [Listof Integer] [Listof Bingo] |
|
(define (parse lines) |
|
(define draws (first lines)) |
|
(define boards (rest lines)) |
|
|
|
(values (map string->number (string-split draws ",")) |
|
(for/list ([board (in-list boards)]) |
|
(bingo (apply vector (map string->number (string-split board))) |
|
(mutable-set))))) |
|
|
|
(define (is-winner? bingo) |
|
(or |
|
;; verticals |
|
(for/or ([i (in-range 5)]) |
|
(for/and ([j (in-range 5)]) |
|
(set-member? |
|
(bingo-seen bingo) |
|
(board-ref (bingo-board bingo) i j)))) |
|
;; horizontals |
|
(for/or ([i (in-range 5)]) |
|
(for/and ([j (in-range 5)]) |
|
(set-member? |
|
(bingo-seen bingo) |
|
(board-ref (bingo-board bingo) j i)))))) |
|
|
|
(define (calculate-score bingo last-called) |
|
(define board-set (apply set (vector->list (bingo-board bingo)))) |
|
(define remaining (set-subtract board-set (bingo-seen bingo))) |
|
|
|
(* (for/sum ([i (in-set remaining)]) i) |
|
last-called)) |
|
|
|
(define (day4a lines) |
|
(define-values (draws boards) (parse lines)) |
|
|
|
(define last-called |
|
(for/fold ([last-called -1]) |
|
([draw (in-list draws)] |
|
#:break (for/or ([board (in-list boards)]) |
|
(is-winner? board))) |
|
(for ([board (in-list boards)]) |
|
(when (vector-member draw (bingo-board board)) |
|
(set-add! (bingo-seen board) draw))) |
|
draw)) |
|
(define winner |
|
(for/first ([board (in-list boards)] |
|
#:when (is-winner? board)) |
|
board)) |
|
|
|
(calculate-score winner last-called)) |
|
|
|
(define (day4b lines) |
|
(define-values (draws boards) (parse lines)) |
|
|
|
(define last-winner |
|
(for/fold ([winners '()] |
|
[remaining boards] |
|
#:result (first winners)) |
|
([draw (in-list draws)]) |
|
(for ([board (in-list remaining)]) |
|
(when (vector-member draw (bingo-board board)) |
|
(set-add! (bingo-seen board) draw))) |
|
(values |
|
(append |
|
(for/list ([board (in-list remaining)] |
|
#:when (is-winner? board)) |
|
(cons board draw)) |
|
winners) |
|
(filter (λ (x) (not (is-winner? x))) remaining)))) |
|
|
|
(print-board (car last-winner)) |
|
(displayln (cdr last-winner)) |
|
(calculate-score (car last-winner) (cdr last-winner))) |
|
|
|
(define (print-board in) |
|
(for ([e (in-slice 5 (in-vector (bingo-board in)))]) |
|
(displayln e))) |
|
|
|
(module+ main |
|
(call-with-input-file "data/day4.txt" |
|
(lambda (prt) |
|
(define lines (string-split (port->string prt) "\n\n")) |
|
(answer 4 1 (day4a lines)) |
|
(answer 4 2 (day4b lines))))) |
|
|
|
(module+ test |
|
(require rackunit) |
|
(define test-board |
|
(vector 14 21 17 24 4 |
|
10 16 15 9 19 |
|
18 8 23 26 20 |
|
22 11 13 6 5 |
|
2 0 12 3 7)) |
|
(define test-seen |
|
(set 14 21 17 4 9 23 11 2 0 7 24)) |
|
|
|
(check-true (is-winner? (bingo test-board test-seen))) |
|
|
|
(call-with-input-file "data/day4.test.txt" |
|
(lambda (prt) |
|
(define lines (string-split (port->string prt) "\n\n")) |
|
(check-equal? (day4a lines) 4512) |
|
(check-equal? (day4b lines) 1924))))
|
|
|