/// 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) = 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"