patternMinor
Conways game of life in Haskell
Viewed 0 times
lifegameconwayshaskell
Problem
I started with Haskell some months ago, but didn't really use it since then. As a simple training I implemented Conways game of life. What/How could it be improved?
```
module Main where
import Control.Monad.State
data CellState = Dead | Alive deriving (Eq)
data Cell = Cell CellState Int deriving (Eq)
data Row = Row [Cell] Int deriving (Eq)
data Grid = Grid [Row]
data NextError = EmptyListError
type GridState a = State Grid a
instance Show Grid where
show (Grid rows) = unlines $ map show rows
instance Show Row where
show (Row cells n) = (show n) ++ ": " ++ (unwords $ map show cells)
instance Show Cell where
show (Cell color _) = show color
instance Show CellState where
show c = case c of
Dead -> "_"
Alive -> "#"
main::IO()
main = print $ run (execState initializeGrid (createGrid 20 20)) 100
run :: Grid -> Int -> [Grid]
run g n = scanl (\s f -> f s) g $ replicate n playRound
initializeGrid :: GridState ()
initializeGrid = do
setPositionToColor 0 0 Alive
setPositionToColor 1 1 Alive
setPositionToColor 1 2 Alive
setPositionToColor 2 0 Alive
setPositionToColor 2 1 Alive
setPositionToColor 4 4 Alive
setPositionToColor 5 4 Alive
setPositionToColor 6 4 Alive
setPositionToColor 5 5 Alive
setPositionToColor 5 4 Alive
setPositionToColor 5 6 Alive
createGrid :: Int -> Int -> Grid
createGrid x y = Grid $ map createRow (take x [0,1..]) where
createRow = Row (map createCell (take y [0,1..]))
createCell = Cell Dead
setPositionToColor :: Int -> Int -> CellState -> GridState ()
setPositionToColor x y color = do
grid [Cell]
getCells (Row c _) = c
getRows :: Grid -> [Row]
getRows (Grid r) = r
replaceListElement :: [a] -> Int -> a -> [a]
replaceListElement list position replacement =
let (x, _:xs) = splitAt position list
in x ++ replacement : xs
pla
```
module Main where
import Control.Monad.State
data CellState = Dead | Alive deriving (Eq)
data Cell = Cell CellState Int deriving (Eq)
data Row = Row [Cell] Int deriving (Eq)
data Grid = Grid [Row]
data NextError = EmptyListError
type GridState a = State Grid a
instance Show Grid where
show (Grid rows) = unlines $ map show rows
instance Show Row where
show (Row cells n) = (show n) ++ ": " ++ (unwords $ map show cells)
instance Show Cell where
show (Cell color _) = show color
instance Show CellState where
show c = case c of
Dead -> "_"
Alive -> "#"
main::IO()
main = print $ run (execState initializeGrid (createGrid 20 20)) 100
run :: Grid -> Int -> [Grid]
run g n = scanl (\s f -> f s) g $ replicate n playRound
initializeGrid :: GridState ()
initializeGrid = do
setPositionToColor 0 0 Alive
setPositionToColor 1 1 Alive
setPositionToColor 1 2 Alive
setPositionToColor 2 0 Alive
setPositionToColor 2 1 Alive
setPositionToColor 4 4 Alive
setPositionToColor 5 4 Alive
setPositionToColor 6 4 Alive
setPositionToColor 5 5 Alive
setPositionToColor 5 4 Alive
setPositionToColor 5 6 Alive
createGrid :: Int -> Int -> Grid
createGrid x y = Grid $ map createRow (take x [0,1..]) where
createRow = Row (map createCell (take y [0,1..]))
createCell = Cell Dead
setPositionToColor :: Int -> Int -> CellState -> GridState ()
setPositionToColor x y color = do
grid [Cell]
getCells (Row c _) = c
getRows :: Grid -> [Row]
getRows (Grid r) = r
replaceListElement :: [a] -> Int -> a -> [a]
replaceListElement list position replacement =
let (x, _:xs) = splitAt position list
in x ++ replacement : xs
pla
Solution
A few things which came to my mind while reading your code:
-
The
-
You could shorten
-
-
On a more general note, seeing how long the
-
The
Show instance for CellState might be a bit nicer by not writing out the case expression manually but rather usinginstance Show CellState where
show Dead = "_"
show Alive = "#"-
You could shorten
initializeGrid by using mapM_:initializeGrid :: GridState ()
initializeGrid = mapM_ (\(x, y) -> setPositionToColor x y Alive)
[(0,0),(1,1),(1,2),(2,0),(2,1),(4,4),(5,4),(6,4),(5,5),(5,4),(5,6)]-
take n [0,1..] is the same as [0,1..n] (looking at createGrid).-
callCellState could be shortened by only explicitely testing the conditions determining whether the cell becomes (or remains) alive:calcCellState :: Bool -> Int -> CellState
calcCellState living livingNeighbours
| living && livingNeighbours `elem` [2,3] = Alive
| not living && livingNeighbours == 3 = Alive
| otherwise = DeadOn a more general note, seeing how long the
updateCell function is and how you had to roll your own next and previous function I do suspect that lists are not the most appropriate data structure for this kind of problem. A vector may be easier to work with.Code Snippets
instance Show CellState where
show Dead = "_"
show Alive = "#"initializeGrid :: GridState ()
initializeGrid = mapM_ (\(x, y) -> setPositionToColor x y Alive)
[(0,0),(1,1),(1,2),(2,0),(2,1),(4,4),(5,4),(6,4),(5,5),(5,4),(5,6)]calcCellState :: Bool -> Int -> CellState
calcCellState living livingNeighbours
| living && livingNeighbours `elem` [2,3] = Alive
| not living && livingNeighbours == 3 = Alive
| otherwise = DeadContext
StackExchange Code Review Q#88025, answer score: 3
Revisions (0)
No revisions yet.