burningmace
asked on
Poker Hand Evaluation in VB.NET
I'm writing a game of Texas Hold'Em in VB.NET and C#, and need to build some logic on calculating the resulting poker hand from a player's two cards and the cards on the table (totalling 7 in Texas Hold'Em).
For example:
Players cards: 7H, JS
Cards on table: TD, 2H, 8H, 7C, 9S
In this case the player has a Jack high straight - 7H, 8H, 9S, TD, JS. The algorithm also has to discern that even though the player also has a pair (7H, 7C) the real hand is the straight.
I tried writing my own logic, but it ended up being 250+ lines of code by the time it could detect a royal flush. I've seen some code around that is meant to do it, but most of it is either in C/C++ or for other versions of poker.
I can write my own logic to discern a winner from a list of hands (i.e. that 3 of a kind beats two pair, and that an ace high straight beats a ten high straight). However, what I'm looking for is a function (or class) written in C# or VB.NET to return a poker hand (e.g. "Two Pair - 3s and Jacks" or "Four Aces") from a set of cards.
I've attached the VB.NET code for my structures and enums, but I can easily convert from another result format.
For example:
Players cards: 7H, JS
Cards on table: TD, 2H, 8H, 7C, 9S
In this case the player has a Jack high straight - 7H, 8H, 9S, TD, JS. The algorithm also has to discern that even though the player also has a pair (7H, 7C) the real hand is the straight.
I tried writing my own logic, but it ended up being 250+ lines of code by the time it could detect a royal flush. I've seen some code around that is meant to do it, but most of it is either in C/C++ or for other versions of poker.
I can write my own logic to discern a winner from a list of hands (i.e. that 3 of a kind beats two pair, and that an ace high straight beats a ten high straight). However, what I'm looking for is a function (or class) written in C# or VB.NET to return a poker hand (e.g. "Two Pair - 3s and Jacks" or "Four Aces") from a set of cards.
I've attached the VB.NET code for my structures and enums, but I can easily convert from another result format.
Private Enum CardSuits As Integer
Spade = 4
Club = 3
Diamond = 2
Heart = 1
End Enum
Private Enum CardValues As Integer
AceLow = 1
Two = 2
Three = 3
Four = 4
Five = 5
Six = 6
Seven = 7
Eight = 8
Nine = 9
Ten = 10
Jack = 11
Queen = 12
King = 13
Ace = 14
End Enum
Private Enum HandTypes As Integer
HighCard = 0
Pair = 1
TwoPair = 2
ThreeOfKind = 3
Straight = 4
Flush = 5
FullHouse = 6
FourOfKind = 7
StraightFlush = 8
End Enum
Private Structure Card
Public Suit As CardSuits
Public Value As CardValues
Public Overrides Function ToString() As String
Dim ret As String = ""
If Value <= CardValues.Ten Then
ret = Value
Else
Select Case Value
Case CardValues.Jack
ret = "Jack"
Case CardValues.Queen
ret = "Queen"
Case CardValues.King
ret = "King"
Case CardValues.Ace
ret = "Ace"
Case Else
ret = "Error"
End Select
End If
Select Case Suit
Case CardSuits.Club
ret &= " of Clubs"
Case CardSuits.Diamond
ret &= " of Diamonds"
Case CardSuits.Heart
ret &= " of Hearts"
Case CardSuits.Spade
ret &= " of Spades"
Case Else
ret &= " of Error"
End Select
Return ret
End Function
End Structure
ASKER
That doesn't really help, as that was what I was doing in the first place. The issue is that the code I wrote for checking hands became extremely complex and bulky within only a single check. For example, my code to check for a straight required the following:
1) Convert the list of 7 cards into a SortedList(Of Integer, Integer) in order to sort the values from lowest to highest. If Highest - Lowest > 4 then there's a possible straight.
2) Loop from highest to lowest, if Abs(c(n) - c(n-1)) = 1 then increment runCount, if not then runCount = 0
3) If runCount reaches 4 (e.g. in case of Q->J, J->10, 10->9, 9->8) then we have a straight.
4) If no straight was detected, replace all instances of Ace with AceLow and try again.
6) Return the list of cards that creates the highest score.
But we have a problem. What if there is a 2H,3H,4H,5H,6H straightf flush and a 3H,4H,5H,6H,7D straight? The algorithm will have completely missed the straight flush, which is the highest possilbe score for Hold'Em. This now means we have to re-engineer the algorithm:
1) Load the suits of the input cards into a frequency array.
2) Check if the frequency of a suit is 5 or more. Only one suit's frequency will ever be >4.
3) Discard any cards not matching that suit. Call the remaining cards FlushDeck
4) Check for straights within FlushDeck. If one exists, we have a straight flush. Return StraightFlush(HighCard).
5) If we don't have a straight, replace all Aces with AceLows and check for straights again. If one exists, we have a straight flush. Return StraightFlush(HighCard).
6) If we don't have a straight flush, we still have a flush. Note tha we have a Flush(HighCard).
7) Go on to check for Four of a Kind and Full House (another algorithm to write).
8) If we didn't have a 4Ok or an FH then return StraightFlush(HighCard)
And that's the tip of the iceberg. I still would have to write logic for six other hand types. On top of that, I'd have to work on determining winners when the top two hands match (e.g. Twos and Fives, Threes and Fives or Sevens and Jacks with 8 kicker, Sevens and Jacks with 5 kicker). I also have to code logic for draws (e.g. both players have 3,4,5,6,7 straights).
What I need is a pre-existing algorithm that can do it for me. They definately exist, but they seem to have eluded me so far.
1) Convert the list of 7 cards into a SortedList(Of Integer, Integer) in order to sort the values from lowest to highest. If Highest - Lowest > 4 then there's a possible straight.
2) Loop from highest to lowest, if Abs(c(n) - c(n-1)) = 1 then increment runCount, if not then runCount = 0
3) If runCount reaches 4 (e.g. in case of Q->J, J->10, 10->9, 9->8) then we have a straight.
4) If no straight was detected, replace all instances of Ace with AceLow and try again.
6) Return the list of cards that creates the highest score.
But we have a problem. What if there is a 2H,3H,4H,5H,6H straightf flush and a 3H,4H,5H,6H,7D straight? The algorithm will have completely missed the straight flush, which is the highest possilbe score for Hold'Em. This now means we have to re-engineer the algorithm:
1) Load the suits of the input cards into a frequency array.
2) Check if the frequency of a suit is 5 or more. Only one suit's frequency will ever be >4.
3) Discard any cards not matching that suit. Call the remaining cards FlushDeck
4) Check for straights within FlushDeck. If one exists, we have a straight flush. Return StraightFlush(HighCard).
5) If we don't have a straight, replace all Aces with AceLows and check for straights again. If one exists, we have a straight flush. Return StraightFlush(HighCard).
6) If we don't have a straight flush, we still have a flush. Note tha we have a Flush(HighCard).
7) Go on to check for Four of a Kind and Full House (another algorithm to write).
8) If we didn't have a 4Ok or an FH then return StraightFlush(HighCard)
And that's the tip of the iceberg. I still would have to write logic for six other hand types. On top of that, I'd have to work on determining winners when the top two hands match (e.g. Twos and Fives, Threes and Fives or Sevens and Jacks with 8 kicker, Sevens and Jacks with 5 kicker). I also have to code logic for draws (e.g. both players have 3,4,5,6,7 straights).
What I need is a pre-existing algorithm that can do it for me. They definately exist, but they seem to have eluded me so far.
"The algorithm will have completely missed the straight flush, which is the highest possilbe score for Hold'Em."
No, look again.
"Then check in reverse order the first score that returns positive is the one you keep."
So, check for royal flush, if no, check for straight flush, if no, check for 4 of a kind, if no, check for flush, etc. and stop the first time any check returns a positive result.
No, look again.
"Then check in reverse order the first score that returns positive is the one you keep."
So, check for royal flush, if no, check for straight flush, if no, check for 4 of a kind, if no, check for flush, etc. and stop the first time any check returns a positive result.
sorry, shouldn't have hit submit so soon
ranking them isn't so hard, write each individual rank routine so it returns the hand in rank order.
that is, if you have 7c7sKhKd8c as your best hand it would return 2 outputs
KhKd7s7c8c (as a string or array or whatever you want)
to compare this hand to another 2 pair ranked hand you would simply walk through the list and compare each of the 5 cards until one hand beat the other.
if the hand does not match the rank, then the output would be null
ranking them isn't so hard, write each individual rank routine so it returns the hand in rank order.
that is, if you have 7c7sKhKd8c as your best hand it would return 2 outputs
KhKd7s7c8c (as a string or array or whatever you want)
to compare this hand to another 2 pair ranked hand you would simply walk through the list and compare each of the 5 cards until one hand beat the other.
if the hand does not match the rank, then the output would be null
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Highcard
Pair
TwoPair
ThreeofAKind
Straight
Flush
FullHouse
FourofaKind
for the 2 biggest hands...
Straight Flush - simply check if both straight and flush
Royal Flush - check if straight and flush and highcard=Ace
Then check in reverse order the first score that returns positive is the one you keep.
Yes, this could mean you could triple check for a straight, if that's a significant concern then save those check results in a variable and return that value instead of re-evaluating.