Advent of Code 2020 solutions in Racket, I guess
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.

92 lines
3.1 KiB

#lang racket
(require "lib/common.rkt"
(for-syntax racket/syntax)
syntax/parse/define)
(define-simple-macro (struct+ name:id (fld:id ...+))
#:with hash-name (format-id #'name "~a-insert-table" #'name)
(begin (struct name (fld ...) #:transparent)
(define hash-name
(make-immutable-hash
(list (cons 'fld
(lambda (strct val)
(struct-copy name strct
[fld val])))
...)))))
(define-simple-macro (struct-insert name:expr instance:expr fld:expr val:expr)
#:with hash-name (format-id #'name "~a-insert-table" #'name)
((hash-ref hash-name fld) instance val))
(struct+ passport (byr iyr eyr hgt hcl ecl pid cid))
(define (parse-passport str)
(for/fold ([pp (passport #f #f #f #f #f #f #f #f)])
([elem (in-list (string-split str))])
(match-define (list name val) (string-split elem ":"))
(struct-insert passport pp (string->symbol name) val)))
(define valid-passport?/a
(struct/c passport
string? string? string? string?
string? string? string? any/c))
(define (regex/c rx) (curry regexp-match? rx))
(define birth-year/c (regex/c #px"^(19[2-9][0-9]|200[0-2])$"))
(define issue-year/c (regex/c #px"^(201[0-9]|2020)$"))
(define expiration-year/c (regex/c #px"^(202[0-9]|2030)$"))
(define (height/c str)
(match str
[(pregexp #px"^([0-9]+)(cm|in)$" (list _ hgt unt))
(match unt
["cm" (<= 150 (string->number hgt) 193)]
["in" (<= 59 (string->number hgt) 76)])]
[_ #f]))
(define hair-color/c (regex/c #px"^#[0-9a-f]{6}$"))
(define eye-color/c (regex/c #px"^(amb|blu|brn|gry|grn|hzl|oth)$"))
(define passport-id/c (regex/c #px"^\\d{9}$"))
(define valid-passport?/b
(and/c valid-passport?/a
(struct/c passport
birth-year/c
issue-year/c
expiration-year/c
height/c
hair-color/c
eye-color/c
passport-id/c
any/c)))
(define (solution valid? in)
(count valid? (map parse-passport in)))
(module+ main
(call-with-input-file "data/day4.txt"
(lambda (prt)
(define input (string-split (port->string prt) "\n\n"))
(answer 4 1 (solution valid-passport?/a input))
(answer 4 2 (solution valid-passport?/b input)))))
(module+ test
(require rackunit)
(define example1
(passport "1937" "2017" "2020" "183cm" "#fffffd" "gry" "860033327" "147"))
(check-equal? (parse-passport "ecl:gry pid:860033327 eyr:2020 hcl:#fffffd\n
byr:1937 iyr:2017 cid:147 hgt:183cm")
example1
"example 1, parse")
(check-pred valid-passport?/a example1
"example 1, valid")
(call-with-input-file "data/day4.txt"
(lambda (prt)
(define input (string-split (port->string prt) "\n\n"))
(check-equal? (solution valid-passport?/a input) 190
"final answer part 1")
(check-equal? (solution valid-passport?/b input) 121
"final answer part 2"))))