98 lines
2.5 KiB
Haskell
98 lines
2.5 KiB
Haskell
import Control.Applicative
|
|
import Control.Monad
|
|
import Data.Char
|
|
import Data.Functor
|
|
import Data.List
|
|
import Data.List.Extra (stripSuffix)
|
|
import Data.List.Split
|
|
import Data.Maybe
|
|
import Debug.Trace
|
|
import Text.Read
|
|
|
|
data Attr
|
|
= Byr
|
|
| Iyr
|
|
| Eyr
|
|
| Hgt
|
|
| Hcl
|
|
| Ecl
|
|
| Pid
|
|
| Cid
|
|
deriving (Show, Bounded, Enum, Eq, Ord)
|
|
|
|
type Passport = [Attr]
|
|
|
|
intval :: (Int, Int) -> Int -> Maybe Int
|
|
intval (lo, hi) = \n ->
|
|
if n >= lo && n <= hi then Just n
|
|
else Nothing
|
|
|
|
nDigits :: Int -> String -> Maybe String
|
|
nDigits n = \s ->
|
|
if length s == n && all isDigit s then Just s
|
|
else Nothing
|
|
|
|
hexColor :: String -> Maybe String
|
|
hexColor s =
|
|
if length s == 7
|
|
&& s !! 0 == '#'
|
|
&& all (\c -> elem c "0123456789abcdef") (drop 1 s) then Just s
|
|
else Nothing
|
|
|
|
validEcl :: String -> Bool
|
|
validEcl s = case s of
|
|
"amb" -> True
|
|
"blu" -> True
|
|
"brn" -> True
|
|
"gry" -> True
|
|
"grn" -> True
|
|
"hzl" -> True
|
|
"oth" -> True
|
|
_ -> False
|
|
|
|
parseAttr :: String -> Maybe Attr
|
|
parseAttr s =
|
|
let parts = splitOn ":" s in
|
|
let key = parts !! 0 in
|
|
let value = parts !! 1 in
|
|
case key of
|
|
"byr" -> (readMaybe value >>= intval (1920, 2002)) $> Byr
|
|
"iyr" -> (readMaybe value >>= intval (2010, 2020)) $> Iyr
|
|
"eyr" -> (readMaybe value >>= intval (2020, 2030)) $> Eyr
|
|
"hgt" ->
|
|
((stripSuffix "cm" value >>= readMaybe >>= intval (150, 193)) <|>
|
|
(stripSuffix "in" value >>= readMaybe >>= intval(59, 76)))
|
|
$> Hgt
|
|
"hcl" -> hexColor value $> Hcl
|
|
"ecl" -> if validEcl value then Just Ecl else Nothing
|
|
"pid" -> nDigits 9 value $> Pid
|
|
"cid" -> Just Cid
|
|
_ -> Nothing
|
|
|
|
processLine :: String -> Maybe [Attr]
|
|
processLine line = sequence $ (map parseAttr $ splitOn " " line)
|
|
|
|
requiredAttrs :: [Attr]
|
|
requiredAttrs = enumFromTo Byr Pid
|
|
|
|
combineAttrs :: [Attr] -> Maybe Passport
|
|
combineAttrs attrs =
|
|
let intersection = intersect attrs requiredAttrs in
|
|
if sort intersection == sort requiredAttrs then Just attrs
|
|
else Nothing
|
|
|
|
processPassport :: [String] -> Maybe Passport
|
|
processPassport lines = concat <$> (sequence $ map processLine lines) >>= combineAttrs
|
|
|
|
processLines :: [String] -> [Maybe Passport]
|
|
processLines lines = map processPassport $ splitWhen (== "") lines
|
|
|
|
countValid :: [Maybe Passport] -> Int
|
|
countValid = length . filter isJust
|
|
|
|
main :: IO ()
|
|
main = do
|
|
content <- readFile "4.txt"
|
|
let valid = countValid $ processLines $ lines content
|
|
putStrLn (show valid)
|