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
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.
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.
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
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.
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
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.
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.
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.
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
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.
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.
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.
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.
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.
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)
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.
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.
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: 4979





MultiQuote






|