10 people like it.

Tennis Kata

Another solution to the Tennis Kata, to score a tennis game.

 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: 
    /// The two players
    type Player = A | B

    /// The point score in for a player in a game
    type PlayerPoints = Zero | Fifteen | Thirty | Forty 

    /// The score of a game
    type Score = 
        | Points of PlayerPoints * PlayerPoints 
        | Advantage of Player 
        | Deuce 
        | Game of Player

    /// Compute the next score in a game 
    let nextPointScore a = 
        match a with 
        | Zero -> Fifteen
        | Fifteen -> Thirty
        | Thirty -> Forty
        | Forty -> failwith "what??"

    /// Check if we've reached deuce
    let normalize score = 
        match score with 
        | Points(Forty,Forty) -> Deuce
        | _ -> score

    /// Score a point in a game
    let scorePoint score point =
        match score, point with 
        | Advantage player1, player2 when  player1 = player2 -> Game player1
        | Advantage player1, player2 -> Deuce
        | Deuce, player -> Advantage player
        | Points(Forty, _), A -> Game A
        | Points(_, Forty), B -> Game B
        | Points(a, b), A -> normalize (Points (nextPointScore a, b))
        | Points(a, b), B -> normalize (Points (a, nextPointScore b))
        | Game _ , _ -> (* printfn "the game is over!"; *) score

    /// Score a whole game, where the game is represented as a sequence of points
    let scoreGame (points: seq<Player>) = 
        Seq.scan scorePoint (Points(Zero,Zero)) points

    /// A sample game - A wins every point
    let game1 = seq { while true do yield A }

    /// A sample game - A and B swap points indefinitely
    let game2 = seq { while true do 
                         yield A 
                         yield B }

    /// A sample game - A and B trade points but A wins more points than B
    let game3 = seq { while true do 
                         yield A 
                         yield B 
                         yield A }

    scoreGame game1 |> Seq.truncate 10 |> Seq.toList


    scoreGame game2 |> Seq.truncate 10 |> Seq.toList

    scoreGame game3 |> Seq.truncate 10 |> Seq.toList


    /// Generate a random game
    let randomGame i = 
        let rnd = new System.Random(i) 
        seq { while true do if rnd.NextDouble() < 0.5 then yield A else yield B }

    // Random testing of 1000 games
    for i in 1 .. 1000 do 
        scoreGame (randomGame i)
           |> Seq.nth 10 
           |> printfn "result is %A"
union case Player.A: Player
union case Player.B: Player
type PlayerPoints =
  | Zero
  | Fifteen
  | Thirty
  | Forty

Full name: Script.PlayerPoints


 The point score in for a player in a game
union case PlayerPoints.Zero: PlayerPoints
union case PlayerPoints.Fifteen: PlayerPoints
union case PlayerPoints.Thirty: PlayerPoints
union case PlayerPoints.Forty: PlayerPoints
type Score =
  | Points of PlayerPoints * PlayerPoints
  | Advantage of Player
  | Deuce
  | Game of Player

Full name: Script.Score


 The score of a game
union case Score.Points: PlayerPoints * PlayerPoints -> Score
union case Score.Advantage: Player -> Score
type Player =
  | A
  | B

Full name: Script.Player


 The two players
union case Score.Deuce: Score
union case Score.Game: Player -> Score
val nextPointScore : a:PlayerPoints -> PlayerPoints

Full name: Script.nextPointScore


 Compute the next score in a game
val a : PlayerPoints
val failwith : message:string -> 'T

Full name: Microsoft.FSharp.Core.Operators.failwith
val normalize : score:Score -> Score

Full name: Script.normalize


 Check if we've reached deuce
val score : Score
val scorePoint : score:Score -> point:Player -> Score

Full name: Script.scorePoint


 Score a point in a game
val point : Player
val player1 : Player
val player2 : Player
val player : Player
val b : PlayerPoints
val scoreGame : points:seq<Player> -> seq<Score>

Full name: Script.scoreGame


 Score a whole game, where the game is represented as a sequence of points
val points : seq<Player>
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

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

--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>
module Seq

from Microsoft.FSharp.Collections
val scan : folder:('State -> 'T -> 'State) -> state:'State -> source:seq<'T> -> seq<'State>

Full name: Microsoft.FSharp.Collections.Seq.scan
val game1 : seq<Player>

Full name: Script.game1


 A sample game - A wins every point
val game2 : seq<Player>

Full name: Script.game2


 A sample game - A and B swap points indefinitely
val game3 : seq<Player>

Full name: Script.game3


 A sample game - A and B trade points but A wins more points than B
val truncate : count:int -> source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.truncate
val toList : source:seq<'T> -> 'T list

Full name: Microsoft.FSharp.Collections.Seq.toList
val randomGame : i:int -> seq<Player>

Full name: Script.randomGame


 Generate a random game
val i : int
val rnd : System.Random
namespace System
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

--------------------
System.Random() : unit
System.Random(Seed: int) : unit
System.Random.NextDouble() : float
val i : int32
val nth : index:int -> source:seq<'T> -> 'T

Full name: Microsoft.FSharp.Collections.Seq.nth
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
Raw view New version

More information

Link:http://fssnip.net/8R
Posted:8 years ago
Author:Don Syme
Tags: tennis; sample; functional data; kata