# Game of Life

While reading about cellular automata in preparation for an essay it struck me that I have never actually written Conway’s Game of Life. No, really!

To correct this embarrassing fact I quickly wrote a version in Haskell using the GLUT bindings.

It is very simple, but it works. :-)

``````import Graphics.UI.GLUT hiding (get)
import Graphics.Rendering.GLU.Raw (gluOrtho2D)
import Data.IORef
import System.Random

-- dimensions of our cellular space
width  = 80 :: Int
height = 60 :: Int

-- takes a two-dimensional list and returns the neighbours of (x,y)
neighbours :: [[a]] -> (Int,Int) -> [a]
neighbours m (x,y) = map (\\(x',y') -> m !! y' !! x') \$ filter valid neighbours'
where height'       = length m
valid (x',y') = x' >= 0 && x' < width' && y' >= 0 && y' < height'
neighbours'   = [(x-1,y-1),(x,y-1),(x+1,y-1), -- neighbours over
(x-1,y),(x+1,y),             -- neighbours left/right
(x-1,y+1),(x,y+1),(x+1,y+1)] -- neighbours under

update :: IORef [[Bool]] -> IO ()
update c = do

let coords = [(x,y) | y <- [0..(height-1)], x <- [0..(width-1)]]

nextGen <- mapM (\\(x,y) -> do
let cell = cells !! y !! x
let ns   = neighbours cells (x,y)
return \$ liveOrDead cell ((length . filter id) ns)
) coords

writeIORef c (nLists width nextGen)
display c

-- survival rule: a live cell only lives on if it has 2 or 3 live neighbours
-- birth rule: a dead cell becomes a live cell if it has 3 live neighbours
liveOrDead :: Bool -> Int -> Bool
liveOrDead True nLive = nLive `elem` [2,3]
liveOrDead False nLive = nLive == 3

-- utility function: split a list into sublists of length n
nLists :: Int -> [a] -> [[a]]
nLists _ [] = []
nLists n ls = take n ls : nLists n (drop n ls)

-- utility function: draws a square at (x,y) with size w×h
drawQuad :: GLdouble -> GLdouble -> GLdouble -> GLdouble -> IO ()
drawQuad x y w h =
vertex (Vertex3  x     y    0)
vertex (Vertex3 (x+w)  y    0)
vertex (Vertex3 (x+w) (y-h) 0)
vertex (Vertex3  x    (y-h) 0)

-- draw each cell as a coloured square
display :: IORef [[Bool]] -> IO ()
display c = do

let h = fromIntegral height
let w = fromIntegral width

mapM_ (\\(n,b) -> do
if b
then currentColor \$= Color4 1.0 0.8 0.6 1.0
else currentColor \$= Color4 0.4 0.5 0.4 1.0
let x = 1/w*fromIntegral (n `mod` width)
let y = 1-(1/h*fromIntegral (n `div` width))
) (zip [0..] \$ concat cells)

swapBuffers

main :: IO ()
main = do
g <- newStdGen
_ <- getArgsAndInitialize

-- random starting values
cells <- newIORef ((nLists width . take (width*height) . randoms) g)

_ <- createWindow "Conway's Game of Life"
initialDisplayMode    \$= [DoubleBuffered]
windowSize            \$= Size 800 600
displayCallback       \$= display cells
idleCallback          \$= Just (update cells)
gluOrtho2D 0 1 0 1    -- orthogonal projection
mainLoop              -- start main loop``````