4 people like it.

TicTacToe(Joinads Example)

TicTacToe game simulator implemented by using Joinads(http://tomasp.net/blog/joinads-async-prog.aspx). Game logic in this snippet was simplified so nicely by using Joinads. You can run this snippet on Try Joinads (http://tryjoinads.org/).

 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: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
59: 
60: 
61: 
62: 
63: 
64: 
65: 
66: 
67: 
68: 
69: 
70: 
71: 
72: 
73: 
74: 
75: 
76: 
77: 
78: 
79: 
80: 
81: 
82: 
83: 
84: 
85: 
86: 
87: 
88: 
89: 
90: 
91: 
92: 
93: 
94: 
95: 
96: 
97: 
open System
open FSharp.Extensions.Joinads

/// state of the square Empty or Occupied by O or X.
type Square = Empty | O | X 
/// turn to move 
let mutable turn = O
/// squares of the game
let squares = [| for _ in 0..8 -> Empty |]

/// Standard .NET delegate is needed to use Async.AwaitEvent
type PutEventHandler = delegate of obj * Square -> unit
/// events which fired when the corresponding square is occupied by any player  
let putEvents = [| for _ in 0..8 -> Event<PutEventHandler,Square>() |]

/// print the turn and status of the squares
let printPosition () =
  String.concat "\n-+-+-\n" [ 
    for y in 0..2 ->
      String.concat "|" [
        for x in 0..2 ->
          match squares.[x+y*3] with
          | Empty -> " "
          | O -> "O"
          | X -> "X" 
      ]
  ] 
  |> printfn "%s"
  printfn "(the side to next move is %A)\n" turn

/// swap turn to move  
let swapTurn () = 
  turn <- match turn with O -> X | X -> O | _ -> failwith "invalid turn!!"

/// put piece on the corresponding square of the given index
let putOn index =
  // is inside the board ?
  if 0 <= index && index < 9 then
    // is the target square empty ?
    if squares.[index] = Empty then
      squares.[index] <- turn
      putEvents.[index].Trigger(null,turn)
      swapTurn ()
      printPosition ()        
    else
      printfn "index %d is already occupied by %A\n" index squares.[index]
  else
    printfn "square must be between 1 and 9\n"

/// clear squares to initiazlize game position
let clearSquares () =
  for index in 0..8 do squares.[index] <- Empty
  
/// Judge win of the specified player.
/// return the line occupied all squares by the same player's mark
let judgeWinOf (turn:Square) = async {
  let putted index = 
    putEvents.[index].Publish
    |> Event.filter(fun square -> square = turn)
    |> Async.AwaitEvent
  match!                             
    putted 0, putted 1, putted 2,
    putted 3, putted 4, putted 5,
    putted 6, putted 7, putted 8 with
  | _,_,_,?,?,?,?,?,? -> return "0-1-2"
  | ?,?,?,_,_,_,?,?,? -> return "3-4-5"
  | ?,?,?,?,?,?,_,_,_ -> return "6-7-8"
  | _,?,?,_,?,?,_,?,? -> return "0-3-6"
  | ?,_,?,?,_,?,?,_,? -> return "1-4-7"
  | ?,?,_,?,?,_,?,?,_ -> return "2-5-8"
  | _,?,?,?,_,?,?,?,_ -> return "0-4-8"
  | ?,?,_,?,_,?,_,?,? -> return "2-4-6"
  } 

/// play all games of given books
let playTicTacToeGames books =
  for book in books do
    /// put marks according to the book
    let putting = async {
      for square in book do
        printfn "** put on square %d" square
        putOn square
        do! Async.Sleep 100
      }
    printfn "****\n**** Start a new game!!\n****\n"
    async {
      // game ends when player X or O won or all marks in book are putted
      match! judgeWinOf X , judgeWinOf O , putting with
      | line, ?,    ? -> printfn "** Player X won the game!!(line=%s)\n" line
      | ?,    line, ? -> printfn "** Player O won the game!!(line=%s)\n" line
      | ?,    ?,    _ -> printfn "** Game draw!!\n"
      clearSquares ()
    }
    |> Async.RunSynchronously

let books = [[0;3;1;4;6;7;2];[0;4;8;1;7;6;2;5;3]]
playTicTacToeGames books // start games
namespace System
namespace Microsoft.FSharp
type Square =
  | Empty
  | O
  | X

Full name: Script.Square


 state of the square Empty or Occupied by O or X.
union case Square.Empty: Square
union case Square.O: Square
union case Square.X: Square
val mutable turn : Square

Full name: Script.turn


 turn to move
val squares : Square []

Full name: Script.squares


 squares of the game
type PutEventHandler =
  delegate of obj * Square -> unit

Full name: Script.PutEventHandler


 Standard .NET delegate is needed to use Async.AwaitEvent
type obj = Object

Full name: Microsoft.FSharp.Core.obj
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
val putEvents : Event<PutEventHandler,Square> []

Full name: Script.putEvents


 events which fired when the corresponding square is occupied by any player
Multiple items
module Event

from Microsoft.FSharp.Control

--------------------
type Event<'T> =
  new : unit -> Event<'T>
  member Trigger : arg:'T -> unit
  member Publish : IEvent<'T>

Full name: Microsoft.FSharp.Control.Event<_>

--------------------
type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =
  new : unit -> Event<'Delegate,'Args>
  member Trigger : sender:obj * args:'Args -> unit
  member Publish : IEvent<'Delegate,'Args>

Full name: Microsoft.FSharp.Control.Event<_,_>

--------------------
new : unit -> Event<'T>

--------------------
new : unit -> Event<'Delegate,'Args>
val printPosition : unit -> unit

Full name: Script.printPosition


 print the turn and status of the squares
Multiple items
type String =
  new : value:char -> string + 7 overloads
  member Chars : int -> char
  member Clone : unit -> obj
  member CompareTo : value:obj -> int + 1 overload
  member Contains : value:string -> bool
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EndsWith : value:string -> bool + 2 overloads
  member Equals : obj:obj -> bool + 2 overloads
  member GetEnumerator : unit -> CharEnumerator
  member GetHashCode : unit -> int
  ...

Full name: System.String

--------------------
String(value: nativeptr<char>) : unit
String(value: nativeptr<sbyte>) : unit
String(value: char []) : unit
String(c: char, count: int) : unit
String(value: nativeptr<char>, startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int) : unit
String(value: char [], startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : unit
val concat : sep:string -> strings:seq<string> -> string

Full name: Microsoft.FSharp.Core.String.concat
val y : int
val x : int
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val swapTurn : unit -> unit

Full name: Script.swapTurn


 swap turn to move
val failwith : message:string -> 'T

Full name: Microsoft.FSharp.Core.Operators.failwith
val putOn : index:int -> unit

Full name: Script.putOn


 put piece on the corresponding square of the given index
val index : int
val clearSquares : unit -> unit

Full name: Script.clearSquares


 clear squares to initiazlize game position
val index : int32
val judgeWinOf : turn:Square -> Async<unit>

Full name: Script.judgeWinOf


 Judge win of the specified player.
 return the line occupied all squares by the same player's mark
val turn : Square
val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
val putted : (int -> Async<Square>)
val filter : predicate:('T -> bool) -> sourceEvent:IEvent<'Del,'T> -> IEvent<'T> (requires delegate and 'Del :> Delegate)

Full name: Microsoft.FSharp.Control.Event.filter
val square : Square
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.AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member Async.Sleep : millisecondsDueTime:int -> Async<unit>
static member Async.RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:Threading.CancellationToken -> 'T

More information

Link:http://fssnip.net/aE
Posted:5 years ago
Author:nagat01
Tags: game , async , joinads