patternMinor
Compact game of life implementation
Viewed 0 times
lifeimplementationgamecompact
Problem
I am learning F# and implemented Conway's Game of Life as an exercise (There are also other implementations on CodeReview). I have tried to forget all my object-oriented background and practice "functional thinking".
Any feedback is welcome, especially related to alternative (more functional) solutions.
```
open System
[]
let main argv =
let rows = 30
let columns = 30
let size = rows*columns
let combine listX listY = [
for x in listX do
for y in listY do
yield x, y ]
let createRandomBoard =
let rnd = new Random(10)
[1..size]
|> List.map (fun x -> if rnd.NextDouble() List.toArray
let board = createRandomBoard
let toBoardLine (line : int[]) : string =
line |> Array.fold (fun agg i -> agg + (if i = 1 then "X" else ".")) ""
let drawBoard () =
[0..rows-1]
|> List.iter (fun y -> printfn "%A" (board.[yrows..yrows+columns-1] |> toBoardLine))
let indexToCoordinates index = (index % columns + 1, index / columns + 1)
let coordinatesToIndex (x, y) = (y - 1) * columns + (x - 1)
let getLivingNeighboursCount idx =
let x, y = indexToCoordinates idx
let (minX, maxX) = (if x = 1 then columns else x - 1), (if x = columns then 1 else x + 1)
let (minY, maxY) = (if y = 1 then rows else y - 1), (if y = rows then 1 else y + 1)
combine [minX; x; maxX] [minY; y; maxY]
|> List.filter (fun com -> com <> (x,y))
|> List.map coordinatesToIndex
|> List.map (fun x -> board.[x])
|> List.sum
let indexToNewState idx =
let state = board.[idx]
let livingNeigbours = getLivingNeighboursCount idx
(if livingNeigbours = 3 || (livingNeigbours = 2 && state = 1) then 1 else 0)
let updateState () =
[0..size-1]
|> List.iter (fun idx -> (Array.set board idx (indexToNewState idx)))
while true do
Console.Clear();
updateState
Any feedback is welcome, especially related to alternative (more functional) solutions.
```
open System
[]
let main argv =
let rows = 30
let columns = 30
let size = rows*columns
let combine listX listY = [
for x in listX do
for y in listY do
yield x, y ]
let createRandomBoard =
let rnd = new Random(10)
[1..size]
|> List.map (fun x -> if rnd.NextDouble() List.toArray
let board = createRandomBoard
let toBoardLine (line : int[]) : string =
line |> Array.fold (fun agg i -> agg + (if i = 1 then "X" else ".")) ""
let drawBoard () =
[0..rows-1]
|> List.iter (fun y -> printfn "%A" (board.[yrows..yrows+columns-1] |> toBoardLine))
let indexToCoordinates index = (index % columns + 1, index / columns + 1)
let coordinatesToIndex (x, y) = (y - 1) * columns + (x - 1)
let getLivingNeighboursCount idx =
let x, y = indexToCoordinates idx
let (minX, maxX) = (if x = 1 then columns else x - 1), (if x = columns then 1 else x + 1)
let (minY, maxY) = (if y = 1 then rows else y - 1), (if y = rows then 1 else y + 1)
combine [minX; x; maxX] [minY; y; maxY]
|> List.filter (fun com -> com <> (x,y))
|> List.map coordinatesToIndex
|> List.map (fun x -> board.[x])
|> List.sum
let indexToNewState idx =
let state = board.[idx]
let livingNeigbours = getLivingNeighboursCount idx
(if livingNeigbours = 3 || (livingNeigbours = 2 && state = 1) then 1 else 0)
let updateState () =
[0..size-1]
|> List.iter (fun idx -> (Array.set board idx (indexToNewState idx)))
while true do
Console.Clear();
updateState
Solution
Some remarks :
-
your
-
-
you are using '0' and '1' to model your two states, why not booleans ? It would eliminate the need for some tests.
-
-
forms such as :
are indeed functional but inefficient and, I believe, not as readable as using a loop. F# is especially powerful because it is a hybrid language (and not a purely functional one) : don't hesitate to leverage arrays and loops when it makes sense.
-
your
combine function is in fact List.zip, you might want to explore the Array and List sections of the standard library, they are full of very useful functions that will speed-up your development.-
createRandomBoard is not a function and thus will always be the same array (arrays being mutable, you might come across some unwanted side effects if you use it twice in the same code). It could be rewritten using Array.init :let createRandomBoard () =
let rnd = new Random(10)
Array.init size (fun _ -> if rnd.NextDouble() < 0.2 then 1 else 0)-
you are using '0' and '1' to model your two states, why not booleans ? It would eliminate the need for some tests.
-
Array.set ar ind x can be rewritten as `ar.[ind] -
forms such as :
[1..n] |> List.iter fare indeed functional but inefficient and, I believe, not as readable as using a loop. F# is especially powerful because it is a hybrid language (and not a purely functional one) : don't hesitate to leverage arrays and loops when it makes sense.
Code Snippets
let createRandomBoard () =
let rnd = new Random(10)
Array.init size (fun _ -> if rnd.NextDouble() < 0.2 then 1 else 0)[1..n] |> List.iter fContext
StackExchange Code Review Q#154587, answer score: 3
Revisions (0)
No revisions yet.