[hs] Solve 2020_04

This commit is contained in:
Joscha 2020-12-04 13:09:49 +00:00
parent c48319423d
commit e2f1687798
3 changed files with 183 additions and 2 deletions

View file

@ -4,10 +4,13 @@ module Aoc.Parse
, module Text.Megaparsec.Char.Lexer
, Parser
, manyLines
, oneSpace
, untilSpace
, untilEol
, lineChar
) where
import Data.Char
import Data.Void
import qualified Data.Text as T
@ -22,8 +25,14 @@ type Parser = Parsec Void T.Text
manyLines :: Parser a -> Parser [a]
manyLines p = endBy (try p) newline
oneSpace :: Parser Char
oneSpace = label "whitespace character" $ satisfy isSpace
untilSpace :: Parser T.Text
untilSpace = takeWhileP (Just "non-whitespace character") (not . isSpace)
untilEol :: Parser T.Text
untilEol = takeWhileP Nothing (/= '\n')
untilEol = takeWhileP (Just "non-newline character") (/= '\n')
lineChar :: Parser Char
lineChar = satisfy (/= '\n')
lineChar = label "non-newline character" $ satisfy (/= '\n')

View file

@ -6,10 +6,12 @@ import Aoc.Day
import qualified Aoc.Y2020.D01 as D01
import qualified Aoc.Y2020.D02 as D02
import qualified Aoc.Y2020.D03 as D03
import qualified Aoc.Y2020.D04 as D04
days :: [Day]
days =
[ D01.day
, D02.day
, D03.day
, D04.day
]

170
hs/src/Aoc/Y2020/D04.hs Normal file
View file

@ -0,0 +1,170 @@
{-# LANGUAGE OverloadedStrings #-}
module Aoc.Y2020.D04
( day
) where
import Control.Monad
import Data.Char
import qualified Data.Text as T
import Data.Void
import Aoc.Day
import Aoc.Parse
data Field a = None | Invalid T.Text | Valid a
deriving (Show)
isPresent :: Field a -> Bool
isPresent None = False
isPresent (Invalid _) = True
isPresent (Valid _) = True
isValid :: Field a -> Bool
isValid None = False
isValid (Invalid _) = False
isValid (Valid _) = True
instance Semigroup (Field a) where
_ <> Valid a = Valid a
Valid a <> _ = Valid a
_ <> Invalid t = Invalid t
Invalid t <> _ = Invalid t
None <> None = None
instance Monoid (Field a) where
mempty = None
data Height = Cm Int | In Int
deriving (Show)
data Passport = Passport
{ byr :: Field Int
, iyr :: Field Int
, eyr :: Field Int
, hgt :: Field Height
, hcl :: Field T.Text
, ecl :: Field T.Text
, pid :: Field Int
, cid :: Field Void
} deriving (Show)
instance Semigroup Passport where
p1 <> p2 = Passport
{ byr = byr p1 <> byr p2
, iyr = iyr p1 <> iyr p2
, eyr = eyr p1 <> eyr p2
, hgt = hgt p1 <> hgt p2
, hcl = hcl p1 <> hcl p2
, ecl = ecl p1 <> ecl p2
, pid = pid p1 <> pid p2
, cid = cid p1 <> cid p2
}
instance Monoid Passport where
mempty = Passport mempty mempty mempty mempty mempty mempty mempty mempty
pField :: T.Text -> Parser a -> Parser (Field a)
pField name p = do
notFollowedBy oneSpace
void $ string name
void $ char ':'
(Valid <$> try p) <|> (Invalid <$> untilSpace)
nDigits :: Int -> Parser Int
nDigits n = do
digits <- takeWhileP (Just "digit") isDigit
guard $ T.length digits == n
pure $ read $ T.unpack digits
nDigitsBetween :: Int -> Int -> Int -> Parser Int
nDigitsBetween n low high = do
i <- nDigits n
guard $ low <= i && i <= high
pure i
pByr :: Parser Passport
pByr = do
f <- pField "byr" $ nDigitsBetween 4 1920 2002
pure mempty{byr = f}
pIyr :: Parser Passport
pIyr = do
f <- pField "iyr" $ nDigitsBetween 4 2010 2020
pure mempty{iyr = f}
pEyr :: Parser Passport
pEyr = do
f <- pField "eyr" $ nDigitsBetween 4 2020 2030
pure mempty{eyr = f}
pHgt :: Parser Passport
pHgt = do
f <- pField "hgt" $ do
i <- decimal
(string "cm" >> guard (150 <= i && i <= 193) >> pure (Cm i)) <|> (string "in" >> guard (59 <= i && i <= 76) >> pure (In i))
pure mempty{hgt = f}
pHcl :: Parser Passport
pHcl = do
f <- pField "hcl" $ do
void $ char '#'
t <- untilSpace
guard $ T.length t == 6 && T.all isHexDigit t
pure t
pure mempty{hcl = f}
pEcl :: Parser Passport
pEcl = do
f <- pField "ecl" $ foldr1 (<|>) ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]
pure mempty{ecl = f}
pPid :: Parser Passport
pPid = do
f <- pField "pid" $ nDigits 9
pure mempty{pid = f}
pCid :: Parser Passport
pCid = do
f <- pField "cid" $ fail "void"
pure mempty{cid = f}
parser :: Parser [Passport]
parser = passport `sepBy` newline
where
passport = mconcat <$> field `endBy1` oneSpace
field = pByr <|> pIyr <|> pEyr <|> pHgt <|> pHcl <|> pEcl <|> pPid <|> pCid
hasRequiredKeys :: Passport -> Bool
hasRequiredKeys p = and
[ isPresent $ byr p
, isPresent $ iyr p
, isPresent $ eyr p
, isPresent $ hgt p
, isPresent $ hcl p
, isPresent $ ecl p
, isPresent $ pid p
]
hasValidKeys :: Passport -> Bool
hasValidKeys p = and
[ isValid $ byr p
, isValid $ iyr p
, isValid $ eyr p
, isValid $ hgt p
, isValid $ hcl p
, isValid $ ecl p
, isValid $ pid p
]
solver :: [Passport] -> IO ()
solver passports = do
putStrLn ">> Part 1"
print $ length $ filter hasRequiredKeys passports
putStrLn ""
putStrLn ">> Part 2"
print $ length $ filter hasValidKeys passports
day :: Day
day = dayParse "2020_04" parser solver