|
- {-# LANGUAGE FlexibleContexts #-}
-
- import Text.Parsec
-
- data Bag = Bag Label [(Quantity, Label)]
- deriving Show
- type Label = String
- type Quantity = Int
-
- main :: IO ()
- main = do
- raw <- readFile "day7.txt"
- let bags = validate $ map (parse lineP []) $ lines raw
- ansA = solveA bags
- ansB = solveB bags
- in do
- putStrLn $ "day7a: " ++ (show ansA)
- putStrLn $ "day7b: " ++ (show ansB)
-
- solveA :: [Bag] -> Int
- solveA bs = pred
- $ length
- $ filter (== True)
- $ map snd
- $ loeb
- $ map loebifyA bs
-
- solveB :: [Bag] -> Int
- solveB bs = pred
- $ unsafeLookup "shiny gold"
- $ loeb
- $ map loebifyB bs
-
- loeb :: Functor f => f (f a -> a) -> f a
- loeb x = go
- where
- go = fmap ($ go) x
-
- loebifyA :: Bag -> [(Label, Bool)] -> (Label, Bool)
- loebifyA (Bag "shiny gold" _) = const ("shiny gold", True)
- loebifyA (Bag l bs) =
- (\ls -> (l, or [f ls | f <- innerbags]))
- where
- lookupify = unsafeLookup . snd
- innerbags = map lookupify bs
-
- loebifyB :: Bag -> ([(Label, Int)] -> (Label, Int))
- loebifyB (Bag l []) = const (l, 1)
- loebifyB (Bag l bs) =
- (\ls -> (l, sum $ 1 : [q*(f ls) | (f,q) <- innerbags]))
- where
- lookupify = unsafeLookup . snd
- innerbags = zip (map lookupify bs)
- (map fst bs)
-
- unsafeLookup :: Eq a => a -> [(a,b)] -> b
- unsafeLookup k = unMaybe . (lookup k)
-
- unMaybe :: Maybe a -> a
- unMaybe Nothing = error "invalid bag label"
- unMaybe (Just v) = v
-
- validate :: [Either ParseError Bag] -> [Bag]
- validate [] = []
- validate ((Left _):_) = error "invalid input"
- validate ((Right b):bs) = b:(validate bs)
-
- lineP :: Parsec String () Bag
- lineP = do
- l <- initlabelP
- ls <- baglistP
- eof
- return $ Bag l ls
-
- baglistP :: Parsec String () [(Quantity, Label)]
- baglistP = do
- ((string "no other bags.") >> return [])
- <|> (endBy qlabelP
- $ oneOf [',', '.'] >> whitespaces)
-
- initlabelP :: Parsec String () String
- initlabelP = manyTill anyChar
- (try $ whitespaces
- >> (string "bags contain")
- >> whitespaces)
-
- whitespaces :: Parsec String () String
- whitespaces = many $ char ' '
-
- qlabelP :: Parsec String () (Quantity, Label)
- qlabelP = do
- q <- many1 digit
- whitespaces
- s <- labelP
- return (read q,s)
-
- labelP :: Parsec String () String
- labelP = manyTill anyChar
- (try $ whitespaces
- >> (string "bag")
- >> optional (char 's'))
|