You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
2.3KB

  1. {-# LANGUAGE FlexibleContexts #-}
  2. import Text.Parsec
  3. data Bag = Bag Label [(Quantity, Label)]
  4. deriving Show
  5. type Label = String
  6. type Quantity = Int
  7. main :: IO ()
  8. main = do
  9. raw <- readFile "day7.txt"
  10. let bags = validate $ map (parse lineP []) $ lines raw
  11. ansA = solveA bags
  12. ansB = solveB bags
  13. in do
  14. putStrLn $ "day7a: " ++ (show ansA)
  15. putStrLn $ "day7b: " ++ (show ansB)
  16. solveA :: [Bag] -> Int
  17. solveA bs = pred
  18. $ length
  19. $ filter (== True)
  20. $ map snd
  21. $ loeb
  22. $ map loebifyA bs
  23. solveB :: [Bag] -> Int
  24. solveB bs = pred
  25. $ unsafeLookup "shiny gold"
  26. $ loeb
  27. $ map loebifyB bs
  28. loeb :: Functor f => f (f a -> a) -> f a
  29. loeb x = go
  30. where
  31. go = fmap ($ go) x
  32. loebifyA :: Bag -> [(Label, Bool)] -> (Label, Bool)
  33. loebifyA (Bag "shiny gold" _) = const ("shiny gold", True)
  34. loebifyA (Bag l bs) =
  35. (\ls -> (l, or [f ls | f <- innerbags]))
  36. where
  37. lookupify = unsafeLookup . snd
  38. innerbags = map lookupify bs
  39. loebifyB :: Bag -> ([(Label, Int)] -> (Label, Int))
  40. loebifyB (Bag l []) = const (l, 1)
  41. loebifyB (Bag l bs) =
  42. (\ls -> (l, sum $ 1 : [q*(f ls) | (f,q) <- innerbags]))
  43. where
  44. lookupify = unsafeLookup . snd
  45. innerbags = zip (map lookupify bs)
  46. (map fst bs)
  47. unsafeLookup :: Eq a => a -> [(a,b)] -> b
  48. unsafeLookup k = unMaybe . (lookup k)
  49. unMaybe :: Maybe a -> a
  50. unMaybe Nothing = error "invalid bag label"
  51. unMaybe (Just v) = v
  52. validate :: [Either ParseError Bag] -> [Bag]
  53. validate [] = []
  54. validate ((Left _):_) = error "invalid input"
  55. validate ((Right b):bs) = b:(validate bs)
  56. lineP :: Parsec String () Bag
  57. lineP = do
  58. l <- initlabelP
  59. ls <- baglistP
  60. eof
  61. return $ Bag l ls
  62. baglistP :: Parsec String () [(Quantity, Label)]
  63. baglistP = do
  64. ((string "no other bags.") >> return [])
  65. <|> (endBy qlabelP
  66. $ oneOf [',', '.'] >> whitespaces)
  67. initlabelP :: Parsec String () String
  68. initlabelP = manyTill anyChar
  69. (try $ whitespaces
  70. >> (string "bags contain")
  71. >> whitespaces)
  72. whitespaces :: Parsec String () String
  73. whitespaces = many $ char ' '
  74. qlabelP :: Parsec String () (Quantity, Label)
  75. qlabelP = do
  76. q <- many1 digit
  77. whitespaces
  78. s <- labelP
  79. return (read q,s)
  80. labelP :: Parsec String () String
  81. labelP = manyTill anyChar
  82. (try $ whitespaces
  83. >> (string "bag")
  84. >> optional (char 's'))