School Assignment? Project Due Tomorrow? Chat LIVE With A Programming Expert!

Welcome to Dream.In.Code
Become an Expert!

Join 307,096 Programmers for FREE! Get instant access to thousands of experts, tutorials, code snippets, and more! There are 2,029 people online right now. Registration is fast and FREE... Join Now!




Playing Cards

 
Reply to this topicStart new topic

> Playing Cards, How to create a set of playing cards. (Net 3.5+)

Rating  5
AdamSpeight2008
Group Icon



post 28 Nov, 2008 - 01:50 PM
Post #1


This tutorial is on how to create your pack of cards, which you can use a basis for other card games.
It'll touch on the aspects of Object Orientated Design (O.O.D.)
You'll also be using LINQ, RegEx (Regular Expressions) and creating custom sorts.

The Basics
So what is a pack of cards?
A pack of cards is collection of 52 playing cards (minus the jokers).
It can be shuffled, to rearrange to order of the cards.
Playing cards can dealt from it.

So what is a playing cards? What make each playing card unique?
A playing card is a card which is made unique by it face value and suit value.

Object Designs
We know have two objects to design (Playing Card and Deck of Cards)
Lets concentrate on the Playing Card first, as need Playing Cards to build deck.

Coding the Playing Card
CODE

Public Class PlayingCard
Dim Suits() As String = {"S", "D", "C", "H"}
Dim Faces() As String = {"2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A"}
Private mFace As String
Private mSuit As String
Protected cv As Integer = 0
Protected sv As Integer = 0

Public ReadOnly Property Suit() As String
  Get
   Return mSuit
  End Get
End Property

Public ReadOnly Property Face() As String
  Get
   Return mFace
  End Get
End Property

Public ReadOnly Property CardValue() As Integer
  Get
   Return cv
  End Get
End Property

Public ReadOnly Property SuitValue() As Integer
  Get
   Return sv
  End Get
End Property

You'll see have defined four (4) read-only properties (Suit, Face, FaceValue, SuitValue).

Why read-only? Cos we don't won't people changing them have been set.
So how do we set them in the first place? We set them when create (initialization) a new playing card object.
Which is what the next sub routine does.
CODE

Public Sub New(ByVal tSuit As String, ByVal tFaceValue As String)
  mFace = tFaceValue
  mSuit = tSuit
  cv = Array.IndexOf(Of String)(Faces, mFace) '+ 1
  sv = Array.IndexOf(Of String)(Suits, mSuit) ' + 1
End Sub


The next function returns a string contain the value of the card e.g. D4 for Four of Diamonds.
CODE

Public Overrides Function ToString() As String
  Return mSuit & mFace
End Function


We don't want to just display text strings for each card, lets draw them a cards.
Note: Each card is a gif contained within a folder called \cards_gif, which must be in the some folder as the executable
CODE


Public Sub DrawCard(ByRef g As Graphics, ByRef x As Integer, ByRef y As Integer)
  g.CompositingQuality = Drawing2D.CompositingQuality.GammaCorrected
  g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
  g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
  g.DrawImage(New System.Drawing.Bitmap(System.Windows.Forms.Application.StartupPath & "\cards_gif\" & Me.ToString & ".gif"), x, y, 46, 64) '71, 96)
End Sub


We also want to be able to compare our playing card and since the VB.Net Framework doesn't know how to sort card, we have tell it.
CODE

Public Class CardComparer
  Implements IComparer(Of PlayingCard)
  Public Function Compare(ByVal x As PlayingCard, ByVal y As PlayingCard) As Integer Implements System.Collections.Generic.IComparer(Of PlayingCard).Compare
   Dim RANKS() As String = {"A", "K", "Q", "J", "T", "9", "8", "7", "6", "5", "4", "3", "2"}
   Dim xi = Array.IndexOf(Of String)(RANKS, CStr(x.ToString)(1))
   Dim Yi = Array.IndexOf(Of String)(RANKS, CStr(y.ToString)(1))
   Return xi.CompareTo(Yi)
  End Function
End Class
End Class



Coding the Deck of Cards
CODE

Public Class DeckOfCards
Dim r As New Random(Now.Millisecond)
Dim mDeck As New List(Of PlayingCard)
Dim Suits() As String = {"S", "D", "C", "H"}
Dim Faces() As String = {"2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A"}

Public Sub New()
  For Each Suit As String In Suits
   For Each Face As String In Faces.Reverse
    mDeck.Add(New PlayingCard(Suit, Face))
   Next
  Next
End Sub



We have just created a collection of playing cards. Simple isn't when you O.O.D. principles.
So we have a deck of cards but at the moment we can't do much with it, lets change that.

CODE

Public Sub ShuffleCards()
  Dim rc As PlayingCard
  Dim rn As Integer
  For i As Integer = 0 To 52
   rn = r.Next(0, 51) Mod mDeck.Count
   rc = mDeck(rn)
   mDeck.RemoveAt(rn)
   mDeck.Add(rc)
  Next
End Sub

Now it can be shuffled.
CODE

Public ReadOnly Property TakeCard() As PlayingCard
  Get
   Dim RemovedCard As PlayingCard = mDeck(0)
   mDeck.RemoveAt(0)
   Return RemovedCard
  End Get
End Property

Now we can take a card from the deck.

The next property displays the state of the deck. So if you want to make cheating harder remove it.
CODE

Public ReadOnly Property DeckState() As String
  Get
   Dim d As String = ""
   For Each m As PlayingCard In mDeck
    d &= m.ToString
   Next
   Return d
  End Get
End Property

End Class


Putting the deck of cards to use.
So now we have deck of playing cards let do something with them.
How about create a several hand of cards and sort them as if they were poker hands, with the best hand first.

Coding the hands
CODE

Public Class HandOfCards
Protected mHand As List(Of PlayingCard)

Public Sub New()
  mHand = New List(Of PlayingCard)
End Sub

Public Sub AddCard(ByVal card As PlayingCard)
  If mHand.Count = 5 Then Exit Sub
  mHand.Add(card)
End Sub

So now it is possible to place cards into a hand.
CODE

Public Function Card(ByRef index As Integer) As PlayingCard
  If index < 0 Or index > 4 Then Return Nothing
  Return mHand(index)
End Function

Refer to a card by its position in a hand.

Sort the hand by using Card Comparer we wrote earlier.
CODE

Public Sub SortHand()
  mHand.Sort(New PlayingCard.CardComparer)
End Sub

Public Overrides Function ToString() As String
  Dim s As String = ""
  mHand.Sort(New PlayingCard.CardComparer)
  For Each pc As PlayingCard In mHand
   s &= pc.ToString
  Next
  Return s
End Function

Public Sub DrawHand(ByRef g As Graphics, ByRef x As Integer, ByRef y As Integer)
  For i As Integer = 0 To 4
   mHand(i).DrawCard(g, 10 + 25 * i, y)

  Next
  g.DrawString(NameOfHand, New Font("Tahoma", 16, FontStyle.Regular, GraphicsUnit.Pixel, 0), Brushes.Black, 175, y + 25)
End Sub

Also we can return the hand as a string or display it on a graphic surface.

The next function is a little complex, as it works out the distribution of the face values in the hand. Orders them by quantity (most to least), them by face value.
To do this we use LINQ.
CODE

Public Function ValueOfHand() As String
  Dim DictionaryOfAlpha As New Dictionary(Of Char, Integer)
  Dim Alpha() As Char = "MLKJIHGFEDCBA"
  For I As Integer = 0 To Alpha.Count - 1 : DictionaryOfAlpha.Add(Alpha(I), 0) : Next
  For i As Integer = 0 To mHand.Count - 1 : DictionaryOfAlpha(Alpha(mHand(i).CardValue)) += 1 : Next
  Dim ReorderDictionary As IEnumerable = From DictionaryEntry In DictionaryOfAlpha _
                          Select DictionaryEntry _
                          Order By DictionaryEntry.Value Descending, _
                          DictionaryEntry.Key Ascending
  ValueOfHand = ""
  For Each Entry In ReorderDictionary : ValueOfHand &= Strings.StrDup(Entry.Value, Entry.Key) : Next
End Function


The next function returns a string containing the distribution of card suits in the hand.

CODE

Public Function SuitValueOfHand() As String
  Dim n(3) As Integer
  For i As Integer = 0 To mHand.Count - 1
   n(mHand(i).SuitValue) += 1
  Next
  SuitValueOfHand = n(0).ToString & n(1).ToString & n(2).ToString & n(3).ToString
  Return SuitValueOfHand
End Function

The next the Rank Distribution.
CODE

Public Function RankDistrubtion() As String
  Dim n(12) As Integer
  For i As Integer = 0 To mHand.Count - 1 : n(mHand(i).CardValue) += 1 : Next
  RankDistrubtion = ""
  For i As Integer = 12 To 0 Step -1 : RankDistrubtion &= n(i).ToString : Next
  Return RankDistrubtion
End Function


The next function returns a string containing the poker hand of the hand of cards (eg Royal Flush or Three of a Kind etc)
CODE

Public Function NameOfHand() As String
  Dim f As String = RankDistrubtion()
  Dim s As String = SuitValueOfHand.ToString
  Select Case True
   Case System.Text.RegularExpressions.Regex.Match(f, "0*111110*").Success
    ' Contains 5 Consectative Cards
    If System.Text.RegularExpressions.Regex.Match(s, "0*50*").Success Then
     ' Contains 5 Cards of same Suit
     If f = "1111100000000" Then
      Return "01. Royal Flush"
     Else
      Return "02. Straight Flush"
     End If
    Else
     Return "06. Straight"
    End If
   Case System.Text.RegularExpressions.Regex.Match(f, "[01]*4[01]*").Success : Return "03. Four of a Kind"
   Case System.Text.RegularExpressions.Regex.Match(f, "(0*20*30*)|(0*30*20*)").Success() : Return "04. Full House"
   Case System.Text.RegularExpressions.Regex.Match(f, "0*10*10*10*10*10*").Success
    If System.Text.RegularExpressions.Regex.Match(s, "0*50*").Success Then Return "05. Flush"
   Case System.Text.RegularExpressions.Regex.Match(f, "[01]*3[01]*").Success : Return "07. Three Of a Kind"
   Case System.Text.RegularExpressions.Regex.Match(f, "[01]*2[01]*2[01]*").Success() : Return "08. Two Pair"
   Case System.Text.RegularExpressions.Regex.Match(f, "[01]*2[01]*").Success : Return "09. Pair"
  End Select
  Return "10. High Card"
End Function

This is achieved by looking for patterns in the distribution of both the card values of suits. The best object to use for pattern matching is a RegEx or Regular Expression.
So a royal flush is SASKSQSJST or 1111100000000 for the value distribution and "0*50*" for the suit distribution.

Since VB.Net doesn't know how to compare to hands of cards, we have to tell it how.
CODE

Public Class HandComparer
  Implements IComparer(Of HandOfCards)
  Public Function Compare(ByVal x As HandOfCards, ByVal y As HandOfCards) As Integer Implements System.Collections.Generic.IComparer(Of HandOfCards).Compare
   Dim c As Integer = x.NameOfHand.Substring(0, 2).CompareTo(y.NameOfHand.Substring(0, 2))
   Select Case c
    Case -1, 1 : Return c
    Case 0 : Return String.Compare(x.ValueOfHand, y.ValueOfHand)
   End Select
  End Function
End Class
End Class


The Demonstration Form

Create a windows form. Add button then insert this code.
CODE

Public Class Form1
Dim TheDeck As DeckOfCards
Dim Hands(NumberOfHands - 1) As HandOfCards
Const NumberOfHands As Integer = 10


Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
  If TheDeck IsNot Nothing Then
   For i = 0 To NumberOfHands - 1
    Hands(i).DrawHand(e.Graphics, 25, 25 + i * 65)
   Next
  End If


End Sub


Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  Console.WriteLine("--------")


  TheDeck = New DeckOfCards
  TheDeck.ShuffleCards()
  TheDeck.ShuffleCards()
  'TheDeck.ShuffleCards()
  ' TheDeck.ShuffleCards()
  For i = 0 To NumberOfHands - 1
   Hands(i) = New HandOfCards
   For c As Integer = 0 To 4
    Hands(i).AddCard(TheDeck.TakeCard)
   Next
   Hands(i).SortHand()
  Next
  Array.Sort(Hands, New HandOfCards.HandComparer)
  For i = 0 To NumberOfHands - 1
   Console.WriteLine("{0}" & vbTab & "[{1}] = {2}" & vbTab & " {3}" & vbTab & "[{4}]" & vbTab & " ({5})", i + 1, Hands(i).ToString, Hands(i).SuitValueOfHand, Hands(i).RankDistrubtion, Hands(i).NameOfHand, Hands(i).ValueOfHand)
  Next
  'Console.WriteLine("Deck [{0}]", TheDeck.DeckState)
  'Console.WriteLine("Deck [{0}]", TheDeck.DeckState)
  'TheDeck.ShuffleCards()
  'Console.WriteLine("Deck [{0}]", TheDeck.DeckState)
  Me.Refresh()
End Sub
End Class


Run.

So now you're Poker Hand Sorting Code Ninja ph34r.gif

Project Files: Attached File  PlayingCards.zip ( 173.67k ) Number of downloads: 437
Go to the top of the page
+Quote Post


Register to Make This Ad Go Away!

jimdandy75
**



post 3 May, 2009 - 06:27 AM
Post #2
WOW!!!!! That is awesome..... Thanks icon_up.gif icon_up.gif icon_up.gif biggrin.gif biggrin.gif biggrin.gif
I see alot of card game questions on the boards they should be directed here.
I tried tweeking this a little to see if I could get it to deal out a 7 card hand and
return the best 5 card poker hand, like in Texas Hold Em. But my coding skills are
weak crazy.gif Could you possibly extend this tutorial to show that?
Pretty, pretty please???? icon_up.gif icon_up.gif icon_up.gif biggrin.gif biggrin.gif biggrin.gif

This post has been edited by jimdandy75: 3 May, 2009 - 10:25 AM
Go to the top of the page
+Quote Post

AdamSpeight2008
Group Icon



post 3 May, 2009 - 03:32 PM
Post #3
Texas-Holdem like play.


Add this to the DeckOfCards Class
CODE

Public ReadOnly Property CardsLeft() As Integer
  Get
   Return mDeck.Count

  End Get
End Property

modified DrawHand also.
CODE


Public Sub DrawHand(ByRef g As Graphics, ByRef x As Integer, ByRef y As Integer,optional ByRef t As Boolean=True)
  For i As Integer = 0 To mHand.Count - 1
   mHand(i).DrawCard(g, 10 + 25 * i, y)

  Next
  If t Then g.DrawString(NameOfHand, New Font("Tahoma", 16, FontStyle.Regular, GraphicsUnit.Pixel, 0), Brushes.Black, 175, y + 25)
End Sub


Create a new project or form, if you're creating a new form in the tutorial project remember to set the startup form to the new one

Add a couple of buttons;- Hand (Players Cards) & Dealt (River cards)

CODE

Public Class TexasHoldem
Dim TheDeck As DeckOfCards
Dim Hands(NumberOfHands - 1) As HandOfCards
Dim Dealt As HandOfCards
Dim BestHandIs As HandOfCards



Const NumberOfHands As Integer = 1
Dim a As New List(Of String)
Dim b As New List(Of String)

Private Sub TexasHoldem_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
  MchooseN(3, 0, 4, a)
  MchooseN(4, 0, 4, b)

End Sub

Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
  If TheDeck IsNot Nothing Then
   For i = 0 To NumberOfHands - 1
    Hands(i).DrawHand(e.Graphics, 25, 100 + i * 65, False)
    If Dealt IsNot Nothing Then Dealt.DrawHand(e.Graphics, 25, 175 + i * 65, False)
    If lh IsNot Nothing AndAlso lh.Count > 0 Then
     BestHandIs.DrawHand(e.Graphics, 25, 250 + i * 65, True)
    End If
   Next
  End If


End Sub

Private Function BestHand(ByRef meCards As HandOfCards, ByRef rc As HandOfCards) As HandOfCards
  If meCards Is Nothing Then Return Nothing
  If rc Is Nothing Then Return Nothing

  lh.Clear()

  Dim an As HandOfCards
  For Each aa As String In a
   an = New HandOfCards

   For i As Integer = 0 To 2
    an.AddCard(rc.Card(Integer.Parse(aa(i))))
   Next
   an.AddCard(meCards.Card(0))
   an.AddCard(meCards.Card(1))
   an.SortHand()
   lh.Add(an)

  Next
  For j As Integer = 0 To 1
   For Each aa As String In b
    an = New HandOfCards

    For i As Integer = 0 To 3
     an.AddCard(rc.Card(Integer.Parse(aa(i))))
    Next
    an.AddCard(meCards.Card(j))
    an.SortHand()
    lh.Add(an)
   Next
  Next
  lh.Sort(hc)

  Return lh(0)

End Function

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  If TheDeck Is Nothing Then newdeck()

  If TheDeck.CardsLeft < 5 Then newdeck()

  TheDeck = New DeckOfCards
  TheDeck.ShuffleCards()
  TheDeck.ShuffleCards()
  For i As Integer = 0 To NumberOfHands - 1
   Hands(i) = New HandOfCards

   For c As Integer = 0 To 1
    Hands(i).AddCard(TheDeck.TakeCard)
   Next
   Hands(i).SortHand()

  Next
  BestHandIs = BestHand(Hands(0), Dealt)

  Me.Refresh()

End Sub
Private Sub newdeck()

  TheDeck = New DeckOfCards
  TheDeck.ShuffleCards()
  TheDeck.ShuffleCards()

End Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
  If TheDeck.CardsLeft < 5 Then newdeck()
  Dealt = New HandOfCards

  For i As Integer = 0 To 4
   Dealt.AddCard(TheDeck.TakeCard)
  Next
  Dealt.SortHand()
  BestHandIs = BestHand(Hands(0), Dealt)

  Me.Refresh()

End Sub
Dim hc As New HandOfCards.HandComparer
Dim lh As New List(Of HandOfCards)

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
  BestHandIs = BestHand(Hands(0), Dealt)
  Me.Refresh()

End Sub


Private Sub MchooseN(ByRef N As Integer, ByRef first_allowed As Integer, ByRef last_allowed As Integer, ByRef solutions As List(Of String))
  Dim i As Integer = 0
  Dim txt As New System.Text.StringBuilder
  Dim partial_solutions As List(Of String)
  Dim fapo As Integer = first_allowed + 1
  Dim lmf As Integer = last_allowed - first_allowed + 1
  ' Change spacer to alter character between numbers.
  Const Spacer As String = ""
  ' If N < 1, we don't need to pick any more items.
  ' If N > last_allowed - first_allowed + 1, there are too few items for a solution.
  ' If N = last_allowed - first_allowed + 1,' all the items must be in the solution.
  Select Case True
   Case N < 1 ' We don't need to pick any more, Do nothing.
   Case N > lmf ' There are not enough items. Do nothing.
   Case N = lmf
    ' All the items must be in the solution.
    txt.Append(Format$(first_allowed))
    For i = fapo To last_allowed
     txt.Append(Format$(i) & Spacer)
    Next i
    solutions.Add(txt.ToString)
   Case Else
    'Get solutions containing first_allowed.
    partial_solutions = New List(Of String)
    If N = 1 Then
     partial_solutions.Add("")
    Else
     MchooseN(N - 1, fapo, last_allowed, partial_solutions)
    End If
    ' Add first_allowed to make the full solutions.
    For i = 0 To partial_solutions.Count - 1
     solutions.Add(Format$(first_allowed) & partial_solutions(i))
    Next i
    ' Get solutions not containing first_allowed.
    partial_solutions = New List(Of String)
    MchooseN(N, fapo, last_allowed, partial_solutions)
    ' Add these to the solutions.
    solutions.AddRange(partial_solutions)
  End Select
End Sub
End ClassEnd Class


Some of you may have noticed that this is only a players hand.
It is left as an exercise for the reader to extend to multiple players.

This post has been edited by AdamSpeight2008: 3 May, 2009 - 04:31 PM
Go to the top of the page
+Quote Post

jimdandy75
**



post 3 May, 2009 - 04:56 PM
Post #4
Yup, you da man!!!!! Thank you, it works great biggrin.gif
icon_up.gif icon_up.gif icon_up.gif icon_up.gif icon_up.gif icon_up.gif icon_up.gif icon_up.gif icon_up.gif icon_up.gif icon_up.gif icon_up.gif icon_up.gif icon_up.gif icon_up.gif
Go to the top of the page
+Quote Post

DataPriest
**



post 20 Aug, 2009 - 10:21 PM
Post #5
Good work man. Loved the code. wub.gif
Go to the top of the page
+Quote Post


Fast ReplyReply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 


Lo-Fi Version Time is now: 11/21/09 11:51AM

Live Help!

Be Social

Dream.In.Code RSS Feed Dream.In.Code LinkedIn Group Follow Us On Twitter Fan Us On Facebook

Tutorials

Programming

Web Development

Reference Sheets

Code Snippets

DIC Chatroom

Bye Bye Ads

Monthly Drawing

Thumb Drive

Top Contributors

Top 10 Kudos This Month