{-# Language BlockArguments, ScopedTypeVariables, QuasiQuotes, TemplateHaskell, ViewPatterns #-}
{-|
Module      : Main
Description : Day 4 solution
Copyright   : (c) Eric Mertens, 2020
License     : ISC
Maintainer  : emertens@gmail.com

<https://adventofcode.com/2020/day/4>

Passport validation

-}
module Main (main) where

import Advent (countBy, stageTH)
import Advent.Format (format)
import Data.Char (isDigit, isHexDigit)
import Data.List (delete, sort)

type Passport = [(F, String)]
data F = Fbyr | Fiyr | Feyr | Fhgt | Fhcl | Fecl | Fpid | Fcid deriving (F -> F -> Bool
(F -> F -> Bool) -> (F -> F -> Bool) -> Eq F
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: F -> F -> Bool
== :: F -> F -> Bool
$c/= :: F -> F -> Bool
/= :: F -> F -> Bool
Eq, Eq F
Eq F =>
(F -> F -> Ordering)
-> (F -> F -> Bool)
-> (F -> F -> Bool)
-> (F -> F -> Bool)
-> (F -> F -> Bool)
-> (F -> F -> F)
-> (F -> F -> F)
-> Ord F
F -> F -> Bool
F -> F -> Ordering
F -> F -> F
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: F -> F -> Ordering
compare :: F -> F -> Ordering
$c< :: F -> F -> Bool
< :: F -> F -> Bool
$c<= :: F -> F -> Bool
<= :: F -> F -> Bool
$c> :: F -> F -> Bool
> :: F -> F -> Bool
$c>= :: F -> F -> Bool
>= :: F -> F -> Bool
$cmax :: F -> F -> F
max :: F -> F -> F
$cmin :: F -> F -> F
min :: F -> F -> F
Ord, Int -> F -> ShowS
[F] -> ShowS
F -> String
(Int -> F -> ShowS) -> (F -> String) -> ([F] -> ShowS) -> Show F
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> F -> ShowS
showsPrec :: Int -> F -> ShowS
$cshow :: F -> String
show :: F -> String
$cshowList :: [F] -> ShowS
showList :: [F] -> ShowS
Show)

stageTH

-- |
-- >>> :main
-- 245
-- 133
main :: IO ()
IO ()
main =
  do [[(F, String)]]
inp <- [format|2020 4 (@F:%s( |%n))*&%n|]
     let xs :: [[(F, String)]]
xs = ([(F, String)] -> Bool) -> [[(F, String)]] -> [[(F, String)]]
forall a. (a -> Bool) -> [a] -> [a]
filter [(F, String)] -> Bool
complete [[(F, String)]]
inp
     Int -> IO ()
forall a. Show a => a -> IO ()
print ([[(F, String)]] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [[(F, String)]]
xs)
     Int -> IO ()
forall a. Show a => a -> IO ()
print (([(F, String)] -> Bool) -> [[(F, String)]] -> Int
forall (f :: * -> *) a. Foldable f => (a -> Bool) -> f a -> Int
countBy (((F, String) -> Bool) -> [(F, String)] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all ((F -> String -> Bool) -> (F, String) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry F -> String -> Bool
validate)) [[(F, String)]]
xs)

reqFields :: [F]
reqFields :: [F]
reqFields = [F] -> [F]
forall a. Ord a => [a] -> [a]
sort [F
Fbyr, F
Fiyr, F
Feyr, F
Fhgt, F
Fhcl, F
Fecl, F
Fpid]

complete :: Passport -> Bool
complete :: [(F, String)] -> Bool
complete [(F, String)]
x = [F]
reqFields [F] -> [F] -> Bool
forall a. Eq a => a -> a -> Bool
== [F] -> [F]
forall a. Ord a => [a] -> [a]
sort (F -> [F] -> [F]
forall a. Eq a => a -> [a] -> [a]
delete F
Fcid (((F, String) -> F) -> [(F, String)] -> [F]
forall a b. (a -> b) -> [a] -> [b]
map (F, String) -> F
forall a b. (a, b) -> a
fst [(F, String)]
x))

range :: Integer -> Integer -> Integer -> Bool
range :: Integer -> Integer -> Integer -> Bool
range Integer
lo Integer
hi Integer
x = Integer
lo Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
x Bool -> Bool -> Bool
&& Integer
x Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
hi

validate :: F -> String -> Bool
validate :: F -> String -> Bool
validate F
Fbyr (ReadS Integer
forall a. Read a => ReadS a
reads -> [(Integer
n,String
""  )]) = Integer -> Integer -> Integer -> Bool
range Integer
1920 Integer
2002 Integer
n
validate F
Fiyr (ReadS Integer
forall a. Read a => ReadS a
reads -> [(Integer
n,String
""  )]) = Integer -> Integer -> Integer -> Bool
range Integer
2010 Integer
2020 Integer
n
validate F
Feyr (ReadS Integer
forall a. Read a => ReadS a
reads -> [(Integer
n,String
""  )]) = Integer -> Integer -> Integer -> Bool
range Integer
2020 Integer
2030 Integer
n
validate F
Fhgt (ReadS Integer
forall a. Read a => ReadS a
reads -> [(Integer
n,String
"cm")]) = Integer -> Integer -> Integer -> Bool
range  Integer
150  Integer
193 Integer
n
validate F
Fhgt (ReadS Integer
forall a. Read a => ReadS a
reads -> [(Integer
n,String
"in")]) = Integer -> Integer -> Integer -> Bool
range   Integer
59   Integer
76 Integer
n
validate F
Fhcl (Char
'#':String
hcl)             = String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
hcl Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
6 Bool -> Bool -> Bool
&& (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isHexDigit String
hcl
validate F
Fecl String
ecl                   = String
ecl String -> [String] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String -> [String]
words String
"amb blu brn gry grn hzl oth"
validate F
Fpid String
pid                   = String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
pid Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
9 Bool -> Bool -> Bool
&& (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit String
pid
validate F
Fcid String
_                     = Bool
True
validate F
_    String
_                     = Bool
False