diff --git a/2020/21.rkt b/2020/21.rkt new file mode 100644 index 0000000..0fe9567 --- /dev/null +++ b/2020/21.rkt @@ -0,0 +1,54 @@ +#lang br +(require racket/file rackunit racket/set) + +(struct rec (ingredients allergens) #:transparent) + +(define (parse-recs str) + (for/list ([r (in-list (string-split str "\n"))]) + (match-define (list head tail) (string-split r "(contains ")) + (rec + (apply set (string-split head)) + (apply set (string-split (string-trim tail ")") ", "))))) + +(define recs (parse-recs (file->string "21.rktd"))) + +(define all-allergens (apply set-union (map rec-allergens recs))) + +(define filtered-recs + (for/list ([allergen (in-set all-allergens)]) + (define recs-with-allergen + (filter (λ (r) (set-member? (rec-allergens r) allergen)) recs)) + (rec (apply set-intersect (map rec-ingredients recs-with-allergen)) + (set allergen)))) + +(define allergenic-records + (let loop ([acc null][filtered-recs filtered-recs]) + (cond + [(null? filtered-recs) acc] + [else + (define-values (singletons others) + (partition (λ (r) (= 1 (set-count (rec-ingredients r)))) filtered-recs)) + (loop (append singletons acc) + (for/list ([other (in-list others)]) + (rec (apply set-subtract (rec-ingredients other) + (map rec-ingredients singletons)) + (rec-allergens other))))]))) + +(define allergenic-ingredients + (apply set-union (map rec-ingredients allergenic-records))) + +(define (hypoallergenic-ingredient-count recs) + (for/sum ([r (in-list recs)]) + (set-count (set-subtract (rec-ingredients r) allergenic-ingredients)))) + +(check-equal? (hypoallergenic-ingredient-count recs) 1958) + +(define ingredient-list + (let* ([pairs (for/list ([r (in-list allergenic-records)]) + (car (map cons (set->list (rec-ingredients r)) + (set->list (rec-allergens r)))))] + [pairs (sort pairs #:key cdr string