newtype Predicate a = Predicate { getPredicate :: a -> Bool }
Predicate a <> Predicate b = Predicate (\x -> a x || b x)
mempty = Predicate (const False)
mconcat <$> sepBy (M.singleton <$>
munch (/= ':') <*>
(char ':' *> munch isSpace *> (getPredicate . foldMap Predicate <$> sepBy
((\a b t -> t >= a && t <= b) <$> positiveIntegerDecimal <*>
(char '-' *> positiveIntegerDecimal)
)
(munch isSpace *> string "or" *> munch isSpace)
))
) (char '\n')
S.fromList <$> sepBy positiveIntegerDecimal (char ',')
data Input = Input {
Input <$>
validity <*>
(munch isSpace *> string "your ticket:" *> munch isSpace *> ticket) <*>
(munch isSpace *> string "nearby tickets:" *> munch isSpace *> sepBy ticket (char '\n') <* (() <$ char '\n' <|> eof))
let
t = not . getPredicate (foldMap Predicate $ ranges s)
in sum $ Compose $ map (S.filter t) $ nearbyTickets s
let
t = getPredicate (foldMap Predicate $ ranges s)
in s {
nearbyTickets = filter ((&&) <$> (not . S.null) <*> all t) $ nearbyTickets s
}
let
unknown = M.keys (ranges s) <$ myTicket s
reduced = foldl' (S.zipWith $ \c v ->
filter (\c' -> case M.lookup c' (ranges s) of
Nothing -> False
Just f -> f v
) c
) unknown $ nearbyTickets s
let
known = do
[c] <- foldMap (:[]) s
return c
in if
| known /= nub known -> []
| length known == S.length s -> [s]
| otherwise -> let
fmap (\c -> case c of
[_] -> c
_ -> filter (not . flip elem known) c
) s
in if s1 == s
then let
narrowest = foldr (\c r n -> case c of
[_] -> r (n + 1)
_ -> case r (n + 1) of
Just (n', c')
| length c' < length c -> Just (n', c')
_ -> Just (n, c)
) (const Nothing) s1 0
in case narrowest of
Nothing -> solve s1
Just (i, c) -> c >>= \c' -> solve $ S.update i [c'] s1
else solve s1
in map (fold . S.mapWithIndex (\i [c] -> M.singleton c i)) $ solve reduced
M.fromList $ do
(k,i) <- M.toList cols
if "departure" `isPrefixOf` k
then [()]
else []
Just v <- [S.lookup i tk]
return (k,v)
main = do
[fn] <- getArgs
parseResult <- parseFile (utf8_stream >># trackPosition >># input) fn
case parseResult of
Right (s:_) -> do
putStrLn $ "Part 1: " ++ show (errorRate s)
let
s1 = onlyValid s
cols = head $ columns s1
putStrLn $ "Part 2: " ++ show (product $ departureFields cols $ myTicket s1)
Left e -> print e