module Simulation where
-- Chose array for it's constant time random access
-- (Haskell's List has linear time random access)
import Data.Array
data State = Alive | Dead deriving (Eq)
type Row = Array Int State
type Grid = Array Int Row
data Board = Board {bGrid :: Grid, bRows :: Int, bCols :: Int} deriving (Show)
data Coord = Coord Int Int deriving (Show)
instance Num Coord where
Coord a b + Coord c d = Coord (a+c) (b+d)
Coord a b - Coord c d = Coord (a-c) (b-d)
instance Show State where
show Alive = "■"
show Dead = " "
-- Gets the coordinates of the cells that are alive around a
-- coordinate
neighbors :: Board -> Coord -> [Coord]
neighbors board coord = filter (\c -> state board c == Alive) validCoords
where
validCoords = filter (validCoord board) coordsAround
coordsAround = map (+ coord) directions
directions = [Coord y x | y <- [-1..1], x <- [-1..1], not (x==0 && y==0)]
-- Determines whether a given coordinate is in bounds
validCoord :: Board -> Coord -> Bool
validCoord board (Coord y x) = xValid && yValid
where
xValid = x >= 0 && x < bCols board
yValid = y >= 0 && y < bRows board
-- Get number of neighbors
numNeighbors :: Board -> Coord -> Int
numNeighbors board coord = length $ neighbors board coord
-- Gets the state of a coordinate on the board
-- (TODO: make this a total function)
state :: Board -> Coord -> State
state board (Coord y x) = bGrid board ! y ! x
-- Executes a single step for a coordinate on the board
singleStep :: Board -> Coord -> State
singleStep board coord = stepCell currentState bors
where
currentState = state board coord
bors = numNeighbors board coord
-- Given the current state, and the # of alive neighbors,
-- returns the new state of the cell
stepCell :: State -> Int -> State
stepCell Dead 3 = Alive
stepCell Dead _ = Dead
stepCell Alive n
| n < 2 = Dead
| n > 3 = Dead
| otherwise = Alive
-- Runs one iteration of game of life
step :: Board -> Board
step board = board {bGrid = newGrid}
where
newGrid :: Grid
newGrid = listArray (0, rs-1) $ map newRow [0..rs-1]
newRow :: Int -> Row
newRow y = listArray (0,cs-1)
[singleStep board (Coord y x) | x <- [0..cs-1]]
cs = bCols board
rs = bRows board