139 lines
4.0 KiB
Scheme
139 lines
4.0 KiB
Scheme
(import (srfi :43))
|
|
|
|
(load "../common.scm")
|
|
|
|
; returns a number from two characters
|
|
; ex: '0' and '9' => 9
|
|
; ' ' and '6' => 6
|
|
; '1' and '2' => 12
|
|
(define (char-pair->number c1 c2)
|
|
(if (char-numeric? c1)
|
|
[+ (* 10 (char->number c1)) (char->number c2)]
|
|
[char->number c2]))
|
|
|
|
; returns a vector of 25 numbers
|
|
; parsed from input file where numbers are separated by whitespace
|
|
(define (read-board file)
|
|
(let parse-loop [(i 0) (numbers '())]
|
|
(if (= i 25)
|
|
[list->vector (reverse numbers)]
|
|
[let [(c2 (get-char file))
|
|
(c1 (get-char file))]
|
|
(get-char file) ; consume space or empty line
|
|
(parse-loop
|
|
(+ i 1)
|
|
(cons (char-pair->number c1 c2) numbers))])))
|
|
|
|
; returns a list of vectors of 25 numbers parsed from input file
|
|
(define (read-boards file)
|
|
(reverse
|
|
(let loop [(boards '())]
|
|
(get-char file) ; consume empty line
|
|
(if (eof-object? (peek-char file))
|
|
boards
|
|
[loop (cons (read-board file) boards)]))))
|
|
|
|
; returns a number where bit N is set if N'th number
|
|
; in board matches given number
|
|
; bits that are already set in mark are kept
|
|
(define (mark-bingo number board mark)
|
|
(let loop [(i 0)]
|
|
(if (< i 25)
|
|
[if (= (vector-ref board i) number)
|
|
[bitwise-ior mark (bitwise-arithmetic-shift-left 1 i)]
|
|
[loop (+ i 1)]]
|
|
mark)))
|
|
|
|
; marks the corresponding n'th bit in each board if
|
|
; an n'th element of the corresponding board matches input number
|
|
(define (mark-bingos! number boards marks)
|
|
(let loop [(board (car boards))
|
|
(boards (cdr boards))
|
|
(index 0)]
|
|
(vector-set! marks index
|
|
(mark-bingo number board (vector-ref marks index)))
|
|
(when (not (null? boards))
|
|
(loop (car boards) (cdr boards) (+ index 1)))))
|
|
|
|
; all possible bitfields corresponding to a line or column of bingo
|
|
(define bingos
|
|
'#(; lines
|
|
#b1111100000000000000000000
|
|
#b0000011111000000000000000
|
|
#b0000000000111110000000000
|
|
#b0000000000000001111100000
|
|
#b0000000000000000000011111
|
|
; columns
|
|
#b1000010000100001000010000
|
|
#b0100001000010000100001000
|
|
#b0010000100001000010000100
|
|
#b0001000010000100001000010
|
|
#b0000100001000010000100001))
|
|
|
|
; check if any bitfield represented grid of marked bingo cell is bingo!
|
|
(define (check-bingos marks)
|
|
(vector-index
|
|
(lambda (mark)
|
|
(vector-any
|
|
(lambda (mask)
|
|
(= mask (bitwise-and mask mark)))
|
|
bingos))
|
|
marks))
|
|
|
|
; computes the score of the winning board
|
|
(define (score boards marks index last-draw)
|
|
(let [(board (list-ref boards index))
|
|
(mark (vector-ref marks index))]
|
|
(let loop [(score 0) (index 0)]
|
|
(if (< index 25)
|
|
[if (bitwise-bit-set? mark index)
|
|
[loop score (+ index 1)]
|
|
[loop (+ score (vector-ref board index)) (+ index 1)]]
|
|
[* last-draw score]))))
|
|
|
|
; returns list with the index of the board that won and the score
|
|
; #f if none won after all draws
|
|
(define (bingo draws boards marks)
|
|
(let loop [(draw (car draws))
|
|
(draws (cdr draws))]
|
|
(mark-bingos! draw boards marks)
|
|
(let [(check (check-bingos marks))]
|
|
(if check
|
|
[list check (score boards marks check draw)]
|
|
[if (null? draws)
|
|
#f
|
|
[loop (car draws) (cdr draws)]]))))
|
|
|
|
; returns a list without its n-th element
|
|
(define (except list n-th)
|
|
(let loop [(list list) (i 0)]
|
|
(if (= i n-th)
|
|
(cdr list)
|
|
(cons (car list) (loop (cdr list) (+ i 1))))))
|
|
|
|
(define (last-winning draws boards)
|
|
(let loop [(boards boards)
|
|
(prev-score 0)
|
|
(marks (make-vector (length boards) 0))]
|
|
(if (null? boards)
|
|
prev-score
|
|
[let [(index+score (bingo draws boards marks))]
|
|
(if index+score
|
|
[loop
|
|
(except boards (car index+score))
|
|
(cadr index+score)
|
|
(make-vector (length boards) 0)]
|
|
[cadr index+score])])))
|
|
|
|
(call-with-input-file
|
|
"input"
|
|
(lambda (file)
|
|
(let* [(draws (read-comma-separated-numbers file))
|
|
(boards (read-boards file))]
|
|
(printf "part 1: ~% ")
|
|
(let* [(marks (make-vector (length boards) 0))
|
|
(index+score (bingo draws boards marks))]
|
|
(printf "bingo! ~a~%" (cadr index+score)))
|
|
(printf "part 2: ~% bingo! ~a~%"
|
|
(last-winning draws boards)))))
|