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

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

-}
module Main where

import Advent (format, counts)
import Data.Char (ord, chr)
import Data.List (sortBy)
import qualified Data.Map as Map

type Entry = ([String], Int, String)

-- | >>> :main
-- 158835
-- [993]
main :: IO ()
IO ()
main =
 do input <- [format|2016 4 ((%a+-)*%d[%a*]%n)*|]
    let valid = [([String], Int, String)
e | ([String], Int, String)
e <- [([String], Int, String)]
input, ([String], Int, String) -> Bool
isGoodEntry ([String], Int, String)
e]
    print (sum [sid | (_, sid, _) <- valid])
    print [sid | e@(_, sid, _) <- valid, decryptEntry e == "northpole object storage"]

decryptEntry :: Entry -> String
decryptEntry :: ([String], Int, String) -> String
decryptEntry ([String]
name, Int
sid, String
_) = [String] -> String
unwords ((String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map ((Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Char -> Char
decrypt Int
sid)) [String]
name)

isGoodEntry :: Entry -> Bool
isGoodEntry :: ([String], Int, String) -> Bool
isGoodEntry ([String]
name, Int
_, String
hash) = String
hash String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String -> String
computeHash ([String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [String]
name)

computeHash :: String -> String
computeHash :: String -> String
computeHash String
x = Int -> String -> String
forall a. Int -> [a] -> [a]
take Int
5 (((Char, Int) -> Char) -> [(Char, Int)] -> String
forall a b. (a -> b) -> [a] -> [b]
map (Char, Int) -> Char
forall a b. (a, b) -> a
fst (((Char, Int) -> (Char, Int) -> Ordering)
-> [(Char, Int)] -> [(Char, Int)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (Char, Int) -> (Char, Int) -> Ordering
forall {a} {a}. (Ord a, Ord a) => (a, a) -> (a, a) -> Ordering
ordering (Map Char Int -> [(Char, Int)]
forall k a. Map k a -> [(k, a)]
Map.toList (String -> Map Char Int
forall (f :: * -> *) a. (Foldable f, Ord a) => f a -> Map a Int
counts String
x))))
  where
    ordering :: (a, a) -> (a, a) -> Ordering
ordering (a
xa,a
xn) (a
ya,a
yn)
       = a -> a -> Ordering
forall a. Ord a => a -> a -> Ordering
compare a
yn a
xn -- descending
      Ordering -> Ordering -> Ordering
forall a. Semigroup a => a -> a -> a
<> a -> a -> Ordering
forall a. Ord a => a -> a -> Ordering
compare a
xa a
ya -- ascending

decrypt :: Int -> Char -> Char
decrypt :: Int -> Char -> Char
decrypt Int
n Char
c = Int -> Char
chr ((Char -> Int
ord Char
c Int -> Int -> Int
forall a. Num a => a -> a -> a
- Char -> Int
ord Char
'a' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
n) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
26 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Char -> Int
ord Char
'a')