0 people like it.

Game of Life

Simple implementation of Conway's Game of Life for the TryF# web site.

Import necessary namespaces for GUI programming

1: 
2: 
3: 
4: 
5: 
6: 
open System
open System.Threading
open System.Windows.Media
open System.Windows.Shapes
open System.Windows.Controls
open Microsoft.TryFSharp

Helpers for dealing with user interface

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
/// Helper function that runs an operation on the UI thread
let guiOperation operation =
  App.Dispatch (fun() -> operation ()) |> ignore

/// Helper function that runs a stateful operation on the user 
/// interface thread repeatedly (with minimal delay) until cancelled 
let guiLoop operation initial =
  let tok = new CancellationTokenSource()
  App.Dispatch (fun() -> 
    let rec loop state = async {
      do! Async.Sleep(1)    
      return! loop (operation state) }
    Async.StartImmediate(loop initial, tok.Token)
    App.Console.CanvasPosition <- CanvasPosition.Right) |> ignore
  tok

Creating & updating the game grid

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
// Configuration of the game of life (map & view sizes)
let actualWidth, actualHeight = 500.0, 500.0
let gameWidth, gameHeight = 40, 40

// Initialize board & colors
let gameView = Array2D.create gameWidth gameHeight null
let black = Color.FromArgb(255uy, 0uy, 0uy, 0uy)
let white = Color.FromArgb(255uy, 250uy, 250uy, 250uy)

// Create shapes representing the game field
guiOperation (fun () ->
  let boxWidth = actualWidth / (float gameWidth)
  let boxHeight = actualHeight / (float gameHeight)
  for x in 0 .. gameWidth - 1 do
    for y in 0 .. gameHeight - 1 do
      let box = Rectangle(Width = boxWidth, Height = boxHeight)
      Canvas.SetTop(box, float x * boxWidth)
      Canvas.SetLeft(box, float y * boxHeight)
      box.Fill <- new SolidColorBrush(white)
      App.Console.Canvas.Children.Add(box)
      gameView.[x, y] <- box
  App.Console.CanvasPosition <- CanvasPosition.Right )

/// Function that shows the new grid state on the screen
let updateGrid data = 
  data |> Array2D.iteri (fun x y v ->
     let color = if v = 1 then black else white
     gameView.[x, y].Fill <- SolidColorBrush(color))

The game of life!

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
/// Count the number of neighbors around game.[x, y]
let computeNeighbours x y (game:int[,]) =
  /// Safely get the value (with wrapping around the field)
  let getValue (x,y) =
    let x = (x + gameWidth) % gameWidth
    let y = (y + gameHeight) % gameHeight
    game.[x,y]
  // Generate offsets, get values & sum the number
  [ for dx in -1 .. 1 do
      for dy in -1 .. 1 do 
        if dx <> 0 || dy <> 0 then 
          yield getValue (x + dx, y + dy) ]
  |> Seq.sum

/// Implements a single step of the game of life
let gameStep board =
  board |> Array2D.mapi (fun x y v ->
    match computeNeighbours x y board with
    | 3 -> 1   // Becomes alive if there are 3 neighbors
    | 2 -> v   // Does not change if there are 2 neighbors
    | _ -> 0 ) // Dies if there is less or more than that

// Initialize the game of life randomly
let rnd = new Random()
let game = Array2D.init gameWidth gameHeight (fun _ _ -> rnd.Next(2))

// Run the simulation - display game & calculate new state in a loop
let it = game |> guiLoop (fun game ->
  updateGrid game
  gameStep game)

// Cancel the evaluation in TryF#
it.Cancel()
namespace System
namespace System.Threading
namespace System.Windows
namespace System.Media
namespace Microsoft
val guiOperation : operation:'a -> unit

Full name: Script.guiOperation


 Helper function that runs an operation on the UI thread
val operation : 'a
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
val guiLoop : operation:'a -> initial:'b -> CancellationTokenSource

Full name: Script.guiLoop


 Helper function that runs a stateful operation on the user
 interface thread repeatedly (with minimal delay) until cancelled
val initial : 'b
val tok : CancellationTokenSource
Multiple items
type CancellationTokenSource =
  new : unit -> CancellationTokenSource
  member Cancel : unit -> unit + 1 overload
  member Dispose : unit -> unit
  member IsCancellationRequested : bool
  member Token : CancellationToken
  static member CreateLinkedTokenSource : [<ParamArray>] tokens:CancellationToken[] -> CancellationTokenSource + 1 overload

Full name: System.Threading.CancellationTokenSource

--------------------
CancellationTokenSource() : unit
val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
Multiple items
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task<'T> -> Async<'T>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken

Full name: Microsoft.FSharp.Control.Async

--------------------
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>
static member Async.Sleep : millisecondsDueTime:int -> Async<unit>
static member Async.StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
property CancellationTokenSource.Token: CancellationToken
type Console =
  static member BackgroundColor : ConsoleColor with get, set
  static member Beep : unit -> unit + 1 overload
  static member BufferHeight : int with get, set
  static member BufferWidth : int with get, set
  static member CapsLock : bool
  static member Clear : unit -> unit
  static member CursorLeft : int with get, set
  static member CursorSize : int with get, set
  static member CursorTop : int with get, set
  static member CursorVisible : bool with get, set
  ...

Full name: System.Console
val actualWidth : float

Full name: Script.actualWidth
val actualHeight : float

Full name: Script.actualHeight
val gameWidth : int

Full name: Script.gameWidth
val gameHeight : int

Full name: Script.gameHeight
val gameView : obj [,]

Full name: Script.gameView
module Array2D

from Microsoft.FSharp.Collections
val create : length1:int -> length2:int -> value:'T -> 'T [,]

Full name: Microsoft.FSharp.Collections.Array2D.create
val black : obj

Full name: Script.black
val white : obj

Full name: Script.white
val boxWidth : float
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
val boxHeight : float
val x : int32
val y : int32
val box : obj
val updateGrid : data:int [,] -> unit

Full name: Script.updateGrid


 Function that shows the new grid state on the screen
val data : int [,]
val iteri : action:(int -> int -> 'T -> unit) -> array:'T [,] -> unit

Full name: Microsoft.FSharp.Collections.Array2D.iteri
val x : int
val y : int
val v : int
val color : obj
val computeNeighbours : x:int -> y:int -> game:int [,] -> int

Full name: Script.computeNeighbours


 Count the number of neighbors around game.[x, y]
val game : int [,]
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
val getValue : (int * int -> int)


 Safely get the value (with wrapping around the field)
val dx : int
val dy : int
module Seq

from Microsoft.FSharp.Collections
val sum : source:seq<'T> -> 'T (requires member ( + ) and member get_Zero)

Full name: Microsoft.FSharp.Collections.Seq.sum
val gameStep : board:int [,] -> int [,]

Full name: Script.gameStep


 Implements a single step of the game of life
val board : int [,]
val mapi : mapping:(int -> int -> 'T -> 'U) -> array:'T [,] -> 'U [,]

Full name: Microsoft.FSharp.Collections.Array2D.mapi
val rnd : Random

Full name: Script.rnd
Multiple items
type Random =
  new : unit -> Random + 1 overload
  member Next : unit -> int + 2 overloads
  member NextBytes : buffer:byte[] -> unit
  member NextDouble : unit -> float

Full name: System.Random

--------------------
Random() : unit
Random(Seed: int) : unit
val game : int [,]

Full name: Script.game
val init : length1:int -> length2:int -> initializer:(int -> int -> 'T) -> 'T [,]

Full name: Microsoft.FSharp.Collections.Array2D.init
Random.Next() : int
Random.Next(maxValue: int) : int
Random.Next(minValue: int, maxValue: int) : int
val it : CancellationTokenSource

Full name: Script.it
CancellationTokenSource.Cancel() : unit
CancellationTokenSource.Cancel(throwOnFirstException: bool) : unit

More information

Link:http://fssnip.net/cU
Posted:11 years ago
Author:Tomas Petricek
Tags: game , array2d , tutorial , tryfsharp