Files
aoc/haskellAoC/src/Y2020/Day04.hs
2020-12-04 18:58:45 +01:00

58 lines
2.4 KiB
Haskell

module Y2020.Day04 (y20day04) where
import Data.Char
import Data.Maybe
import Data.List.Split
import qualified Data.Map as M
data RawPassport = RawPassport {
birth_year :: String
, issue_year :: String
, expir_year :: String
, height :: String
, hair_color :: String
, eye_color :: String
, passport_id :: String
} deriving (Show)
parsePassport :: String -> Maybe RawPassport
parsePassport input = do
byr <- M.lookup "byr" mapped
iyr <- M.lookup "iyr" mapped
eyr <- M.lookup "eyr" mapped
hgt <- M.lookup "hgt" mapped
hcl <- M.lookup "hcl" mapped
ecl <- M.lookup "ecl" mapped
pid <- M.lookup "pid" mapped
return (RawPassport byr iyr eyr hgt hcl ecl pid)
where mapped = M.fromList $ map (\a -> (a !! 0, a !! 1)) $ map (splitOn ":") $ splitOn " " input
allTrue :: [Bool] -> Bool
allTrue = all (== True)
checkDigitInRange :: Int -> Int -> String -> Bool
checkDigitInRange mini maxi input = allTrue $ map ($ input) [(all (isDigit)), ((>= mini) . read), ((<= maxi) . read)]
validatePassport :: RawPassport -> Bool
validatePassport pass = allTrue [validatePassID, validateBirth, validateIssued, validateExpiration, validateHeight, validateHairColor, validateEyeColor]
where validateBirth = checkDigitInRange 1920 2002 $ birth_year pass
validateIssued = checkDigitInRange 2010 2020 $ issue_year pass
validateExpiration = checkDigitInRange 2020 2030 $ expir_year pass
validateHeight = not unitAvailable && (correctHeightCm || correctHeightIn)
where correctHeightCm = checkDigitInRange 150 193 $ (splitOn "cm" (height pass)) !! 0
correctHeightIn = checkDigitInRange 59 76 $ (splitOn "in" (height pass)) !! 0
unitAvailable = all (isDigit) (height pass)
validateHairColor = allTrue $ map ($ hair_color pass) [((== '#') . head), (all (`elem` "0123456789abcdef") . tail), ((== 7) . length)]
validateEyeColor = (eye_color pass) `elem` ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]
validatePassID = allTrue $ map ($ passport_id pass) [((== 9) . length), (all (isDigit))]
y20day04 :: [String] -> (String, String)
y20day04 input = (part1, part2)
where part1 = show $ length $ passports
part2 = show $ length $ validPassports
passports = concatMap maybeToList $ map parsePassport $ map replaceNL $ splitOn "\n\n" $ unlines input
replaceNL = map repl
repl '\n' = ' '
repl c = c
validPassports = filter validatePassport passports