[hs] Solve 2020_04
This commit is contained in:
parent
c48319423d
commit
e2f1687798
3 changed files with 183 additions and 2 deletions
|
|
@ -4,10 +4,13 @@ module Aoc.Parse
|
||||||
, module Text.Megaparsec.Char.Lexer
|
, module Text.Megaparsec.Char.Lexer
|
||||||
, Parser
|
, Parser
|
||||||
, manyLines
|
, manyLines
|
||||||
|
, oneSpace
|
||||||
|
, untilSpace
|
||||||
, untilEol
|
, untilEol
|
||||||
, lineChar
|
, lineChar
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Data.Char
|
||||||
import Data.Void
|
import Data.Void
|
||||||
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
|
@ -22,8 +25,14 @@ type Parser = Parsec Void T.Text
|
||||||
manyLines :: Parser a -> Parser [a]
|
manyLines :: Parser a -> Parser [a]
|
||||||
manyLines p = endBy (try p) newline
|
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 :: Parser T.Text
|
||||||
untilEol = takeWhileP Nothing (/= '\n')
|
untilEol = takeWhileP (Just "non-newline character") (/= '\n')
|
||||||
|
|
||||||
lineChar :: Parser Char
|
lineChar :: Parser Char
|
||||||
lineChar = satisfy (/= '\n')
|
lineChar = label "non-newline character" $ satisfy (/= '\n')
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,12 @@ import Aoc.Day
|
||||||
import qualified Aoc.Y2020.D01 as D01
|
import qualified Aoc.Y2020.D01 as D01
|
||||||
import qualified Aoc.Y2020.D02 as D02
|
import qualified Aoc.Y2020.D02 as D02
|
||||||
import qualified Aoc.Y2020.D03 as D03
|
import qualified Aoc.Y2020.D03 as D03
|
||||||
|
import qualified Aoc.Y2020.D04 as D04
|
||||||
|
|
||||||
days :: [Day]
|
days :: [Day]
|
||||||
days =
|
days =
|
||||||
[ D01.day
|
[ D01.day
|
||||||
, D02.day
|
, D02.day
|
||||||
, D03.day
|
, D03.day
|
||||||
|
, D04.day
|
||||||
]
|
]
|
||||||
|
|
|
||||||
170
hs/src/Aoc/Y2020/D04.hs
Normal file
170
hs/src/Aoc/Y2020/D04.hs
Normal 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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue