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

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

Password validation rules.

-}
module Main (main) where

import Advent (countBy, count)
import Advent.Format (format)

type Input = (Int, Int, Char, String)

-- | Check both password validation rules against the list of passwords.
--
-- >>> :main
-- 600
-- 245
main :: IO ()
IO ()
main =
  do [(Int, Int, Char, [Char])]
inp <- [format|2020 2 (%u-%u %c: %s%n)*|]
     Int -> IO ()
forall a. Show a => a -> IO ()
print (((Int, Int, Char, [Char]) -> Bool)
-> [(Int, Int, Char, [Char])] -> Int
forall (f :: * -> *) a. Foldable f => (a -> Bool) -> f a -> Int
countBy (Int, Int, Char, [Char]) -> Bool
p1 [(Int, Int, Char, [Char])]
inp)
     Int -> IO ()
forall a. Show a => a -> IO ()
print (((Int, Int, Char, [Char]) -> Bool)
-> [(Int, Int, Char, [Char])] -> Int
forall (f :: * -> *) a. Foldable f => (a -> Bool) -> f a -> Int
countBy (Int, Int, Char, [Char]) -> Bool
p2 [(Int, Int, Char, [Char])]
inp)

-- | Target character must occur between low and high inclusive bounds.
--
-- >>> p1 (1,3,'a',"abcde")
-- True
--
-- >>> p1 (1,3,'b',"cdefg")
-- False
--
-- >>> p1 (2,9,'c',"ccccccccc")
-- True
p1 :: Input -> Bool
p1 :: (Int, Int, Char, [Char]) -> Bool
p1 (Int
lo,Int
hi,Char
c,[Char]
str) = Int
lo Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
n Bool -> Bool -> Bool
&& Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
hi
  where n :: Int
n = Char -> [Char] -> Int
forall (f :: * -> *) a. (Foldable f, Eq a) => a -> f a -> Int
count Char
c [Char]
str

-- | Target character must occur at two given, 1-based indexes.
--
-- >>> p2 (1,3,'a',"abcde")
-- True
--
-- >>> p2 (1,3,'b',"cdefg")
-- False
--
-- >>> p2 (2,9,'c',"ccccccccc")
-- False
p2 :: Input -> Bool
p2 :: (Int, Int, Char, [Char]) -> Bool
p2 (Int
i1,Int
i2,Char
c,[Char]
str) = Int -> Bool
check Int
i1 Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
/= Int -> Bool
check Int
i2
  where check :: Int -> Bool
check Int
i = ([Char]
str [Char] -> Int -> Char
forall a. HasCallStack => [a] -> Int -> a
!! (Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1)) Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c