Each packet has a version number; the first half of the problem is to return the sum of all the version numbers.
$ cat 16a.rkt #lang racket (provide (struct-out packet-literal) (struct-out packet-operator) parse-input read-input ) (require "aoc.rkt" ) (define aoc16a (lambda args ; Return the summed packet versions for the first half of the ; sixteenth AoC '21 problem. (let-values (((packets bl) (parse-input (apply read-input args)))) (traverse-packets packets)))) (define (traverse-packets pl) ; Return the sum of all the version numbers in the given list of ; packets. (define (traverse-packet p) ; Return the sum of all the version numbers in the given packet. (cond ((packet-literal? p) (packet-literal-version p)) ((packet-operator? p) (+ (packet-operator-version p) (traverse-packets (packet-operator-subpackets p)))) (#t (raise-user-error 'traverse-packet "unrecognized packet type")))) (foldr + 0 (map traverse-packet pl))) (define (hex-char->4-bit-list hc) ; Return the given hex-digit character as a four-element list of 0s ; and 1s (an exploded nybble). (define (hex-char->number hdc) ; Return the number represented give hex-digit character. (let ((hc (char-downcase hdc))) (cond ((and (char<=? #\0 hc) (char<=? hc #\9)) (- (char->integer hc) (char->integer #\0))) ((and (char<=? #\a hc) (char<=? hc #\h)) (+ (- (char->integer hc) (char->integer #\a)) 10)) (#t (raise-user-error 'hex-char->number "non-hex digit given: " hdc))))) (define (number->bit-list d po2) ; Taking po2 to be a number of the form 2^n for n > -1, return the ; right-most n + 1 bits of the given number as a list. If p02 is ; zero, return the empty list. (if (zero? po2) '() (let ((next-po2 (quotient po2 2))) (if (<= po2 d) (cons 1 (number->bit-list (- d po2) next-po2)) (cons 0 (number->bit-list d next-po2)))))) (number->bit-list (hex-char->number hc) 8)) (struct packet-literal (version type value) #:transparent) (struct packet-operator (version type subpackets) #:transparent) (define (parse-input bl) ; Return the values (pl bl) where pl is the list of packets parsed ; from the given bit list, and bl is whatever's left of the given ; bit list after parsing. (let loop ((bl bl) (packets '())) (if (< (length bl) 8) (values (reverse packets) bl) (let-values (((b p bl) (parse-packet bl))) (loop bl (cons p packets)))))) (define (parse-packet bl) ; Return the values (b p bl). If the given bit list has a prefix ; interperable as a packet, b is #t, p is the packet represented by ; the prefix, and bl is the suffix of the given bit list following ; the packet prefix. If the given bit list doesn't have a prefix ; interperable as a packet, b is #f, p is undefined and bl is the ; given bit list. (let-values (((b packet bl) (parse-packet-literal bl))) (if b (values b packet bl) (let-values (((b packet bl) (parse-packet-operator bl))) (if b (values b packet bl) (values #f #f bl)))))) (define (parse-packet-literal bl) ; Return the values (b p bl). If the given bit list has a prefix ; interperable as a literal packet, b is #t, p is the literal packet ; represented by the prefix, and bl is the suffix of the given bit ; list following the literal-packet prefix. If the given bit list ; doesn't have a prefix interperable as a literal packet, b is #f, p ; is undefined and bl is the given bit list. (let-values (((version bl1) (parse-prefix bl 3))) (let-values (((type bl2) (parse-prefix bl1 3))) (if (= type 4) (let loop ((bl bl2) (sum 0)) (let-values (((flag bl) (parse-prefix bl 1))) (let-values (((val bl) (parse-prefix bl 4))) (let ((sum (+ (* sum 16) val))) (if (zero? flag) (values #t (packet-literal version type sum) bl) (loop bl sum)))))) (values #f #f bl))))) (define (parse-packet-operator bl) ; Return the values (b p bl). If the given bit list has a prefix ; interperable as an operator packet, b is #t, p is the operator ; packet represented by the prefix, and bl is the suffix of the ; given bit list following the operator-packet prefix. If the given ; bit list doesn't have a prefix interperable as an operator packet, ; b is #f, p is undefined and bl is the given bit list. (define (subpackets-by-packets bl) ; Return the values (b p bl). If the given bit list has a prefix ; interperable as an operator packet, b is #t, p is the operator ; packet represented by the prefix, and bl is the suffix of the ; given bit list following the operator-packet prefix. If the ; given bit list doesn't have a prefix interperable as an operator ; packet, b is #f, p is undefined and bl is the given bit list. (let-values (((packet-count bl) (parse-prefix bl 11))) (let loop ((packet-count packet-count) (bl bl) (subpackets '())) (if (zero? packet-count) (values (reverse subpackets) bl) (let-values (((b packet bl) (parse-packet bl))) (loop (- packet-count 1) bl (cons packet subpackets))))))) (define (subpackets-by-bits bl) ; Return the values (spl bl). If the given bit list has a prefix ; interperable as an operator packet, b is #t, p is the operator ; packet represented by the prefix, and bl is the suffix of the ; given bit list following the operator-packet prefix. (let-values (((bits-available bl) (parse-prefix bl 15))) (let loop ((bits-read 0) (bl bl) (subpackets '())) (if (= bits-read bits-available) (values (reverse subpackets) bl) (let ((bits-available (length bl))) (let-values (((b packet bl) (parse-packet bl))) (loop (+ bits-read (- bits-available (length bl))) bl (cons packet subpackets)))))))) (let-values (((version bl1) (parse-prefix bl 3))) (let-values (((type bl2) (parse-prefix bl1 3))) (if (= type 4) (values #f #f bl) (let-values (((length-type bl) (parse-prefix bl2 1))) (if (zero? length-type) (let-values (((subpackets bl) (subpackets-by-bits bl))) (values #t (packet-operator version type subpackets) bl)) (let-values (((subpackets bl) (subpackets-by-packets bl))) (values #t (packet-operator version type subpackets) bl)))))))) (define (parse-prefix bl n) ; Return the values (s bls), where s is the number represented by ; the first n bits from the bit-list bl (most-significant bit ; leftmost) and bls is the remainder of the list bl when the first n ; elements are removed. (when (or (< n 0) (> n (length bl))) (raise-user-error 'parse-prefix "prefix size ~a not in bit-list range [0..~a]" n (length bl))) (if (zero? n) 0 (let loop ((po2 (expt 2 (- n 1))) (bl bl) (sum 0)) (let ((sum (+ sum (* po2 (car bl)))) (po2 (quotient po2 2)) (bl (cdr bl))) (if (zero? po2) (values sum bl) (loop po2 bl sum)))))) (define read-input (lambda arg? ; Return the risk-map described by the problem input. If no ; argument is given, use the contest problem input; otherwise assume ; the argument is the string representation of a problem input. (flatten (map (lambda (l) (map hex-char->4-bit-list (string->list (car l)))) (car (aoc-read-input (if (null? arg?) 16 (car arg?)))))))) (define example-a1-input "D2FE28") ; a literal packet (define example-a1-result (digit-string->digit-list "110100101111111000101000")) (define example-a2-input "38006F45291200") (define example-a2-result (digit-string->digit-list "00111000000000000110111101000101001010010001001000000000")) (define example-a3-input "EE00D40C823060") (define example-a3-result (digit-string->digit-list "11101110000000001101010000001100100000100011000001100000")) (define example-a4-input "8A004A801A8002F478") (define example-a4-result 16) (define example-a5-input "620080001611562C8802118E34") (define example-a5-result 12) (define example-a6-input "C0015000016115A2E0802F182340") (define example-a6-result 23) (define example-a7-input "A0016C880162017C3686B18A3D4780") (define example-a7-result 31) (module+ main (aoc16a)) (module+ test (require rackunit) (check-equal? (read-input example-a1-input) example-a1-result) (check-equal? (read-input example-a2-input) example-a2-result) (check-equal? (read-input example-a3-input) example-a3-result) (define (check-literal-packet p ver val) (check-equal? p (packet-literal ver 4 val))) (let-values (((s bl) (parse-prefix '(1 0 1) 3))) (check-equal? s 5) (check-true (null? bl))) (let-values (((s bl) (parse-prefix '(1 1 1 0 0 1 0) 3))) (check-equal? s 7) (let-values (((s bl) (parse-prefix bl 4))) (check-equal? s 2) (check-true (null? bl)))) (let-values (((b p bl) (parse-packet-literal (read-input example-a1-input)))) (check-true b) (check-equal? p (packet-literal 6 4 2021)) (check-equal? bl '(0 0 0))) (let-values (((b p bl) (parse-packet-operator (read-input example-a2-input)))) (check-true b) (check-equal? (packet-operator-version p) 1) (check-equal? (packet-operator-type p) 6) (let ((subps (packet-operator-subpackets p))) (check-equal? (length subps) 2) (let ((p (car subps))) (check-equal? (packet-literal-version p) 6) (check-equal? (packet-literal-type p) 4) (check-equal? (packet-literal-value p) 10)) (let ((p (cadr subps))) (check-equal? (packet-literal-version p) 2) (check-equal? (packet-literal-type p) 4) (check-equal? (packet-literal-value p) 20))) (check-equal? bl '(0 0 0 0 0 0 0))) (let-values (((b p bl) (parse-packet-operator (read-input example-a3-input)))) (check-true b) (check-equal? (packet-operator-version p) 7) (check-equal? (packet-operator-type p) 3) (let ((subps (packet-operator-subpackets p))) (check-equal? (length subps) 3) (let ((p (car subps))) (check-equal? (packet-literal-version p) 2) (check-equal? (packet-literal-type p) 4) (check-equal? (packet-literal-value p) 1)) (let ((p (cadr subps))) (check-equal? (packet-literal-version p) 4) (check-equal? (packet-literal-type p) 4) (check-equal? (packet-literal-value p) 2)) (let ((p (caddr subps))) (check-equal? (packet-literal-version p) 1) (check-equal? (packet-literal-type p) 4) (check-equal? (packet-literal-value p) 3))) (check-equal? bl '(0 0 0 0 0))) (check-equal? (aoc16a example-a4-input) example-a4-result) (check-equal? (aoc16a example-a5-input) example-a5-result) (check-equal? (aoc16a example-a6-input) example-a6-result) (check-equal? (aoc16a example-a7-input) example-a7-result) (check-equal? (aoc16a) 993) ) $ raco test 16a.rkt raco test: (submod "16a.rkt" test) 41 tests passed $