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 BasicsSo 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 DesignsWe 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 CardCODE
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 executableCODE
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 CardsCODE
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 FormCreate 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
Project Files:
PlayingCards.zip ( 173.67k )
Number of downloads: 437