From 8e97f2dfdc35d5249b8be681580b6243aebb80c3 Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Sat, 11 Mar 2023 21:01:24 +0100 Subject: 2020-04 in haskell --- 2020/04-Passport_Processing/second.hs | 153 ++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 2020/04-Passport_Processing/second.hs (limited to '2020/04-Passport_Processing/second.hs') diff --git a/2020/04-Passport_Processing/second.hs b/2020/04-Passport_Processing/second.hs new file mode 100644 index 0000000..e258302 --- /dev/null +++ b/2020/04-Passport_Processing/second.hs @@ -0,0 +1,153 @@ +-- requires cabal install --lib megaparsec parser-combinators +module Main (main) where + +import Control.Applicative.Permutations +import Control.Monad (void, when) +import Data.Maybe (catMaybes) +import Data.Void (Void) +import Text.Megaparsec +import Text.Megaparsec.Char +-- import Text.Megaparsec.Debug +import System.Exit (die) + +exampleExpectedOutput = 2 + +data Passport = Passport { byr :: Int + , iyr :: Int + , eyr :: Int + , hgt :: Int + , hcl :: String + , ecl :: String + , pid :: Int + } deriving (Show) + +type Parser = Parsec Void String + +parseKey :: String -> Parser () +parseKey key = do + void (string key) + void (char ':') + +parseColor :: String -> Parser (Maybe String) +parseColor key = do + value <- try (do + parseKey key + void $ char '#' + v <- some hexDigitChar + spaceBetween + return $ Just v + ) <|> skipString key + case value of + Just v -> if length v == 6 then return value else return Nothing + _ -> return Nothing + +parseEyeColor :: String -> Parser (Maybe String) +parseEyeColor key = do + parseKey key + value <- (Just <$> ( string "amb" + <|> string "blu" + <|> string "brn" + <|> string "gry" + <|> string "grn" + <|> string "hzl" + <|> string "oth" + )) <|> ((some $ alphaNumChar <|> char '#') *> return Nothing) + spaceBetween + return value + +parseHeight :: String -> Parser (Maybe Int) +parseHeight key = do + vu <- try (do + parseKey key + v <- some digitChar + u <- string "cm" <|> string "in" + spaceBetween + return $ Just (v, u) + ) <|> skipString key *> return Nothing + case vu of + Just (value, unit) -> let v = read value + in case unit of + "cm" -> if v >= 150 && v <= 193 then return (Just v) else return Nothing + "in" -> if v >= 59 && v <= 76 then return (Just v) else return Nothing + _ -> return Nothing + _ -> return Nothing + +parseInt :: String -> Int -> Int -> Parser (Maybe Int) +parseInt key low high = do + parseKey key + value <- some digitChar + spaceBetween + let vv = read value + if vv >= low && vv <= high then return $ Just vv else return Nothing + +parsePid :: String -> Parser (Maybe Int) +parsePid key = do + value <- try (do + parseKey key + v <- some digitChar + spaceBetween + return $ Just v + ) <|> skipString key *> return Nothing + case value of + Just v -> if length v == 9 then return (Just $ read v) else return Nothing + _ -> return Nothing + +skipInt :: String -> Parser (Maybe Int) +skipInt key = do + parseKey key + void (some $ alphaNumChar <|> char '#') + spaceBetween + return Nothing + +skipString :: String -> Parser (Maybe String) +skipString key = do + parseKey key + void (some $ alphaNumChar <|> char '#') + spaceBetween + return Nothing + +spaceBetween :: Parser () +spaceBetween = void $ char ' ' <|> char '\n' + +parsePassport :: Parser (Maybe Passport) +parsePassport = do + -- (byr, iyr, eyr, hgt, hcl, ecl, pid, _) <- dbg "passport" . runPermutation $ + (byr, iyr, eyr, hgt, hcl, ecl, pid, _) <- runPermutation $ + (,,,,,,,) <$> toPermutationWithDefault Nothing (parseInt "byr" 1920 2002) + <*> toPermutationWithDefault Nothing (parseInt "iyr" 2010 2020) + <*> toPermutationWithDefault Nothing (parseInt "eyr" 2020 2030) + <*> toPermutationWithDefault Nothing (parseHeight "hgt") + <*> toPermutationWithDefault Nothing (parseColor "hcl") + <*> toPermutationWithDefault Nothing (parseEyeColor "ecl") + <*> toPermutationWithDefault Nothing (parsePid "pid") + <*> toPermutationWithDefault Nothing (skipString "cid") + void (char '\n') + return $ makePassport byr iyr eyr hgt hcl ecl pid + +makePassport :: Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe String -> Maybe String -> Maybe Int -> Maybe Passport +makePassport (Just byr) ( Just iyr) (Just eyr) (Just hgt) (Just hcl) (Just ecl) (Just pid) = Just $ Passport { byr=byr, iyr=iyr, eyr=eyr, hgt=hgt, hcl=hcl, ecl=ecl, pid=pid } +makePassport _ _ _ _ _ _ _ = Nothing + +parsePassports :: Parser [Passport] +parsePassports = do + passports <- some parsePassport + eof + return $ catMaybes passports + +parseInput :: String -> IO [Passport] +parseInput filename = do + input <- readFile filename + case runParser parsePassports filename input of + Left bundle -> die $ errorBundlePretty bundle + Right passports -> return passports + +compute :: [Passport]-> Int +compute passports = length passports + +main :: IO () +main = do + example <- parseInput "example" + let exampleOutput = compute example + when (exampleOutput /= exampleExpectedOutput) (die $ "example failed: got " ++ show exampleOutput ++ " instead of " ++ show exampleExpectedOutput) + input <- parseInput "input" + print $ compute input -- cgit v1.2.3