First attempt at doing anything F#-y from a very heavy C# background, think I've grasped the fundamentals of FP, but always good to get other eyes on it:
namespace FSharp.GameOfLife
open System
open System.Drawing
module Array2D =
let toArray (arr: 'T [,]) = arr |> Seq.cast<'T> |> Seq.toArray
module Array =
let count (predicate: 'T -> bool) (arr: 'T[]) = arr |> Array.filter(predicate) |> Array.length
module Game =
let (<&>) f g = (fun x -> f x && g x)
type Cell (position) =
member this.Position = position
member val IsAlive = false with get, set
and Board (width : int, height : int) =
let grid = Array2D.init width height (fun x y -> Cell(System.Drawing.Point(x, y)))
let withinBounds (x : int, y : int) =
let insideWidth = width > x && x >= 0
let insideHeight = height > y && y >= 0
insideWidth && insideHeight
let isSamePosition (cell : Cell, x : int, y : int) =
cell.Position = Point(x, y)
let getNeighbours (grid : Cell[,]) (cell : Cell) =
let isDifferentPosition (x, y) = not (isSamePosition (cell, x, y))
let positionModifiers = Array2D.initBased -1 -1 3 3 (fun x y -> (cell.Position.X + x, cell.Position.Y + y))
positionModifiers |> Array2D.toArray |> Array.filter (withinBounds <&> isDifferentPosition) |> Array.map (fun (x, y) -> grid.[x, y].IsAlive)
member this.Grid with get() = grid
member this.Tick() =
let cellWithNeighbourStates = this.Grid |> Array2D.map (fun cell -> (cell, (getNeighbours this.Grid cell))) |> Array2D.toArray
cellWithNeighbourStates |> Array.iter (fun (cell, neighbours) ->
let liveNeighbours = neighbours |> Array.count (fun c -> c = true)
if cell.IsAlive then
cell.IsAlive <- 2 = liveNeighbours || 3 = liveNeighbours
else
cell.IsAlive <- 3 = liveNeighbours
)
member this.SetAlive(cells : list<int * int>) =
cells |> List.iter (fun (x, y) -> this.Grid.[y, x].IsAlive <- true)
override this.ToString() =
let structs = this.Grid |> Array2D.map (fun cell -> if cell.IsAlive then "[]" else "()")
let asRows = [|0..(height - 1)|] |> Array.map (fun row -> structs.[0.., row] |> Array.fold (fun acc elem -> sprintf "%s%s" acc elem) Environment.NewLine)
asRows |> Array.fold (fun acc elem -> sprintf "%s%s" acc elem) Environment.NewLine
Can be tested as such:
open FSharp.GameOfLife.Game
[<EntryPoint>]
let main argv =
let board = Board (10, 10)
//standard ocilator
let x = board.SetAlive([
(1, 1)
(1, 2)
(2, 1)
(2, 2)
(3, 3)
(3, 4)
(4, 3)
(4, 4)
])
printf "%s" (board.ToString())
[|0..2|] |> Array.iter (fun _ ->
board.Tick()
printf "%s" (board.ToString())
)
System.Console.ReadKey() |> ignore
0 // return an integer exit code