{-# Language BlockArguments, LambdaCase #-}
{-|
Module      : Main
Description : Day 5 solution
Copyright   : (c) Eric Mertens, 2021
License     : ISC
Maintainer  : emertens@gmail.com

<https://adventofcode.com/2015/day/5>

Password validation problems.

-}
module Main where

import Advent (getInputLines, countBy)
import Data.List (isInfixOf, tails)

main :: IO ()
IO ()
main =
  do [String]
strs <- Int -> Int -> IO [String]
getInputLines Int
2015 Int
5
     Int -> IO ()
forall a. Show a => a -> IO ()
print ((String -> Bool) -> [String] -> Int
forall (f :: * -> *) a. Foldable f => (a -> Bool) -> f a -> Int
countBy String -> Bool
part1 [String]
strs)
     Int -> IO ()
forall a. Show a => a -> IO ()
print ((String -> Bool) -> [String] -> Int
forall (f :: * -> *) a. Foldable f => (a -> Bool) -> f a -> Int
countBy String -> Bool
part2 [String]
strs)

part1 :: String -> Bool
part1 :: String -> Bool
part1 String
str = String -> Bool
threeVowels String
str Bool -> Bool -> Bool
&& String -> Bool
hasDouble String
str Bool -> Bool -> Bool
&& String -> Bool
noProhibited String
str

part2 :: String -> Bool
part2 :: String -> Bool
part2 String
str = String -> Bool
pairTwice String
str Bool -> Bool -> Bool
&& String -> Bool
nearby String
str

threeVowels :: String -> Bool
threeVowels :: String -> Bool
threeVowels = Bool -> Bool
not (Bool -> Bool) -> (String -> Bool) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (String -> Bool) -> (String -> String) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> String -> String
forall a. Int -> [a] -> [a]
drop Int
2 (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
filter (Char -> String -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
"aeiou")

hasDouble :: String -> Bool
hasDouble :: String -> Bool
hasDouble =
  (String -> Bool) -> String -> Bool
search \case
    Char
x:Char
y:String
_ -> Char
x Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
y
    String
_     -> Bool
False

noProhibited :: String -> Bool
noProhibited :: String -> Bool
noProhibited String
str = Bool -> Bool
not ((String -> Bool) -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
str) [String
"ab",String
"cd",String
"pq",String
"xy"])

search :: (String -> Bool) -> String -> Bool
search :: (String -> Bool) -> String -> Bool
search String -> Bool
p = (String -> Bool) -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any String -> Bool
p ([String] -> Bool) -> (String -> [String]) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
forall a. [a] -> [[a]]
tails

pairTwice :: String -> Bool
pairTwice :: String -> Bool
pairTwice =
  (String -> Bool) -> String -> Bool
search \case
    Char
x:Char
y:String
z -> [Char
x,Char
y] String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
z
    String
_     -> Bool
False

nearby :: String -> Bool
nearby :: String -> Bool
nearby =
  (String -> Bool) -> String -> Bool
search \case
    Char
w:Char
_:Char
y:String
_ -> Char
w Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
y
    String
_       -> Bool
False