-
Notifications
You must be signed in to change notification settings - Fork 0
/
04.hs
56 lines (47 loc) · 1.79 KB
/
04.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import Data.Char (isDigit)
import Data.Map (Map, (!), fromList, member)
import Text.Read (readMaybe)
import Runner (runner)
{-|
Solver for Day 4 of the Advent of Code 2020
Problem description: https://adventofcode.com/2020/day/4
-}
main :: IO ()
main = runner (solve False) (solve True)
solve :: Bool -> String -> Int
solve isPart2 =
let
requiredFields = [
("byr", validateInt 4 1920 2002),
("iyr", validateInt 4 2010 2020),
("eyr", validateInt 4 2020 2030),
("hgt", validateHeight),
("hcl", validateHair),
("ecl", (`elem` ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"])),
("pid", validateInt 9 minBound maxBound)]
removeMissing = filter $ \p -> all ((`member` p) . fst) requiredFields
removeInvalid = filter $ \p -> all (\(k, fn) -> fn (p!k)) requiredFields
in length . (if isPart2 then removeInvalid else id) . removeMissing . parse
inRange :: Ord a => a -> a -> a -> Bool
inRange min max v = min <= v && v <= max
validateInt :: Int -> Int -> Int -> String -> Bool
validateInt size min max v =
length v == size && maybe False (inRange min max) (readMaybe v)
validateHeight :: String -> Bool
validateHeight v =
let (digits, unit) = span isDigit v
in case (readMaybe digits, unit) of
(Just n, "cm") -> inRange 150 193 n
(Just n, "in") -> inRange 59 76 n
_ -> False
validateHair :: String -> Bool
validateHair v =
length v == 7 && head v == '#' && all (`elem` "0123456789abcdef") (tail v)
parse :: String -> [Map String String]
parse input = parse' [] (words <$> lines input)
where
toPairs = map ((\(k, ':':v) -> (k, v)) . break (== ':'))
parse' acc ts = case ts of
[] -> [fromList acc]
([]:rest) -> fromList acc : parse' [] rest
(fields:rest) -> parse' (toPairs fields ++ acc) rest