Learning F#: The Game of Life (Vol. 5)

Intro

I have been plugging away at the Game of Life implementation and have been spinning my wheels on mapping.

Maps

There’s two types of maps that I’m aware of in F#.
There’s “Map” the type and there’s “map” the function.

“Map” the type

Map the type is essentially a dictionary. It’s an immutable structure of keys and values.

I have only learned to construct a map from a sequence of tuples:

let myMap = [ (0,0); (1,1); (2,2); (3,3) ] |> Map.ofSeq

If one wanted to retrieve values from a map (aka: f# dictionary) then I think one could do something like this:

let values = [ (0,0); (1,1); (2,2); (3,3) ] |> Map.ofSeq
                                            |> Map.toSeq
                                            |> Seq.map snd

The above code should have captured all the value elements within the dictionary.

“Map” the function

Map also is the name of a function that does a transform from one structure to another structure based on source data.

Remember this:

let values = [ (0,0); (1,1); (2,2); (3,3) ] |> Map.ofSeq
                                            |> Map.toSeq
                                            |> Seq.map snd

Well we used map the function here:

...
|> Seq.map snd

In the above case we took a list of tuples and took the second element from the tuple.

The Game of Life

In the Game of Life, I added some new tests:

[<Test>]
let ``set cell to alive``() =
    // Setup
    let rowCount = 3
    let target = { X=2; Y=2; State=Alive }

    let grid = rowCount |> createGrid
                        |> setCell target
    // Test
    let result = grid |> getStatus (2,2)

    // Verify
    result |> should equal Alive

[<Test>]
let ``get neighbors``() =
    // Setup
    let rowCount = 3
    let grid = rowCount |> createGrid

    let center = 2,2
    let count = center |> getNeighbors
                       |> List.length
    // Verify
    count |> should equal 8

[<Test>]
let ``center cell is the first to live``() =
    // Setup
    let rowCount = 3
    let result = rowCount |> createGrid
                          |> cycle 
                          |> getStatus (2,2)
    // Verify
    result |> should equal Alive

[<Test>]
let ``Any live cell with fewer than two live neighbors dies, as if caused by under-population``() =
    // Setup
    let rowCount = 3
    let result = rowCount |> createGrid
                          |> cycle
                          |> cycle
                          |> getStatus (2,2)
    // Verify
    result |> should equal Dead

Thus, the updated business logic is the following:

type State = Alive | Dead
type Cell = { X:int; Y:int; State:State }

type Response = | Die
                | Survive
                | Resurect
    
let isNeighbor cell1 cell2 =

    let isAbsNeighbor v1 v2 =
        match abs (v1 - v2) with
        | 0 | 1 -> true
        | _     -> false

    let isValueNeighbor v1 v2 =
        match v1 >= 0
          &&  v2 >= 0 with
        | true  -> isAbsNeighbor v1 v2
        | _     -> isAbsNeighbor v2 v1

    match cell1.X <> cell2.X
      ||  cell1.Y <> cell2.Y with
    | true ->   isValueNeighbor cell1.X cell2.X
             && isValueNeighbor cell1.Y cell2.Y
    | _    -> false

let createGrid rowCount = 

    [for x in 1..rowCount do
        for y in 1..rowCount do
            yield { X=x; Y=y; State=Dead } 
    ]|> List.map (fun c -> (c.X, c.Y), { X=c.X; Y=c.Y; State=Dead })
     |> Map.ofList

let setCell cell (grid:Map<(int * int), Cell>) =

    grid |> Map.map (fun k v -> match k with
                                | c when c = (cell.X, cell.Y) -> { v with State=cell.State }
                                | _ -> v)

let getStatus coordinate (grid:Map<(int * int), Cell>) =

    match grid.TryFind coordinate with
    | Some cell -> cell.State
    | None      -> Dead

let getNeighbors (coordinate:int*int) =
        
    let x,y = coordinate
    let west = x-1, y
    let northWest = x-1, y+1
    let north = x, y+1
    let northEast = x+1, y+1
    let east = x+1, y
    let southEast = x+1, y-1
    let south = x, y-1
    let southWest = x-1, y-1

    [west; northWest; north; northEast; east; southEast; south; southWest]

let values = [ (0,0); (1,1); (2,2); (3,3) ] |> Map.ofSeq
                                            |> Map.toSeq
                                            |> Seq.map snd

let cycle (grid:Map<(int * int), Cell>) =

    let isBeginnng = grid |> Map.forall (fun _ cell -> cell.State = Dead)
    match isBeginnng with
    | true  ->  grid |> setCell { X=2; Y=2; State=Alive }
    | false ->  ??? idk

Impediment

I’m stuck on updating the grid per time interval.

Here’s the function that I attempt to update the cells for within the grid:

let cycle (grid:Map<(int * int), Cell>) =

    let isBeginnng = grid |> Map.forall (fun _ cell -> cell.State = Dead)
    match isBeginnng with
    | true  ->  grid |> setCell { X=2; Y=2; State=Alive }
    | false ->  ??? idk

In the above code, I’m struggling with transforming the state of specific elements within a collection that’s always changing? Specifically, I do not know of an appropriate approach for transforming a grid each time the state of one of the cells within the grid changes.

Conclusion

In conclusion, I have been plugging away at the Game of Life implementation and have been spinning my wheels on mapping. Thus I discussed the two types of mapping.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: