Day 2 is the first of the year for one of the typical “one input, two interpretations” AoC puzzles. This post is literate Haskell.
{-# LANGUAGE RecordWildCards #-}
import Data.List
import Text.Parsec
type Parser = Parsec String ()
Each record provides two numbers, a character and a password. The interpretation for them varies for both parts, so I’ll store them under generic names.
data Entry = Entry
a :: !Int
{ b :: !Int
, c :: !Char
, password :: String
, }
I’ll parse them with a straightfoward Parsec
parser.
entry :: Parser Entry
= Entry
entry <$> number
<* char '-'
<*> number
<* char ' '
<*> anyChar
<* string ": "
<*> many1 anyChar
number :: Parser Int
= read <$> many1 digit number
And a little wrapper.
parseInput :: String -> [Entry]
=
parseInput either (error . show) id .
mapM (parse (entry <* eof) "input") .
lines
The notion of password validity for part 1 is that the character must occur a number of times within the interval.
valid1 :: Entry -> Bool
Entry{..} = a <= numC && numC <= b
valid1 where numC = length (elemIndices c password)
For part 2, the logic is a bit more involved: exactly one position indexed by the numbers must be equal to the character. I’ll implement that as reading both, checking for equality to the provided character, and validate by canonicalizing with a sort. Being careful to adjust the indexing’s origin.
valid2 :: Entry -> Bool
Entry{..} =
valid2 sort (map ((== c) . (password !!) . pred) [a,b]) == [False,True]
And here’s the main
wrapper.
main :: IO ()
= do
main <- parseInput <$> readFile "day02.in"
entries print $ length $ filter valid1 entries
print $ length $ filter valid2 entries
This concludes today’s solution. See you soon!