\$\begingroup\$

You don't need the functions you created to work with arrays. There's no point in putting them in modules like that unless you're going to give the modules the [<RequireQualifiedAccess>] attribute.

You also didn't need the Cell type or that operator You shouldn't use and to define your types unless they're mutually recursive.

open System open System.Text type Board (width : int, height : int) = let grid = Array2D.create width height false let withinBounds (x:int) (y:int) = (0 <= x && x < width) && (0 <= y && y < height) let getNbs x y = ([| x-1,y+1; x,y+1; x+1,y+1; x-1,y ; x+1,y ; x-1,y-1; x,y-1; x+1,y-1 |] |> Array.filter(fun (a,b) -> (withinBounds a b)) |> Array.filter(fun (a,b) -> grid.[a,b] = true)).Length member __.Item with get (x,y) = grid.[x,y] and set (x,y) v = grid.[x,y] <- v member __.Tick() = grid |> Array2D.iteri( fun x y alive -> let liveNeighbors = getNbs x y match alive with | true -> grid.[x,y] <- liveNeighbors = 2 || liveNeighbors = 3 | false -> grid.[x,y] <- liveNeighbors = 3 ) override __.ToString() = let frame = Array2D.zeroCreate height width grid |> Array2D.iteri(fun x y alive -> match alive with |true -> frame.[height-1-y,x] <-"[@]" |false -> frame.[height-1-y,x] <-"[ ]" ) let sb = StringBuilder() frame |> Array2D.iteri(fun _ y str -> match y = height-1 with | true -> sb.Append(str).Append("

") |> ignore | false -> sb.Append(str) |> ignore ) sb.ToString()

The __.Item method gives you indexed acces into the board like this

let tb = Board(5,5) tb.[1,1] <- true tb.[1,0] <- true tb.[0,1] <- true tb.[0,3] <- true tb.[2,4] <- true tb.[4,4] <- true printfn "%A" tb tb.Tick() printfn "%A" tb tb.Tick() printfn "%A" tb [ ][ ][@][ ][@] [@][ ][ ][ ][ ] [ ][ ][ ][ ][ ] [@][@][ ][ ][ ] [ ][@][ ][ ][ ] [ ][ ][ ][ ][ ] [ ][ ][ ][ ][ ] [@][ ][ ][ ][ ] [@][ ][ ][ ][ ] [@][@][ ][ ][ ] [ ][ ][ ][ ][ ] [ ][ ][ ][ ][ ] [ ][ ][ ][ ][ ] [@][@][ ][ ][ ] [@][@][ ][ ][ ]

There's no need to build and use the console to test this code, write your code in an .fsx and run it in FSI