Done day 19, but not sure why attoparsec isn't working
[advent-of-code-20.git] / advent19 / src / advent19.hs
1 -- import Debug.Trace
2
3 import Text.ParserCombinators.ReadP
4 -- import Text.ParserCombinators.ReadP ((+++))
5 import Data.Char (isDigit, isAlpha)
6
7 import qualified Data.IntMap.Strict as M
8 import Data.IntMap.Strict ((!))
9
10 import Data.Functor (void)
11 import Data.Either
12
13
14 data Rule = Letter Char
15 -- | Then2 Rule Rule
16 -- | Then3 Rule Rule Rule
17 | Then [Rule]
18 | Or Rule Rule
19 | See Int
20 deriving (Show, Eq)
21
22 type RuleSet = M.IntMap Rule
23
24
25 main :: IO ()
26 main =
27 do text <- readFile "data/advent19.txt"
28 -- print text
29 let (rules, messages) = parse inputP text
30 print $ part1 rules messages
31 print $ part2 rules messages
32 -- print $ part2 text
33
34 setup fname =
35 do text <- readFile fname
36 let (rules, messages) = parse inputP text
37 let newRules = parse rulesP "8: 42 | 42 8\n11: 42 31 | 42 11 31"
38 let updatedRules = M.union newRules rules
39 let myParser = makeParser updatedRules (See 0)
40 return (myParser, updatedRules, messages)
41
42
43 part1 = countMatches
44
45 part2 rules messages = countMatches updatedRules messages
46 where newRules = parse rulesP "8: 42 | 42 8\n11: 42 31 | 42 11 31"
47 updatedRules = M.union newRules rules
48
49 countMatches rules messages
50 = length $ filter ((== "") . snd) results
51 where myParser = makeParser rules (See 0)
52 results = concatMap (readP_to_S myParser) messages
53
54
55 parse :: ReadP a -> String -> a
56 parse parser str = fst $ head $ filter ((== "") . snd) $ readP_to_S parser str
57
58
59 -- Generate the rules
60
61 makeParser :: RuleSet -> Rule -> ReadP ()
62 makeParser m (Letter c) = void $ char c
63 makeParser m (Then rs) = mapM_ (makeParser m) rs
64 makeParser m (Or a b) = (makeParser m a) +++ (makeParser m b)
65 makeParser m (See i) = makeParser m (m!i)
66
67
68 -- Parse the input
69
70 rulesP = M.fromList <$> ruleP `sepBy` endOfLine
71 ruleP = (,) <$> decimal <* (string ": ") <*> ruleBodyP
72 ruleBodyP = choice [letterRuleP, orRuleP, thenRuleP, seeRuleP]
73
74 letterRuleP = Letter <$> between (string "\"") (string "\"") get
75 orRuleP = Or <$> thenRuleP <* (string " | ") <*> thenRuleP
76 thenRuleP = Then <$> seeRuleP `sepBy` (string " ")
77 seeRuleP = See <$> decimal
78
79
80 inputP = (,) <$> rulesP <* blankLines <*> messagesP
81
82 messagesP = (munch1 isAlpha) `sepBy` endOfLine
83
84 blankLines = skipMany1 endOfLine
85
86 decimal = read <$> many1 (satisfy isDigit)
87 endOfLine = char '\n'