Phillip Trelford's Array

POKE 36879,255

Silverlife

Back in February Jon Harrop published an implementation of John Conway’s Game of Life in 32 lines of F# using WPF. I’ve managed to increase Jon’s example to a massive 50 lines but at the same time get it running in Silverlight. If you have Silverlight 4 installed you can click in the box below to kick start life.


Jon’s original code for calculating life:

module Game =
    let count (a: _ [,]) x y =
        let m, n = a.GetLength 0, a.GetLength 1
        let mutable c = 0
        for x in x-1..x+1 do
            for y in y-1..y+1 do
                if x>=0 && x<m && y>=0 && y<n && a.[x, y] then
                    c <- c + 1
        if a.[x, y] then c-1 else c
    let rule (a: _ [,]) x y =
        match a.[x, y], count a x y with
        | true, (2 | 3) | false, 3 -> true
        | _ -> false

The Silverlight Control, containing a WriteableBitmap updated from an F# Async Workflow:

type GameControl(n) as control =
    inherit System.Windows.Controls.UserControl()
    let game =
        let rand = System.Random()
        Array2D.init n n (fun _ _ -> rand.Next 2 = 0) |> ref
    let update _ = Game.rule !game |> Array2D.init n n
    let bitmap = System.Windows.Media.Imaging.WriteableBitmap(n,n)
    do  control.Content <- System.Windows.Controls.Image(Source=bitmap)
    let render () =
        let pixels = bitmap.Pixels
        let black, white = 0xff000000, 0xffffffff
        for x in 0..n-1 do
            for y in 0..n-1 do
                pixels.[x+y*n] <- 
                    if (!game).[x, y] then white else black
    do  for i = 1 to 3 do game:= update() done; render ()
    do  async {
        use handle = new System.Threading.AutoResetEvent(false)
        do! control.MouseLeftButtonUp |> Async.AwaitEvent |> Async.Ignore
        do! Async.SwitchToThreadPool ()
        while true do
            game := update ()
            bitmap.Dispatcher.BeginInvoke(fun _ -> 
                render()
                bitmap.Invalidate()
                handle.Set() |> ignore
            ) |> ignore
            do! Async.AwaitWaitHandle handle |> Async.Ignore
            handle.Reset() |> ignore
        } |> Async.StartImmediate

Application startup:

type App() as app =
    inherit System.Windows.Application()
    do  app.Startup.Add(fun _ -> app.RootVisual <- GameControl(256))

Resources:


Source code: GameOfLife.zip (2.38 kb)

Missile Command

Just for fun, a clone of the 1980s arcade game Missile Command written in F# and deployed in Silverlight.


A language feature I have repeatedly found useful for games is state machines as sequences using yield. The following is used to define the path of explosions:

static member Path radius = seq {
    for i in [50..2..100] do
        yield radius * ((float i / 100.0) ** 3.0)
    for i in [100..-1..0] do
        yield radius * ((float i / 100.0) ** 3.0)
    }

 

References:

MissileCommand.zip (4.65 kb)

Fog Index

The Gunning Fog Index is an estimate of readability based on:

  • the number words per sentences
  • the number of complex words

The index estimates the years of formal education needed to understand the text on a first reading. Texts for a wide audience generally need a fog index less than 12.

If you have Silverlight 4 installed you, paste your text below to get a score:


The Gunning Fog Index formula along with some samples can be found on Wikipedia. The paste window gives results that are quite close to the values given in the samples, but this is an approximation.

The paste box was written in F# using the freely available Visual Studio 2010 Shell. If you are interested in the implementation some code snippets follow.

Compute approximate fog index:

let toFogIndex text =
    let sentences = getSentences text
    let words = sentences |> Array.collect getWords
    let complexWords =
        words
        |> Array.filter (fun word -> word.Length>3)
        |> Array.map removeSuffixes            
        |> Array.filter (fun word -> countSyllables word >= 3)
    0.4 * ((float words.Length/float sentences.Length) + 
           (100.0 * float complexWords.Length/float words.Length))

 

React to text pasted into window; computing new Fog index in the background then updating the display on the UI thread:

do  pasteText.TextChanged         
    |> Observable.map (fun _ -> pasteText.Text)
    |> Observable.onThreadPool
    |> Observable.map toFogIndex
    |> Observable.onDispatcher
    |> Observable.subscribe (fun index -> 
        label.Text <- sprintf "Fog Index %0.2f" index
    )
    |> remember

 

References:

FogIndexSource.zip (5.71 kb)