DEV Community

loading...

Discussion on: Advent of Code 2020 Solution Megathread - Day 4: Passport Processing

Collapse
bgaster profile image
Benedict Gaster

Here is a Haskell soloution for Day 4:

-- check if a given "passport" field is a valid passport field
isValidField :: (T.Text, T.Text) -> Bool
isValidField (name,v) | name == "byr" = fourAux 1920 2002 
                      | name == "iyr" = fourAux 2010 2020
                      | name == "eyr" = fourAux 2020 2030 

                      | name == "ecl" = v `elem` ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]

                      | name == "hcl" = let h = T.head v == '#'
                                            ts = T.tail v
                                        in h && T.length ts == 6 && elementOf "0123456789abcdef" ts 
                      | name == "pid" = either (const False) (\_ -> T.length v == 9) (decimal v)
                      | name == "hgt" = let (n,t) = (fst $ fromRight (0,"") (decimal (T.takeWhile (not . isAlpha) v)), 
                                                                                      T.dropWhile (not . isAlpha) v)
                                        in (t == "cm" && n >= 150 && n <= 193) || (t == "in" && n >= 59 && n <= 76)
                      | otherwise     = name == "cid"
    where 
        fourAux min max = let n = fst $ fromRight (0,"") (decimal v) 
                          in n >= min && n <= max && T.length v == 4

        elementOf :: String -> T.Text -> Bool
        elementOf xs = T.all (`elem` xs)

-- check if a input field is a actually within the set of valid passport fields
isValidFieldName xs = all (`elem` xs) ["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"]

isValidPassport :: [(T.Text, T.Text)] -> Bool
isValidPassport xs = all isValidField xs && (isValidFieldName $ map fst xs)

-- generate a list of passports, given a list of input lines
passports :: [T.Text] -> [[(T.Text, T.Text)]]
passports = docs [] []
    where
        docs doc ds [] = doc:ds
        docs doc ds ("":xs) = docs [] (doc:ds) xs    
        docs doc ds (x:xs) = docs (mkKey x ++ doc) ds xs

        mkKey = map (\xs -> (T.takeWhile  (/=':') xs, 
                             T.tail (T.dropWhile  (/=':') xs))) . splitOn " "

main = do xs <- IOT.readFile "day4_input" <&> T.lines 
          print (length $ filter isValidFieldName (map (map fst) $ passports xs)) 
          print (length (filter isValidPassport (passports xs)))
Enter fullscreen mode Exit fullscreen mode