1 {-# LANGUAGE OverloadedStrings #-} -- for building Text strings
3 import Control.Applicative (Alternative((<|>)))
4 import Control.Monad (void, when)
5 import Data.Text.Lazy (Text)
6 import Data.Void (Void)
8 import qualified Data.Text.Lazy.Encoding as Text
9 import qualified Text.Megaparsec as P
10 import qualified Text.Megaparsec.Char as P
11 import qualified Text.Megaparsec.Char.Lexer as P
12 -- import qualified Text.Megaparsec.Debug as P
13 import qualified Data.ByteString.Lazy as BSL
15 import Data.Map.Strict (Map)
16 import qualified Data.Map.Strict as Map
18 -- | See https://adventofcode.com/2020/day/7 for the problem statements
19 data Day07Results = Day07Results
29 putStr "Day07Inputs: " >> getDataFileName "" >>= putStrLn
30 print =<< Day07Results
31 <$> (countTransitiveParentBags "shiny gold" <$> parse "example1")
32 <*> (countTransitiveParentBags "shiny gold" <$> parse "batch")
33 <*> (countChildrenBags "shiny gold" <$> parse "example1")
34 <*> (countChildrenBags "shiny gold" <$> parse "example2")
35 <*> (countChildrenBags "shiny gold" <$> parse "batch")
37 type Parser output = P.Parsec {-error-}Void {-input-}Text output
40 type Rule = (Bag, Map Bag Int)
41 type Rules = Map Bag (Map Bag Int)
43 parserBag :: Parser Bag
45 (fst <$>) $ P.match $ do
46 void $ P.takeWhileP (Just "adj") (/=' ')
48 void $ P.takeWhileP (Just "col") (/=' ')
50 parserRule :: Parser Rule
51 parserRule = {- P.dbg "parserRule" $ -} do
53 void $ P.string " bags contain "
55 [ [] <$ P.string "no other bags"
56 , (`P.sepBy` P.string ", ") $ do
60 void $ P.string " bag"
62 void $ P.optional (P.char 's')
66 return (outBag, Map.fromList inBags)
68 parserRules :: Parser Rules
70 -- P.dbg "parserRules" $
72 P.many $ parserRule <* (() <$ P.char '\n' <|> P.eof)
74 parse :: FilePath -> IO Rules
76 content <- Text.decodeUtf8 <$> (BSL.readFile =<< getDataFileName input)
77 case P.parse (parserRules <* P.eof) input content of
78 Left err -> error (P.errorBundlePretty err)
79 Right rules -> return rules
81 type ReverseRules = Map Bag (Map Bag ())
83 reverseRules :: Rules -> ReverseRules
85 Map.foldlWithKey (\acc outBag inBags ->
86 Map.unionWith (<>) acc (Map.singleton outBag () <$ inBags)
89 parentBags :: Bag -> ReverseRules -> Map Bag ()
90 parentBags = Map.findWithDefault Map.empty
92 transitiveParentBags :: ReverseRules -> Bag -> Map Bag ()
93 transitiveParentBags revRules bag =
94 parentBags bag revRules <>
95 Map.unions (transitiveParentBags revRules <$> Map.keys (parentBags bag revRules))
97 countTransitiveParentBags :: Bag -> Rules -> Int
98 countTransitiveParentBags bag rules = Map.size $ transitiveParentBags (reverseRules rules) bag
100 countChildrenBags :: Bag -> Rules -> Int
101 countChildrenBags bag rules =
102 sum $ Map.mapWithKey (\childBag childBagCount ->
103 childBagCount + childBagCount * countChildrenBags childBag rules
105 where childrenBags = Map.findWithDefault Map.empty bag rules