Files
advent-of-code-2021/08-seven-segment-search/code.scm
2021-12-08 14:47:24 +01:00

179 lines
5.3 KiB
Scheme

(import (srfi :43)) ; vector extensions
(load "../common.scm")
; convert wire's letter to corresponding bit of 7-segment display
(define (signal->bit c)
(case c
[#\a #b0000001]
[#\b #b0000010]
[#\c #b0000100]
[#\d #b0001000]
[#\e #b0010000]
[#\f #b0100000]
[#\g #b1000000]))
; read a set of wires and return corresponding bits
(define (parse-wires file)
(let loop [(n 0)]
(let [(c (get-char file))]
(if (and (not (eof-object? c))
(char-alphabetic? c))
[loop (bitwise-ior (signal->bit c) n)]
n))))
; reads a set of 10 signals and 4 digits
; returned as a two lists of bitfields
(define (parse-line file)
(let loop [(signals '()) (digits '()) (n 0)]
(cond
[(< n 10)
(loop (cons (parse-wires file) signals) digits (+ n 1))]
[(= n 10)
(get-char file) ; consume |
(get-char file) ; consume space
(loop signals digits (+ n 1))]
[(< n 15)
(loop signals (cons (parse-wires file) digits) (+ n 1))]
[else
(list (reverse signals) (reverse digits))])))
; read all the sets of signals/digits
(define (parse-file file)
(let loop [(lines '())]
(if (eof-object? (peek-char file))
lines
(loop (cons (parse-line file) lines)))))
; count digits that are easily recognized as 1, 4, 7 or 8
; since they are the only combinations of 2, 4, 3 and 7 wires
; it's as easy as counting the number of bits set in the bitfield
(define (count-easy-digits lines)
(let [(count 0)]
(for-each
(lambda (line)
(for-each
(lambda (digit)
(let [(bit-count (bitwise-bit-count digit))]
(when (or
(= bit-count 2) ; that's a 1
(= bit-count 4) ; that's a 4
(= bit-count 3) ; that's a 7
(= bit-count 7)) ; that's an 8
(set! count (+ count 1)))))
(cadr line)))
lines)
count))
; returns a vector of 10 encodings for numbers 0 to 9 from the 10 signals received
(define (build-decoder signals)
(let [(decoder (make-vector 10))
(encode-2-3-5 '())
(encode-0-6-9 '())]
(for-each
(lambda (signal)
(case (bitwise-bit-count signal)
(2 (vector-set! decoder 1 signal))
(3 (vector-set! decoder 7 signal))
(4 (vector-set! decoder 4 signal))
(5 (set! encode-2-3-5 (cons signal encode-2-3-5)))
(6 (set! encode-0-6-9 (cons signal encode-0-6-9)))
(7 (vector-set! decoder 8 signal))))
signals)
; between the 3 possible encodings of 3,
; only the one with both the bits of 1 set can be correct
(let [(encode-1 (vector-ref decoder 1))]
(vector-set! decoder 3
(fold-left
(lambda (encoding signal)
(if (= (bitwise-and signal encode-1) encode-1)
signal
encoding))
0 encode-2-3-5)))
; between the 3 possible encodings of 9,
; only the one with all the bits of 4 set can be correct
(let [(encode-4 (vector-ref decoder 4))]
(vector-set! decoder 9
(fold-left
(lambda (encoding signal)
(if (= (bitwise-and signal encode-4) encode-4)
signal
encoding))
0 encode-0-6-9)))
; (xor (encode 4) (encode 1) yields the bits corresponding to
; the top left and middle segments
(let [(tl+m (bitwise-xor (vector-ref decoder 1)
(vector-ref decoder 4)))]
; between the 3 possible encodings of 5,
; only the one with both of those bits set can be correct
(vector-set! decoder 5
(fold-left
(lambda (encoding signal)
(if (= (bitwise-and signal tl+m) tl+m)
signal
encoding))
0 encode-2-3-5))
; between the 3 possible encodings of 6,
; only the one that is not nine and has top-left and mid segment set can be correct
(let [(encode-9 (vector-ref decoder 9))]
(vector-set! decoder 6
(fold-left
(lambda (encoding signal)
(if (and (= (bitwise-and signal tl+m) tl+m)
(not (= signal encode-9)))
signal
encoding))
0 encode-0-6-9))))
; encoding for two is the last possible encoding
(let [(encode-3 (vector-ref decoder 3))
(encode-5 (vector-ref decoder 5))]
(vector-set! decoder 2
(fold-left
(lambda (encoding signal)
(if (not (or (= signal encode-3)
(= signal encode-5)))
signal
encoding))
0 encode-2-3-5)))
; encoding for zero is the last possible encoding
(let [(encode-6 (vector-ref decoder 6))
(encode-9 (vector-ref decoder 9))]
(vector-set! decoder 0
(fold-left
(lambda (encoding signal)
(if (not (or (= signal encode-6)
(= signal encode-9)))
signal
encoding))
0 encode-0-6-9)))
decoder))
; with given decoder vector, decode input digit
(define (decode decoder input)
(vector-index (lambda (encoding) (= encoding input))
decoder))
; returns the number read on the 4x 7-segment display
; by decoding the encoding of digits with the signals
; and applying the decoded bitfields to the digits received
(define (decode-digits line)
(let [(signals (car line))
(digits (cadr line))]
(let [(decoder (build-decoder signals))]
(fold-left
(lambda (result digit)
(+
(* 10 result)
(decode decoder digit)))
0
digits))))
(call-with-input-file
"input"
(lambda (file)
(let [(lines (parse-file file))]
(printf "part 1:~% Number of easy digits (1, 4, 7 or 8): ~a~%"
(count-easy-digits lines))
(printf "part 2:~% Sum of all decoded digits: ~a~%"
(apply + (map decode-digits lines))))))