I'm trying to write a poker odds calculator. The idea behind it is that it brute forces it's way through all possible combinations of cards that might be played.
My current logic (stripped down for easy of reading) is as follows;
For i = 0 To unknownCards - 1
For j = 1 To 4
'pick suit
For k = 1 To 13
'pick number
'do other work here
Next
Next
Next
However this is wrong. It's only going to loop through the cards in order. For my purposes the order of the cards isn't important (eg. I don't want to deal with 2, 3, 4 and 4, 3, 2 separately), but it's important that I see every possible unique combination. I just can't wrap my head around how to do this? Any help or advice would be great.
P.S. I'm doing this in VB.net
There are 2,598,960 possible hands. This code generates all possible hands through brute force. It's faster / easier / better to just generate the combinations of 52 card indices than to worry about suits and ranks in your loop. Exactly what @ElizabethSQGoodman said, where there are 5 nested loops, each starting higher than the previous in my case.
I opted for a byte to hold each card, and a struct to hold the hand, for performance reasons. Then, later, you can figure out what card each is based on rules: the first 13 cards are clubs, the next 13 are diamonds, etc. (see getHumanReadableHand()). There you can also define Ace high or low (but not both, sorry!). The rank (A, 2, 3, ..., J, Q, K) is determined by the index modulo 13. The suit is determined by integer division of 13 into the index.
Module Module1
Sub Main()
Dim hands As New List(Of Hand)()
For c0 As SByte = 0 To 51
For c1 As SByte = c0 + 1 To 51
For c2 As SByte = c1 + 1 To 51
For c3 As SByte = c2 + 1 To 51
For c4 As SByte = c3 + 1 To 51
Dim hand = New Hand
hand.Card0 = c0
hand.Card1 = c1
hand.Card2 = c2
hand.Card3 = c3
hand.Card4 = c4
hands.Add(hand)
Next c4
Next c3
Next c2
Next c1
Next c0
Console.WriteLine("There are {0} possible hands.", hands.Count)
Dim rnd As New Random()
Dim r = rnd.Next(hands.Count - 1)
Console.WriteLine("Random hand: {0}", getHumanReadableHand(hands(r)))
Console.WriteLine("Value: {0}", getHandValue(hands(r)))
Console.ReadLine()
End Sub
Function getHumanReadableHand(hand As Hand) As String
Static suits = {"C", "D", "H", "S"}
Static ranks = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"}
Return String.Join(", ", hand.Cards.Select(Function(card) ranks(rank(card)) & suits(suit(card))))
End Function
Private Function rank(card As SByte) As SByte
Return card Mod 13
End Function
Private Function suit(card As SByte) As SByte
Return CSByte(card \ 13)
End Function
Function getHandValue(hand As Hand) As String
Dim cards = hand.Cards
If cards.Select(Function(card) rank(card)).Max() - cards.Select(Function(card) rank(card)).Min() = 4 AndAlso
cards.Select(Function(card) rank(card)).Distinct().Count = 5 AndAlso
cards.Select(Function(card) suit(card)).Distinct().Count = 1 Then
Return "Straight Flush"
ElseIf cards.OrderBy(Function(card) rank(card)).Take(4).Select(Function(card) rank(card)).Distinct().Count = 1 OrElse
cards.OrderBy(Function(card) rank(card)).Skip(1).Take(4).Select(Function(card) rank(card)).Distinct().Count = 1 Then
Return "Four of a Kind"
ElseIf cards.Select(Function(card) rank(card)).Distinct().Count = 2 Then
Return "Full House"
ElseIf cards.Select(Function(card) suit(card)).Distinct().Count = 1 Then
Return "Flush"
ElseIf cards.Select(Function(card) rank(card)).Max() - cards.Select(Function(card) rank(card)).Min() = 4 AndAlso
cards.Select(Function(card) rank(card)).Distinct().Count = 5 Then
Return "Straight"
ElseIf cards.OrderBy(Function(card) rank(card)).Take(3).Select(Function(card) rank(card)).Distinct().Count = 1 OrElse
cards.OrderBy(Function(card) rank(card)).Skip(1).Take(3).Select(Function(card) rank(card)).Distinct().Count = 1 OrElse
cards.OrderBy(Function(card) rank(card)).Skip(2).Take(3).Select(Function(card) rank(card)).Distinct().Count = 1 Then
Return "Three of a Kind"
ElseIf cards.Select(Function(card) rank(card)).Distinct().Count = 3 Then
Return "Two Pairs"
ElseIf cards.Select(Function(card) rank(card)).Distinct().Count = 4 Then
Return "One Pair"
Else
Return "Garbage"
End If
End Function
Structure Hand
Public Property Card0 As SByte
Public Property Card1 As SByte
Public Property Card2 As SByte
Public Property Card3 As SByte
Public Property Card4 As SByte
Public ReadOnly Property Cards As IEnumerable(Of SByte)
Get
Return New List(Of SByte)({Card0, Card1, Card2, Card3, Card4})
End Get
End Property
End Structure
End Module
Sample output:
There are 2598960 possible hands.
Random hand: 2C, 5C, 2D, 5S, KS
Value: Two Pairs
This code takes around 60 ms to generate all the possible hands on my machine.