aoc2020/4.hs

68 lines
2.2 KiB
Haskell
Raw Permalink Normal View History

2020-12-04 12:27:13 +00:00
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 Text.Read
2020-12-05 00:57:18 +00:00
import Data.Set (Set, fromList, isSubsetOf)
2020-12-04 12:27:13 +00:00
2020-12-05 00:57:18 +00:00
data Attr = Byr | Iyr | Eyr | Hgt | Hcl | Ecl | Pid | Cid
2020-12-04 12:27:13 +00:00
deriving (Show, Bounded, Enum, Eq, Ord)
2020-12-05 00:57:18 +00:00
intval :: Int -> Int -> Int -> Maybe Int
2020-12-05 01:01:41 +00:00
intval lo hi n = guard (lo <= n && n <= hi) $> n
2020-12-04 12:27:13 +00:00
nDigits :: Int -> String -> Maybe String
2020-12-05 00:57:18 +00:00
nDigits n s = guard (length s == n && all isDigit s) $> s
2020-12-04 12:27:13 +00:00
hexColor :: String -> Maybe String
2020-12-05 01:04:32 +00:00
hexColor s = guard (length s == 7 && s !! 0 == '#' && all (flip elem "0123456789abcdef") (drop 1 s)) $> s
2020-12-04 12:27:13 +00:00
validEcl :: String -> Bool
2020-12-05 00:57:18 +00:00
validEcl = flip elem ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]
2020-12-04 12:27:13 +00:00
parseAttr :: String -> Maybe Attr
parseAttr s =
2020-12-05 01:01:41 +00:00
case splitOn ":" s of
[key, value] -> 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" ->
let inch = stripSuffix "cm" value >>= readMaybe >>= intval 150 193 in
let cm = stripSuffix "in" value >>= readMaybe >>= intval 59 76 in
(inch <|> cm) $> Hgt
"hcl" -> hexColor value $> Hcl
"ecl" -> if validEcl value then Just Ecl else Nothing
"pid" -> nDigits 9 value $> Pid
"cid" -> Just Cid
_ -> Nothing
2020-12-04 12:27:13 +00:00
_ -> Nothing
processLine :: String -> Maybe [Attr]
2020-12-05 00:57:18 +00:00
processLine = mapM parseAttr . splitOn " "
2020-12-04 12:27:13 +00:00
2020-12-05 00:57:18 +00:00
requiredAttrs :: Set Attr
requiredAttrs = fromList $ enumFromTo Byr Pid
2020-12-04 12:27:13 +00:00
2020-12-05 00:57:18 +00:00
combineAttrs :: [Attr] -> Maybe [Attr]
combineAttrs attrs = guard (isSubsetOf requiredAttrs $ fromList $ attrs) $> attrs
2020-12-04 12:27:13 +00:00
2020-12-05 00:57:18 +00:00
processPassport :: [String] -> Maybe [Attr]
processPassport = combineAttrs . concat <=< mapM processLine
2020-12-04 12:27:13 +00:00
2020-12-05 00:57:18 +00:00
processLines :: [String] -> [Maybe [Attr]]
processLines = map processPassport . splitWhen (== "")
2020-12-04 12:27:13 +00:00
2020-12-05 00:57:18 +00:00
countValid :: [Maybe [Attr]] -> Int
2020-12-04 12:27:13 +00:00
countValid = length . filter isJust
main :: IO ()
main = do
content <- readFile "4.txt"
let valid = countValid $ processLines $ lines content
2020-12-05 00:57:18 +00:00
print valid