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

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

Cellular automaton on a hexagonal grid

-}
module Main (main) where

import Advent (counts, stageTH, times)
import Advent.Coord (Coord, north, east, south, west)
import Advent.Format (format)
import Data.Foldable (foldl')
import Data.Map (Map)
import Data.Map.Strict qualified as Map
import Data.Set (Set)
import Data.Set qualified as Set

data D = De | Dne | Dse | Dw | Dnw | Dsw

stageTH

-- |
-- >>> :main
-- 400
-- 3768
main :: IO ()
IO ()
main =
  do [[D]]
inp <- [format|2020 24 (@D*%n)*|]
     let board :: Set Coord
board = [Coord] -> Set Coord
forall a. Ord a => [a] -> Set a
odds (([D] -> Coord) -> [[D]] -> [Coord]
forall a b. (a -> b) -> [a] -> [b]
map [D] -> Coord
walk [[D]]
inp)
     Int -> IO ()
forall a. Show a => a -> IO ()
print (Set Coord -> Int
forall a. Set a -> Int
Set.size Set Coord
board)
     Int -> IO ()
forall a. Show a => a -> IO ()
print (Set Coord -> Int
forall a. Set a -> Int
Set.size (Int -> (Set Coord -> Set Coord) -> Set Coord -> Set Coord
forall a. Int -> (a -> a) -> a -> a
times Int
100 Set Coord -> Set Coord
step Set Coord
board))

odds :: Ord a => [a] -> Set a
odds :: forall a. Ord a => [a] -> Set a
odds = Map a Int -> Set a
forall k a. Map k a -> Set k
Map.keysSet (Map a Int -> Set a) -> ([a] -> Map a Int) -> [a] -> Set a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Bool) -> Map a Int -> Map a Int
forall a k. (a -> Bool) -> Map k a -> Map k a
Map.filter Int -> Bool
forall a. Integral a => a -> Bool
odd (Map a Int -> Map a Int) -> ([a] -> Map a Int) -> [a] -> Map a Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> Map a Int
forall (f :: * -> *) a. (Foldable f, Ord a) => f a -> Map a Int
counts

step :: Set Coord -> Set Coord
step :: Set Coord -> Set Coord
step Set Coord
board
  = Map Coord Int -> Set Coord
forall k a. Map k a -> Set k
Map.keysSet
  (Map Coord Int -> Set Coord) -> Map Coord Int -> Set Coord
forall a b. (a -> b) -> a -> b
$ (Coord -> Int -> Bool) -> Map Coord Int -> Map Coord Int
forall k a. (k -> a -> Bool) -> Map k a -> Map k a
Map.filterWithKey Coord -> Int -> Bool
forall {a}. (Eq a, Num a) => Coord -> a -> Bool
rule
  (Map Coord Int -> Map Coord Int) -> Map Coord Int -> Map Coord Int
forall a b. (a -> b) -> a -> b
$ (Int -> Int -> Int) -> [Map Coord Int] -> Map Coord Int
forall (f :: * -> *) k a.
(Foldable f, Ord k) =>
(a -> a -> a) -> f (Map k a) -> Map k a
Map.unionsWith Int -> Int -> Int
forall a. Num a => a -> a -> a
(+)
    [(Coord -> Coord) -> Map Coord Int -> Map Coord Int
forall k1 k2 a. (k1 -> k2) -> Map k1 a -> Map k2 a
Map.mapKeysMonotonic (Coord
c Coord -> Coord -> Coord
forall a. Num a => a -> a -> a
+) Map Coord Int
neighborhood
    | Coord
c <- Set Coord -> [Coord]
forall a. Set a -> [a]
Set.toList Set Coord
board]
  where
    rule :: Coord -> a -> Bool
rule Coord
k a
v = a
v a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
2 Bool -> Bool -> Bool
|| a
v a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
1 Bool -> Bool -> Bool
&& Coord -> Set Coord -> Bool
forall a. Ord a => a -> Set a -> Bool
Set.member Coord
k Set Coord
board

neighborhood :: Map Coord Int
neighborhood :: Map Coord Int
neighborhood = [Coord] -> Map Coord Int
forall (f :: * -> *) a. (Foldable f, Ord a) => f a -> Map a Int
counts ((D -> Coord) -> [D] -> [Coord]
forall a b. (a -> b) -> [a] -> [b]
map D -> Coord
translate [D
Dw,D
De,D
Dne,D
Dse,D
Dnw,D
Dsw])

walk :: [D] -> Coord
walk :: [D] -> Coord
walk = [Coord] -> Coord
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ([Coord] -> Coord) -> ([D] -> [Coord]) -> [D] -> Coord
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (D -> Coord) -> [D] -> [Coord]
forall a b. (a -> b) -> [a] -> [b]
map D -> Coord
translate

translate :: D -> Coord
translate :: D -> Coord
translate D
Dw  = Coord
west
translate D
De  = Coord
east
translate D
Dne = Coord
north Coord -> Coord -> Coord
forall a. Num a => a -> a -> a
+ Coord
east
translate D
Dse = Coord
south
translate D
Dnw = Coord
north
translate D
Dsw = Coord
south Coord -> Coord -> Coord
forall a. Num a => a -> a -> a
+ Coord
west