4
\$\begingroup\$

Since I'm learning F# along with functional programming, I managed to implement the rules for Conway's Game of Life. I'm not sure if I can improve some of its parts, though. For example, the neighbours function does an ugly range testing, but I can't think of anything simpler.

module Game

open System

type Cell =
    | Alive
    | Dead

let amount_neighbours (board: Cell[,]) (pos: int * int) =
    let range (n: int) (limit: int) = 
        let min = if n < 1 then 0 else n - 1
        let max = if n > (limit - 2) then (limit - 1) else n + 1
        [min .. max]

    let x_range = range (fst pos) (Array2D.length1 board)
    let y_range = range (snd pos) (Array2D.length2 board)

    List.sum [for x in x_range ->
        List.sum [for y in y_range -> if board.[x, y] = Alive && (x, y) <> pos then 1 else 0]]

let lifecycle (board: Cell[,]) = 
    Array2D.init (Array2D.length1 board) (Array2D.length2 board) (fun i j ->
        let neighbours = amount_neighbours board (i, j)
        match neighbours with
            | 2 -> board.[i, j]
            | 3 -> Alive
            | _ -> Dead)

let rec process_game (board: Cell[,]) (n: int) =
    match n with
        | x when x > 0 -> 
            printfn "Iteration"
            printfn "%A" board
            process_game (lifecycle board) (n - 1)
        | _ -> 0

[<EntryPoint>]
let main args =
    let board = Array2D.init 5 5 (fun i j -> if i = 2 && j > 0 && j < 4 then Alive else Dead)
    ignore (process_game board 4)
    0
\$\endgroup\$
3
\$\begingroup\$

I thought it would make sense if you created a function alive, which would take care of bounds checking for you. And also simplify the the final expression by using a single sequence expression instead of two like you do.

But I'm not sure it's actually much better than your version:

let amount_neighbours (board: Cell[,]) (pos: int * int) =
    let alive board pos = 
        let (x, y) = pos
        if x < 0 || x >= Array2D.length1 board ||
           y < 0 || y >= Array2D.length2 board then
            false
        else
            board.[x, y] = Alive

    let vicinity x = seq { x - 1 .. x + 1 }

    seq {
        for x in vicinity (fst pos) do
        for y in vicinity (snd pos) do
        if (x, y) <> pos && alive board (x, y) then
            yield true
    } |> Seq.length
\$\endgroup\$
  • \$\begingroup\$ Well, this is a good improvement IMO. I'll vote up as soon as I can! Thanks. \$\endgroup\$ – viniciushana Oct 20 '12 at 19:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.