Day 2

Both parts of the problem ask us to count how many passwords satisfy some validation function based on a policy. We create a datatype for a password and its associated policy as follows.

data Password = Password Int Int Char String
  deriving (Eq, Show)

instance Read Password where
  readPrec = lift $ do
    min' <- read <$> munch1 isDigit
    _    <- char '-'
    max' <- read <$> munch1 isDigit
    skipSpaces
    c    <- get
    _    <- char ':'
    skipSpaces
    pw   <- many get
    pure $ Password min' max' c pw

Here we use the ReadP type parser because they are much more easier to work with than old style parsers. Plus we can perform some data validation on them too to ensure our input is correctly formatted. Next, validating part A is as simple as counting how many characters in the password are equal to the one we want and comparing the count.

validateA :: Password -> Bool
validateA (Password l h c p) = n >= l && n <= h
  where n = length $ filter (==c) p

For part B, we use the ix traversal to safely index into our password in case it is too short and check to see if the characters are equal. The given condition is also equivalent to boolean XOR.

validateB :: Password -> Bool
validateB (Password l h c p) = first `xor` second
  where first = p ^? ix (l - 1) == Just c
        second = p ^? ix (h - 1) == Just c