packages feed

brians-brain-0.0.1: BriansBrain.hs

import Data.Array             -- Used to store the world state for processing
import System.Random          -- Used to generate the initial random world
import Control.Monad          -- Used for some fancy looping constructs
import Control.Concurrent     -- Used to fork the quit event handler
import Graphics.UI.SDL as SDL -- Used to draw the pretty pictures
import Control.Parallel.Strategies

data Cell  = Off | Dying | On deriving (Eq, Enum)

worldX   = 90                -- The horizontal size of the world
worldY   = 90                -- The vertical size of the world
cellSize = 8                 -- The overall size of a cell
border   = 1                 -- The border width between cells
screenX  = worldX * cellSize -- The horizontal size of the world, in pixels
screenY  = worldY * cellSize -- The vertical size of the world, in pixels
fillSize = cellSize - border -- The size of the filled area in each cell

stepCell (Off,   2) = On     -- If a dead cell has 2 live neighbors, turn on
stepCell (Off,   _) = Off    --   Otherwise, just stay turned off
stepCell (Dying, _) = Off    -- Dying cells always turn off
stepCell (On,    _) = Dying  -- Live cells always start to die

indexArray x y = listArray ((1,1),(x,y)) [(a,b) | a <- [1..x], b <- [1..y]]
stepWorld w    = newWorld `using` parArr rwhnf
  where newWorld = fmap (stepCell . getPeers w) $ indexArray worldX worldY

getPeers world (x,y) = (world ! (x,y), length . filter (== On) $ neighbors)
  where neighbors    = [getCell x y | x <- [x-1 .. x+1], y <- [y-1 .. y+1]]
        getCell x y  = world ! (clip worldX x, clip worldY y)
        clip max val | val <  1  = clip max $ val + max - 1
                     | val > max = clip max $ val - max + 1
                     | otherwise = val

main = do rng <- newStdGen
          SDL.init [SDL.InitVideo]
          SDL.setCaption "Brian's Purely Functional Brain" "Brian's Brain"
          surface <- SDL.setVideoMode screenX screenY 24 [SDL.DoubleBuf]
          forkIO . forever $ waitEvent >>= \e -> when (e == Quit) quit
          mapM (drawWorld surface) (iterate stepWorld $ world rng)
  where world = listArray ((1,1),(worldX,worldY)) . map toEnum . randomRs (0,2)

drawWorld s w = do sequence [draw x y | x <- [1..worldX], y <- [1..worldY]]
                   SDL.flip s
  where draw x y = SDL.fillRect s (Just rect) . color $ w ! (x,y)
          where rect        = SDL.Rect (scale x) (scale y) fillSize fillSize
                scale n     = (n - 1) * cellSize
                color On    = SDL.Pixel 0x00FFFFFF
                color Dying = SDL.Pixel 0x00888888
                color Off   = SDL.Pixel 0x00000000