/
day21.hs
63 lines (48 loc) · 1.83 KB
/
day21.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
{-# LANGUAGE ParallelListComp #-}
{-# LANGUAGE TupleSections #-}
import AOC.Helpers (unique)
import Control.Category ((>>>))
import Data.List (intercalate, intersect, sortOn)
import Data.List.Extra (groupOn)
import Text.Parsec (char, choice, many, parse, sepBy1, space, string)
main :: IO ()
main = interact $ mkInput >>> ([solve1, solve2] <*>) . pure >>> unlines
-- part 1
solve1 :: Input -> String
solve1 = show . length . nonAllergenIngredients
nonAllergenIngredients :: Input -> [Ingredient]
nonAllergenIngredients ip =
[i | i <- allIngredients ip, i `notElem` allAllergenIngredients ip]
allAllergenIngredients :: Input -> [Ingredient]
allAllergenIngredients = unique . concatMap snd . allergenIngredients
allergenIngredients :: Input -> [(Allergen, [Ingredient])]
allergenIngredients =
map (\xs -> (fst (head xs), foldl1 intersect . map snd $ xs)) .
groupOn fst . sortOn fst . concatMap (\(i, a) -> map (, i) a)
-- part 2
dangerousIngredientList :: Input -> [Ingredient]
dangerousIngredientList =
head . filter isValid . mapM snd . sortOn fst . allergenIngredients
where
isValid xs = length (unique xs) == length xs
solve2 :: Input -> String
solve2 = intercalate "," . dangerousIngredientList
-- helpers
allIngredients :: Input -> [Ingredient]
allIngredients = concatMap fst
-- Input
type Ingredient = String
type Allergen = String
type Dish = ([Ingredient], [Allergen])
type Input = [Dish]
mkDish :: String -> Dish
mkDish = either undefined id . parse parseDish ""
where
parseDish = (,) <$> fil parseIngredients <*> fil parseAllergens
parseIngredients = parseWord `sepBy1` space
parseAllergens =
string "(contains " *> (parseWord `sepBy1` string ", ") <* char ')'
parseWord = many $ choice [char c | c <- ['a' .. 'z']]
fil = fmap (filter (not . null))
mkInput :: String -> Input
mkInput = map mkDish . lines