59 lines
1.9 KiB
Haskell
59 lines
1.9 KiB
Haskell
|
module Main (main) where
|
||
|
import Control.Monad (when)
|
||
|
import Data.List (foldl')
|
||
|
import Data.Map qualified as M
|
||
|
import System.Exit (die)
|
||
|
|
||
|
exampleExpectedOutput = 848
|
||
|
|
||
|
type Coord = (Int, Int, Int, Int)
|
||
|
type Input = M.Map Coord Bool
|
||
|
|
||
|
parseInput :: String -> IO Input
|
||
|
parseInput filename = do
|
||
|
input <- readFile filename
|
||
|
return . M.fromList . fst $ foldl' process ([], (0, 0, 0, 0)) input
|
||
|
where
|
||
|
process :: ([(Coord, Bool)], Coord) -> Char -> ([(Coord, Bool)], Coord)
|
||
|
process (acc, (x, y, z, w)) c = case c of
|
||
|
'.' -> (acc, (x+1, y, z, w))
|
||
|
'#' -> (((x, y, z, w), True):acc, (x+1, y, z, w))
|
||
|
'\n' -> (acc, (0, y+1, z, w))
|
||
|
|
||
|
r :: [Int]
|
||
|
r = [-1..1]
|
||
|
|
||
|
complete :: Input -> Input
|
||
|
complete input = M.foldrWithKey process M.empty input
|
||
|
where
|
||
|
process :: Coord -> Bool -> Input -> Input
|
||
|
process (x, y, z, w) False acc = acc
|
||
|
process (x, y, z, w) True acc = M.unions [M.singleton (x, y, z, w) True, acc, M.fromList [((x+a, y+b, z+c, w+d), False) | a<-r, b<-r, c<-r, d<-r]]
|
||
|
|
||
|
step :: Int -> Input -> Input
|
||
|
step 0 input = input
|
||
|
step n input = step (n-1) (M.foldrWithKey evolve M.empty input')
|
||
|
where
|
||
|
input' = complete input
|
||
|
evolve :: Coord -> Bool -> Input -> Input
|
||
|
evolve (x, y, z, w) s acc = case s of
|
||
|
False -> M.insert (x, y, z, w) (actives == 3) acc
|
||
|
True -> M.insert (x, y, z, w) (actives == 3 || actives == 4) acc
|
||
|
where
|
||
|
actives = length . filter id $ [lookup (x+a, y+b, z+c, w+d) | a<-r, b<-r, c<-r, d<-r]
|
||
|
lookup :: Coord -> Bool
|
||
|
lookup c = case M.lookup c input' of
|
||
|
Just True -> True
|
||
|
otherwise -> False
|
||
|
|
||
|
compute :: Input -> Int
|
||
|
compute input = M.size . M.filter id $ step 6 input
|
||
|
|
||
|
main :: IO ()
|
||
|
main = do
|
||
|
example <- parseInput "example"
|
||
|
let exampleOutput = compute example
|
||
|
when (exampleOutput /= exampleExpectedOutput) (die $ "example failed: got " ++ show exampleOutput ++ " instead of " ++ show exampleExpectedOutput)
|
||
|
input <- parseInput "input"
|
||
|
print $ compute input
|