F# Poker Kata par Jérémie, Thomas et Damien
j'espere que je ne me trompe pas dans les prénoms...
Un mot sur FSharp.Formatting et literate F#
La doc est totalement générée à partir du Markdown contenu dans le fichier Poker.fsx.
Ce fichier fsx est cependant un fichier F# script valide qui peut être executé dans fsi !
Plus d'infos sur le site de FSharp.Formatting
Overview
Les couleurs sont représentée par une discriminated union:
1: 2: 3: 4: 5: |
type Color = | Spade | Heart | Club | Diamond |
On représente les valeurs des cartes de 2 à 14 comme ceci :
1: 2: |
2 3 4 5 6 7 8 9 10 J Q K A 2 3 4 5 6 7 8 9 10 11 12 13 14 |
L'as vaut 14 pour simplifier la plupart des cas, on a juste un cas particulier pour la suite.
Les actives patterns Ace
, King
, Queen
, Jack
permettent d'utiliser
les noms dans les patterns matching au lieu des valeurs 14, 11, 12 et 13.
1: 2: 3: 4: 5: 6: |
let isValue n v = if v = n then Some() else None let (|Ace|_|) = isValue 14 let (|King|_|) = isValue 13 let (|Queen|_|) = isValue 12 let (|Jack|_|) = isValue 11 |
Une carte est simplement un tuple couleur, value:
1:
|
type Card = Card of Color * int |
Les pattern matchings Color
et Value
matchent n'importe quelle carte
et en donne la couleur ou la valeur.
1: 2: 3: 4: 5: |
let color (Card(c,_)) = c let value (Card(_,v)) = v let (|Color|) = color let (|Value|) = value |
La main est une liste de Card
.
La plupart des fonctions ont besoin que la liste soit triée.
1:
|
let sort = List.sortBy value |
Couleur
L'active pattern HandColor
indique si on a une couleur, et laquelle.
Il trouve la couleur de la première carte et vérifie recursivement que toutes les autre ont la même couleur.
Il retourne alors la couleur de la première carte.
1: 2: 3: 4: 5: 6: 7: 8: 9: |
let (|HandColor|_|) hand = let rec loop firstColor = function | Color(c) :: tail when c = firstColor -> loop firstColor tail | [] -> Some firstColor | _ -> None match hand with | Color(c) :: tail -> loop c tail | [] -> None |
Suite
L'active pattern Sequence
indique si on a une suite, et quelle est la plus haute carte.
Il fonctionne sur une main classée de la plus petite à la plus grande valeur.
Il prend la valeur de la première (plus petite) carte, et vérifie recursivement que la carte suivante - 1 à la valeur de la précédente. Arrivé au bout, il sort la valeur de la dernière (plus forte) carte.
1: 2: 3: 4: |
let (|Sequence|_|) hand = let rec loop previous = function | Value(v) :: tail when previous = v-1 -> loop v tail |
Le cas particulier de la suite A 2 3 4 5 est assuré par une condition de sortie supplémentaire :
1: 2: |
// match the special case of A 2 3 4 5 as 2 3 4 5 14 !! | [Value(Ace)] when previous = 5 -> Some 5 |
Le pattern en est là quand il a trouvé 2 3 4 5. Si la dernière carte est un as, on est en presence d'une suite à 5.
1: 2: 3: 4: 5: |
| [] -> Some previous | _ -> None match hand with | Value v :: tail -> loop v tail | [] -> None |
Quinte Flush
L'active pattern QuinteFlush
indique si on a une suite et une couleur.
On utilise &
pour combiner les deux active patterns.
1: 2: 3: 4: |
let (|QuinteFlush|_|) hand = match hand with | Sequence(v) & HandColor(c) -> Some(c,v) | _ -> None |
Autres combinaisons
Pour les autres mains, on utilise l'active pattern GroupCards
qui retourne une liste de paires (value,count) classée
par count décroissant.
On matchera alors les counts de cette liste pour trouver les carrés, full, brelans, doubles paires et paires.
1: 2: 3: 4: 5: 6: |
let (|GroupCards|) hand = hand |> Seq.countBy value |> Seq.sortBy snd |> Seq.toList |> List.rev |
Carré
Le carré contient un groupe de 4 cartes identiques, et une carte seule
1: 2: 3: 4: |
let (|Square|_|) = function | GroupCards [s, 4; _] -> Some(s) | _ -> None |
Full
Le full contient un brelan et une paire
1: 2: 3: 4: |
let (|Full|_|) = function | GroupCards [b,3; p,2] -> Some(b,p) | _ -> None |
Brelan
Le brelan contient un groupe de 3 cartes identiques et 2 cartes seules
1: 2: 3: 4: |
let (|Brelan|_|) = function | GroupCards [b,3; _; _] -> Some(b) | _ -> None |
Double paire
La double paire contient deux paires, et une carte seule.
On retourne la plus haute valeur en premier.
1: 2: 3: 4: |
let (|DoublePair|_|) = function | GroupCards [p1, 2; p2,2; _] -> Some(max p1 p2, min p1 p2) | _ -> None |
Paire
La paire contient deux cartes identiques et trois cartes seules.
1: 2: 3: 4: |
let (|Pair|_|) = function | GroupCards [p,2; _,1; _; _] -> Some(p) | _ -> None |
La plus haute carte
Sinon on prend simplement la carte la plus haute.
1: 2: 3: 4: |
let (|High|_|) = function | GroupCards ((_,1) :: _) as hand -> Some(List.maxBy value hand) | _ -> None |
Nommage d'une combinaison
Les fonction svalue et scolor sont utilisée pour le pretty print, avec les noms de cartes et les caractères unicodes pour les couleurs.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: |
let svalue _ = function | Ace -> "A" | King -> "K" | Queen -> "Q" | Jack -> "J" | n -> sprintf "%d" n let scolor _ = function | Spade -> "\u2660" | Heart -> "\u2665" | Club -> "\u2663" | Diamond -> "\u2666" |
la fonction showHand
prend une main, la trie,
et match avec les active patterns de chaque combinaison pour afficher
le résultat.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: |
let showHand hand = match sort hand with | QuinteFlush(c,v) -> sprintf "Quint Flush of %a%a" svalue v scolor c | Square(v) -> sprintf "Square of %a" svalue v | Full(b,p) -> sprintf "Full of %a by %a" svalue b svalue p | HandColor(c) -> sprintf "Color of %a" scolor c | Sequence(v) -> sprintf "Sequence of %a" svalue v | Brelan(b) -> sprintf "Brelan of %a" svalue b | DoublePair(p1, p2) -> sprintf "Double pair of %a and %a" svalue p1 svalue p2 | Pair(p) -> sprintf "Pair of %a" svalue p | High(Card(c,v)) -> sprintf "Highest %a%a" svalue v scolor c | _ -> hand |> Seq.map (fun (Card(c,v)) -> sprintf "%a%a" svalue v scolor c) |> String.concat " " |> printfn "%s" |
Tests
Les examples utilisent les fonctions h
,s
,c
et d
pour
Hear
, Spade
, Club
et Diamond
. Elles prennent une valeur et
constuisent une carte de la couleur donnée.
1: 2: 3: 4: |
let h n= Card(Heart, n) let s n= Card(Spade, n) let c n= Card(Club, n) let d n= Card(Diamond, n) |
Les valeurs a
, k
, q
, j
représentent les valeurs
de l'as (14), roi (13), reine (12) et valet (11).
1: 2: 3: 4: |
let a = 14 let k = 13 let q = 12 let j = 11 |
Teste la Quinte flush.
1:
|
[h 7;h 8; h 9;h 10; h j] |> showHand |
Teste la quinte flush A 2 3 4 5. c'est le cas particulier de Sequence.
1:
|
[h a;h 2; h 3;h 4; h 5] |> showHand |
Teste la quinte flush 10 J Q K A. C'est l'autre extreme.
1:
|
[h 10;h j; h q;h k; h a] |> showHand |
Teste une main sans combinaison.
1:
|
[s 5; h 3; c q; d 4; h 8] |> showHand |
Teste une double paire.
1:
|
[s 5; h 3; c 5; d 4; c 3] |> showHand |
Teste As carte haute.
1:
|
[s 9; h 10; c j; d q; h a] |> showHand |
Teste un Carré.
1:
|
[s 6; h 6; h j; d 6; c 6] |> showHand |
Teste un Brelan.
1:
|
[s j; d 3; h j; c q; d j] |> showHand |
Teste un Full.
1:
|
[s j; d 3; h j; c 3; d j] |> showHand |
Teste une paire.
1:
|
[s 5; h 3; c 5; d 4; c a] |> showHand |
Teste une couleur.
1:
|
[s 5; s 3; s 5; s 4; s a] |> showHand |
Teste une suite.
1:
|
[h 7;s 8; d 9;c 10; h j] |> showHand |
Full name: Poker.isValue
union case Card.Card: Color * int -> Card
--------------------
type Card = | Card of Color * int
Full name: Poker.Card
| Spade
| Heart
| Club
| Diamond
Full name: Poker.Color
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
Full name: Poker.color
Full name: Poker.value
Full name: Poker.sort
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IEnumerable
interface IEnumerable<'T>
member Head : 'T
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list
Full name: Microsoft.FSharp.Collections.List<_>
Full name: Microsoft.FSharp.Collections.List.sortBy
active recognizer Color: Card -> Color
Full name: Poker.( |Color| )
--------------------
type Color =
| Spade
| Heart
| Club
| Diamond
Full name: Poker.Color
Full name: Poker.( |Value| )
Full name: Poker.( |Ace|_| )
Full name: Poker.( |Sequence|_| )
Full name: Poker.( |HandColor|_| )
from Microsoft.FSharp.Collections
Full name: Microsoft.FSharp.Collections.Seq.countBy
Full name: Microsoft.FSharp.Collections.Seq.sortBy
Full name: Microsoft.FSharp.Core.Operators.snd
Full name: Microsoft.FSharp.Collections.Seq.toList
Full name: Microsoft.FSharp.Collections.List.rev
Full name: Poker.( |GroupCards| )
Full name: Microsoft.FSharp.Core.Operators.max
Full name: Microsoft.FSharp.Core.Operators.min
Full name: Microsoft.FSharp.Collections.List.maxBy
Full name: Poker.svalue
Full name: Poker.( |King|_| )
Full name: Poker.( |Queen|_| )
Full name: Poker.( |Jack|_| )
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
Full name: Poker.scolor
Full name: Poker.showHand
Full name: Poker.( |QuinteFlush|_| )
Full name: Poker.( |Square|_| )
Full name: Poker.( |Full|_| )
Full name: Poker.( |Brelan|_| )
Full name: Poker.( |DoublePair|_| )
Full name: Poker.( |Pair|_| )
Full name: Poker.( |High|_| )
Full name: Microsoft.FSharp.Collections.Seq.map
from Microsoft.FSharp.Core
Full name: Microsoft.FSharp.Core.String.concat
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
Full name: Poker.h
Full name: Poker.s
Full name: Poker.c
Full name: Poker.d
Full name: Poker.a
Full name: Poker.k
Full name: Poker.q
Full name: Poker.j