An array representation on the other hand, grows proportionally with the area spanned by all live cells. Since random patterns quite often fire gliders in opposite directions, this area can grow very quickly.
Anyway, this program takes a .cells pattern file for an optional argument; you can also press 'r' while paused for a random pattern, or click to create your own.
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE TupleSections #-}
import Graphics.UI.SDL as SDL
import System.Environment (getArgs)
import Control.Arrow ((***))
import Control.Monad (liftM2, join)
import Data.List (delete, unfoldr)
import System.Random.Mersenne.Pure64 (newPureMT, randomInt)
import qualified "hashmap" Data.HashSet as S
import qualified "unordered-containers" Data.HashMap.Strict as M
(xres, yres, cellSz) = (1600, 900, 3)
main = withInit [InitVideo] $ do
win <- setVideoMode xres yres 32 [Fullscreen]
args <- getArgs
pat <- case args of
[s] -> loadPattern s
_ -> return []
enableEvent SDLMouseMotion False
setCaption "Life" "Life"
pause win pat
pause w cs = do
delay 128
drawCells w cs
e <- pollEvent
case e of
KeyUp (Keysym SDLK_ESCAPE _ _) -> return ()
KeyUp (Keysym SDLK_SPACE _ _) -> run w cs
KeyUp (Keysym SDLK_r _ _) -> pause w =<< randPattern
MouseButtonUp x y _ -> click (scale x, scale y)
_ -> pause w cs
where
scale = (`div` cellSz) . fromIntegral
click c | c `elem` cs = pause w $ delete c cs
| otherwise = pause w $ c:cs
run w cs = do
drawCells w cs
e <- pollEvent
case e of
KeyUp (Keysym SDLK_ESCAPE _ _) -> return ()
KeyUp (Keysym SDLK_SPACE _ _) -> pause w cs
_ -> run w $ next cs
drawCells w cs = do
fillRect w (Just $ Rect 0 0 xres yres) (Pixel 0)
c <- createRGBSurface [SWSurface] cellSz cellSz 32 0 0 0 0
mapM_ (draw c . scale) cs
SDL.flip w
where
rect (x,y) = Just $ Rect x y cellSz cellSz
scale = join (***) (* cellSz)
draw c p = do fillRect c Nothing $ Pixel 0xFFFFFF
blitSurface c Nothing w $ rect p
----------------------------------------------------------------
loadPattern = fmap parse . readFile -- reads .cells format
where
parse = center . clean . coord . strip . lines
strip = dropWhile $ (== '!') . head
coord = zipWith zip $ map (zip [0..] . repeat) [0..]
clean = concatMap $ map fst . filter ((== 'O') . snd)
randPattern = fmap f newPureMT
where
f = center . uncurry zip . splitAt 48 . g
g = map (`rem` 9) . unfoldr (Just . randomInt)
center = map $ (x+) *** (+y)
where
[x,y] = map (`div` (2 * cellSz)) [xres, yres]
next cs = [i | (i,n) <- M.toList neighbors,
n == 3 || (n == 2 && S.member i cs')]
where
cs' = S.fromList cs
moore (x,y) = tail $ liftM2 (,) [x, x+1, x-1] [y, y+1, y-1]
neighbors = M.fromListWith (+) $ map (,1) $ moore =<< cs