F#: Modeling a Deck of Cards Kata

I really do enjoy writing F# code. In fact, I enjoy it more than C# or XAML. I feel as if I’m spending my mornings doing Soduku.

This post will go over my thought process as I attempted to construct a deck of cards.

My first attempt was the following:

module TempImpl

type Joker = Big | Little with
    static member Suite() = [Big ; Little]

type Spade = 
    | Ace  | King  | Queen | Jack| Ten
    | Nine | Eight | Seven | Six
    | Five | Four  | Three | Two with
    static member Suite() = [Ace  ; King  ; Queen ; Jack ; Ten
                             Nine ; Eight ; Seven ; Six
                             Five ; Four  ; Three ; Two]

type Club = 
    | Ace  | King  | Queen | Jack| Ten
    | Nine | Eight | Seven | Six
    | Five | Four  | Three | Two with
    static member Suite() = [Ace  ; King  ; Queen ; Jack ; Ten
                             Nine ; Eight ; Seven ; Six
                             Five ; Four  ; Three ; Two]

type Heart = 
    | Ace  | King  | Queen | Jack| Ten
    | Nine | Eight | Seven | Six
    | Five | Four  | Three | Two with
    static member Suite() = [Ace  ; King  ; Queen ; Jack ; Ten
                             Nine ; Eight ; Seven ; Six
                             Five ; Four  ; Three ; Two]

type Diamond = 
    | Ace  | King  | Queen | Jack| Ten
    | Nine | Eight | Seven | Six
    | Five | Four  | Three | Two with
    static member Suite() = [Ace  ; King  ; Queen ; Jack ; Ten
                             Nine ; Eight ; Seven ; Six
                             Five ; Four  ; Three ; Two]

type Deck = 
    | Spades   of Spade   list
    | Hearts   of Heart   list
    | Clubs    of Club    list
    | Diamonds of Diamond list
    | Jokers   of Joker   list

let deck = [Spades   <| Spade.Suite()
            Hearts   <| Heart.Suite()
            Clubs    <| Club.Suite()
            Diamonds <| Diamond.Suite()
            Jokers   <| Joker.Suite()]

The following output was generated:

val deck : Deck list =
  [Spades
     [Ace; King; Queen; Jack; Ten; Nine; Eight; Seven; Six; Five; Four; Three;
      Two];
   Hearts
     [Ace; King; Queen; Jack; Ten; Nine; Eight; Seven; Six; Five; Four; Three;
      Two];
   Clubs
     [Ace; King; Queen; Jack; Ten; Nine; Eight; Seven; Six; Five; Four; Three;
      Two];
   Diamonds
     [Ace; King; Queen; Jack; Ten; Nine; Eight; Seven; Six; Five; Four; Three;
      Two]; Jokers [Big; Little]]

The output above presents challenges if I were to next shuffle the deck. Hence, the list consists of a list of suits instead of a list of cards.

I then went back to the drawing board and wrote the following:

module File1

type Ace =   Spades | Hearts | Clubs | Diamonds
type King =  Spades | Hearts | Clubs | Diamonds
type Queen = Spades | Hearts | Clubs | Diamonds
type Jack =  Spades | Hearts | Clubs | Diamonds
type Ten =   Spades | Hearts | Clubs | Diamonds
type Nine =  Spades | Hearts | Clubs | Diamonds
type Eight = Spades | Hearts | Clubs | Diamonds
type Seven = Spades | Hearts | Clubs | Diamonds
type Six =   Spades | Hearts | Clubs | Diamonds
type Five =  Spades | Hearts | Clubs | Diamonds
type Four =  Spades | Hearts | Clubs | Diamonds
type Three = Spades | Hearts | Clubs | Diamonds
type Two =   Spades | Hearts | Clubs | Diamonds

type Suit =
    | Ace   of Ace 
    | King  of King 
    | Queen of Queen 
    | Jack  of Jack
    | Ten   of Ten 
    | Nine  of Nine 
    | Eight of Eight
    | Seven of Seven
    | Six   of Six 
    | Five  of Five 
    | Four  of Four 
    | Three of Three 
    | Two   of Two
    | BigJoker   
    | LittleJoker

let deck = 
    [
     Ace Ace.Spades   ; King King.Spades   ; Queen Queen.Spades   ; Jack Jack.Spades  
     Ace Ace.Hearts   ; King King.Hearts   ; Queen Queen.Hearts   ; Jack Jack.Hearts  
     Ace Ace.Clubs    ; King King.Clubs    ; Queen Queen.Clubs    ; Jack Jack.Clubs   
     Ace Ace.Diamonds ; King King.Diamonds ; Queen Queen.Diamonds ; Jack Jack.Diamonds

     Ten Ten.Spades   ; Nine Nine.Spades   ; Eight Eight.Spades   ; Seven Seven.Spades  
     Ten Ten.Hearts   ; Nine Nine.Hearts   ; Eight Eight.Hearts   ; Seven Seven.Hearts  
     Ten Ten.Clubs    ; Nine Nine.Clubs    ; Eight Eight.Clubs    ; Seven Seven.Clubs   
     Ten Ten.Diamonds ; Nine Nine.Diamonds ; Eight Eight.Diamonds ; Seven Seven.Diamonds

     Six Six.Spades   ; Five Five.Spades   ; Four Four.Spades   ; Three Three.Spades  
     Six Six.Hearts   ; Five Five.Hearts   ; Four Four.Hearts   ; Three Three.Hearts
     Six Six.Clubs    ; Five Five.Clubs    ; Four Four.Clubs    ; Three Three.Clubs   
     Six Six.Diamonds ; Five Five.Diamonds ; Four Four.Diamonds ; Three Three.Diamonds

     Two Two.Spades
     Two Two.Hearts
     Two Two.Clubs    
     Two Two.Diamonds 

     BigJoker ; LittleJoker
    ]

The above code generated the following deck:

val deck : Suit list =
  [Ace Spades; King Spades; Queen Spades; Jack Spades; Ace Hearts; King Hearts;
   Queen Hearts; Jack Hearts; Ace Clubs; King Clubs; Queen Clubs; Jack Clubs;
   Ace Diamonds; King Diamonds; Queen Diamonds; Jack Diamonds; Ten Spades;
   Nine Spades; Eight Spades; Seven Spades; Ten Hearts; Nine Hearts;
   Eight Hearts; Seven Hearts; Ten Clubs; Nine Clubs; Eight Clubs; Seven Clubs;
   Ten Diamonds; Nine Diamonds; Eight Diamonds; Seven Diamonds; Six Spades;
   Five Spades; Four Spades; Three Spades; Six Hearts; Five Hearts;
   Four Hearts; Three Hearts; Six Clubs; Five Clubs; Four Clubs; Three Clubs;
   Six Diamonds; Five Diamonds; Four Diamonds; Three Diamonds; Two Spades;
   Two Hearts; Two Clubs; Two Diamonds; BigJoker; LittleJoker]

The deck generated above was better than my previous deck. Hence, I actually had a deck of cards instead of a deck of suits.

I then refactored the code above to the following:

type Ace =   Spades | Hearts | Clubs | Diamonds
type King =  Spades | Hearts | Clubs | Diamonds
type Queen = Spades | Hearts | Clubs | Diamonds
type Jack =  Spades | Hearts | Clubs | Diamonds
type Ten =   Spades | Hearts | Clubs | Diamonds
type Nine =  Spades | Hearts | Clubs | Diamonds
type Eight = Spades | Hearts | Clubs | Diamonds
type Seven = Spades | Hearts | Clubs | Diamonds
type Six =   Spades | Hearts | Clubs | Diamonds
type Five =  Spades | Hearts | Clubs | Diamonds
type Four =  Spades | Hearts | Clubs | Diamonds
type Three = Spades | Hearts | Clubs | Diamonds
type Two =   Spades | Hearts | Clubs | Diamonds

type Deck =
    | Ace   of Ace 
    | King  of King 
    | Queen of Queen 
    | Jack  of Jack
    | Ten   of Ten 
    | Nine  of Nine 
    | Eight of Eight
    | Seven of Seven
    | Six   of Six 
    | Five  of Five 
    | Four  of Four 
    | Three of Three 
    | Two   of Two
    | BigJoker   
    | LittleJoker with

    static member Generate() = 
        [
        Ace Ace.Spades   ; King King.Spades   ; Queen Queen.Spades   ; Jack Jack.Spades  
        Ace Ace.Hearts   ; King King.Hearts   ; Queen Queen.Hearts   ; Jack Jack.Hearts  
        Ace Ace.Clubs    ; King King.Clubs    ; Queen Queen.Clubs    ; Jack Jack.Clubs   
        Ace Ace.Diamonds ; King King.Diamonds ; Queen Queen.Diamonds ; Jack Jack.Diamonds

        Ten Ten.Spades   ; Nine Nine.Spades   ; Eight Eight.Spades   ; Seven Seven.Spades  
        Ten Ten.Hearts   ; Nine Nine.Hearts   ; Eight Eight.Hearts   ; Seven Seven.Hearts  
        Ten Ten.Clubs    ; Nine Nine.Clubs    ; Eight Eight.Clubs    ; Seven Seven.Clubs   
        Ten Ten.Diamonds ; Nine Nine.Diamonds ; Eight Eight.Diamonds ; Seven Seven.Diamonds

        Six Six.Spades   ; Five Five.Spades   ; Four Four.Spades   ; Three Three.Spades  
        Six Six.Hearts   ; Five Five.Hearts   ; Four Four.Hearts   ; Three Three.Hearts
        Six Six.Clubs    ; Five Five.Clubs    ; Four Four.Clubs    ; Three Three.Clubs   
        Six Six.Diamonds ; Five Five.Diamonds ; Four Four.Diamonds ; Three Three.Diamonds

        Two Two.Spades
        Two Two.Hearts
        Two Two.Clubs    
        Two Two.Diamonds 

        BigJoker ; LittleJoker
        ]

let result = Deck.Generate()

The output was the same:

val result : Deck list =
  [Ace Spades; King Spades; Queen Spades; Jack Spades; Ace Hearts; King Hearts;
   Queen Hearts; Jack Hearts; Ace Clubs; King Clubs; Queen Clubs; Jack Clubs;
   Ace Diamonds; King Diamonds; Queen Diamonds; Jack Diamonds; Ten Spades;
   Nine Spades; Eight Spades; Seven Spades; Ten Hearts; Nine Hearts;
   Eight Hearts; Seven Hearts; Ten Clubs; Nine Clubs; Eight Clubs; Seven Clubs;
   Ten Diamonds; Nine Diamonds; Eight Diamonds; Seven Diamonds; Six Spades;
   Five Spades; Four Spades; Three Spades; Six Hearts; Five Hearts;
   Four Hearts; Three Hearts; Six Clubs; Five Clubs; Four Clubs; Three Clubs;
   Six Diamonds; Five Diamonds; Four Diamonds; Three Diamonds; Two Spades;
   Two Hearts; Two Clubs; Two Diamonds; BigJoker; LittleJoker]

I then revisited a blog post from last year, that Mark Seeman had influenced:

type Suit = | Spades
            | Clubs
            | Diamonds
            | Hearts
 
type Face = | Two  | Three | Four  | Five
            | Six  | Seven | Eight | Nine | Ten
            | Jack | Queen | King  | Ace
 
type Card = { Face:Face; Suit:Suit }
 
let suits = [Spades; Clubs; Diamonds; Hearts]
let faces = [Two; Three; Four; Five; Six; Seven; Eight; Nine; Ten;
                     Jack; Queen; King; Ace]
 
let deck = [for suit in suits do
                for face in faces do
                    yield {Face=face; Suit=suit}]

Here’s the output:

val deck : Card list =
  [{Face = Two;
    Suit = Spades;}; {Face = Three;
                      Suit = Spades;}; {Face = Four;
                                        Suit = Spades;}; {Face = Five;
                                                          Suit = Spades;};
   {Face = Six;
    Suit = Spades;}; {Face = Seven;
                      Suit = Spades;}; {Face = Eight;
                                        Suit = Spades;}; {Face = Nine;
                                                          Suit = Spades;};
   {Face = Ten;
    Suit = Spades;}; {Face = Jack;
                      Suit = Spades;}; {Face = Queen;
                                        Suit = Spades;}; {Face = King;
                                                          Suit = Spades;};
   {Face = Ace;
    Suit = Spades;}; {Face = Two;
                      Suit = Clubs;}; {Face = Three;
                                       Suit = Clubs;}; {Face = Four;
                                                        Suit = Clubs;};
   {Face = Five;
    Suit = Clubs;}; {Face = Six;
                     Suit = Clubs;}; {Face = Seven;
                                      Suit = Clubs;}; {Face = Eight;
                                                       Suit = Clubs;};
   {Face = Nine;
    Suit = Clubs;}; {Face = Ten;
                     Suit = Clubs;}; {Face = Jack;
                                      Suit = Clubs;}; {Face = Queen;
                                                       Suit = Clubs;};
   {Face = King;
    Suit = Clubs;}; {Face = Ace;
                     Suit = Clubs;}; {Face = Two;
                                      Suit = Diamonds;}; {Face = Three;
                                                          Suit = Diamonds;};
   {Face = Four;
    Suit = Diamonds;}; {Face = Five;
                        Suit = Diamonds;}; {Face = Six;
                                            Suit = Diamonds;};
   {Face = Seven;
    Suit = Diamonds;}; {Face = Eight;
                        Suit = Diamonds;}; {Face = Nine;
                                            Suit = Diamonds;};
   {Face = Ten;
    Suit = Diamonds;}; {Face = Jack;
                        Suit = Diamonds;}; {Face = Queen;
                                            Suit = Diamonds;};
   {Face = King;
    Suit = Diamonds;}; {Face = Ace;
                        Suit = Diamonds;}; {Face = Two;
                                            Suit = Hearts;}; {Face = Three;
                                                              Suit = Hearts;};
   {Face = Four;
    Suit = Hearts;}; {Face = Five;
                      Suit = Hearts;}; {Face = Six;
                                        Suit = Hearts;}; {Face = Seven;
                                                          Suit = Hearts;};
   {Face = Eight;
    Suit = Hearts;}; {Face = Nine;
                      Suit = Hearts;}; {Face = Ten;
                                        Suit = Hearts;}; {Face = Jack;
                                                          Suit = Hearts;};
   {Face = Queen;
    Suit = Hearts;}; {Face = King;
                      Suit = Hearts;}; {Face = Ace;
                                        Suit = Hearts;}]

> 

The code above did not account for jokers though.

The following model accounts for jokers in the deck:

type Suit = 
    | Spades | Hearts 
    | Clubs  | Diamonds

type Face =
    | Ace  | King  | Queen | Jack | Ten
    | Nine | Eight | Seven | Six  | Five | Four | Three | Two

type Joker = BigJoker | LittleJoker

type Standard = { Face:Face; Suit:Suit }

and Card = 
    | Card of Standard 
    | Wild of Joker

let suits = [Spades; Hearts; Clubs; Diamonds]

let faces = [Ace; King; Queen; Jack; Ten; Nine; Eight; Seven
             Six; Five; Four; Three; Two]

let deck = [for suit in suits do
                for face in faces do
                    yield Card { Face=face; Suit=suit }
           ] @ [Wild BigJoker
                Wild LittleJoker]

The output is the following:

val deck : Card list =
  [Card {Face = Ace;
         Suit = Spades;}; Card {Face = King;
                                Suit = Spades;}; Card {Face = Queen;
                                                       Suit = Spades;};
   Card {Face = Jack;
         Suit = Spades;}; Card {Face = Ten;
                                Suit = Spades;}; Card {Face = Nine;
                                                       Suit = Spades;};
   Card {Face = Eight;
         Suit = Spades;}; Card {Face = Seven;
                                Suit = Spades;}; Card {Face = Six;
                                                       Suit = Spades;};
   Card {Face = Five;
         Suit = Spades;}; Card {Face = Four;
                                Suit = Spades;}; Card {Face = Three;
                                                       Suit = Spades;};
   Card {Face = Two;
         Suit = Spades;}; Card {Face = Ace;
                                Suit = Hearts;}; Card {Face = King;
                                                       Suit = Hearts;};
   Card {Face = Queen;
         Suit = Hearts;}; Card {Face = Jack;
                                Suit = Hearts;}; Card {Face = Ten;
                                                       Suit = Hearts;};
   Card {Face = Nine;
         Suit = Hearts;}; Card {Face = Eight;
                                Suit = Hearts;}; Card {Face = Seven;
                                                       Suit = Hearts;};
   Card {Face = Six;
         Suit = Hearts;}; Card {Face = Five;
                                Suit = Hearts;}; Card {Face = Four;
                                                       Suit = Hearts;};
   Card {Face = Three;
         Suit = Hearts;}; Card {Face = Two;
                                Suit = Hearts;}; Card {Face = Ace;
                                                       Suit = Clubs;};
   Card {Face = King;
         Suit = Clubs;}; Card {Face = Queen;
                               Suit = Clubs;}; Card {Face = Jack;
                                                     Suit = Clubs;};
   Card {Face = Ten;
         Suit = Clubs;}; Card {Face = Nine;
                               Suit = Clubs;}; Card {Face = Eight;
                                                     Suit = Clubs;};
   Card {Face = Seven;
         Suit = Clubs;}; Card {Face = Six;
                               Suit = Clubs;}; Card {Face = Five;
                                                     Suit = Clubs;};
   Card {Face = Four;
         Suit = Clubs;}; Card {Face = Three;
                               Suit = Clubs;}; Card {Face = Two;
                                                     Suit = Clubs;};
   Card {Face = Ace;
         Suit = Diamonds;}; Card {Face = King;
                                  Suit = Diamonds;}; Card {Face = Queen;
                                                           Suit = Diamonds;};
   Card {Face = Jack;
         Suit = Diamonds;}; Card {Face = Ten;
                                  Suit = Diamonds;}; Card {Face = Nine;
                                                           Suit = Diamonds;};
   Card {Face = Eight;
         Suit = Diamonds;}; Card {Face = Seven;
                                  Suit = Diamonds;}; Card {Face = Six;
                                                           Suit = Diamonds;};
   Card {Face = Five;
         Suit = Diamonds;}; Card {Face = Four;
                                  Suit = Diamonds;}; Card {Face = Three;
                                                           Suit = Diamonds;};
   Card {Face = Two;
         Suit = Diamonds;}; Wild BigJoker; Wild LittleJoker]
Advertisements
5 comments
  1. Have you thought about the cards having a Suit and a Value? Something like type PlayingCard = Suit * Rank | Joker with type Suit = Spades|Hearts|Clubs|Diamonds type Rank = Two | Three| Four | Five| Six | Seven | Eight | Nine | Ten | Jack | Queen | King | Ace | Joker. Also you could use something like FSharpType.GetUnionCases to create the deck without have to specify all the cards in the deck. (That does use reflection which you may not want to do though). This is an interesting domain, and the funny thing is a deck of cards is somewhat useless without the game/rules that they are being used to play. Also in a game I wonder if you would need more information embedded into the PlayingCard types themselves like the number of eyes on a jack, or that Diamonds are always Red. I know you were just exploring the simple exercise of printing out the card deck, I’m just exploring other related use cases.

    • Hey Adam, I updated the post before seeing your feedback.

      • Ha…That’s funny. Maybe I missed that update. I like the 18 line version now. Only thing missing is the Jokers. That’s why I suggested type PlayingCard = Suit * Rank | Joker. Face is probably a better than Rank too. So that’s good. I couldn’t figure out what to call it.

  2. Yea… Thanks for the feedback.
    How’s your F# coming along?

    • I’m still learning. Reading a lot of FSharpForFunAndProfit.com That is a fountain of knowledge. Working on starting a blog. I let you know when I actually post a few things. It will be geared toward C# devs wanting to learn F#.

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: