59 lines
1.9 KiB
Racket
59 lines
1.9 KiB
Racket
#lang racket
|
|
|
|
(module+ test
|
|
(require rackunit))
|
|
|
|
(define (read-input filename)
|
|
(map parse-food (file->lines filename)))
|
|
|
|
(define (parse-food str)
|
|
(define match (regexp-match #px"(.+)\\(contains (.+)\\)" str))
|
|
(list (string-split (cadr match))
|
|
(string-split (caddr match) ", ")))
|
|
|
|
(define (find-allergens foods)
|
|
(define h (make-hash))
|
|
(for* ([food (in-list foods)]
|
|
[allergen (in-list (cadr food))])
|
|
(define s (hash-ref h allergen (list->set (car food))))
|
|
(hash-set! h allergen (set-intersect s (list->set (car food)))))
|
|
(unique-allergens h))
|
|
|
|
(define (unique-allergens h)
|
|
(if (for/and ([(k v) (in-hash h)]) (= 1 (set-count v)))
|
|
(for/hash ([(k v) (in-hash h)]) (values k (set-first v)))
|
|
(begin
|
|
(for ([(k v) (in-hash h)]
|
|
#:when (= 1 (set-count v))
|
|
[(other-k other-v) (in-hash h)]
|
|
#:unless (equal? other-k k))
|
|
(hash-set! h other-k (set-remove other-v (set-first v))))
|
|
(unique-allergens h))))
|
|
|
|
(define (part1 filename)
|
|
(define foods (read-input filename))
|
|
(define allergens (find-allergens foods))
|
|
(define all-ingredients (apply append (map car foods)))
|
|
(define allergen-ingredients (hash-values allergens))
|
|
(length (for/list ([ingredient (in-list all-ingredients)]
|
|
#:unless (set-member? allergen-ingredients ingredient))
|
|
ingredient)))
|
|
|
|
(module+ test
|
|
(check-equal? (part1 "test") 5))
|
|
|
|
(module+ main
|
|
(displayln (part1 "input")))
|
|
|
|
(define (part2 filename)
|
|
(define foods (read-input filename))
|
|
(define allergens (find-allergens foods))
|
|
(define allergen-ingredients (for/list ([allergen (sort (hash-keys allergens) string<=?)])
|
|
(hash-ref allergens allergen)))
|
|
(string-join allergen-ingredients ","))
|
|
|
|
(module+ test
|
|
(check-equal? (part2 "test") "mxmxvkd,sqjhc,fvjkl"))
|
|
|
|
(module+ main
|
|
(displayln (part2 "input")))
|