Need a Faster Poker Evaluation Method
I have been working on a poker calculator with the ultimate goal of testing for both optimal play methods and maximal play methods using opponent modeling. Right now, I have a way to evaluate and compare 7-card poker hands with reasonable speed (~100K compares/s). However, I know it can be done faster; I currently delegate larger enumerations to an external program ("pokenum"). The speed difference is significant: the other program can compare two 2-card pockets in 0.15s, while my program takes about 15-20s. I would like to achieve similar speeds using native VB code, but I am also willing to write a C++/ASM module if necessary.
My current method is based on something I found online. It combines the card rank/suit bits using the OR operation such that it can quickly detect a flush or a straight. For other hands, it multiplies the prime numbers associated with each rank (order independent) and uses a static perfect double hash table.
In order to improve performance, I am trying to figure out a way to quickly evaluate the seven card hand using the card indexes (1-52) directly. There are three ways I thought of to do it:
1) Create a tree to store all the ~134M values (large memory requirement) and use the indexes sorted low to high as vectors
2) Create a similar tree for ranks/suits and combine them somehow (haven't figured out how to do it yet)
3) Use the prime multiplication and hash table approach again (this would still require ~134M entries however)
Is there a fast and straightforward way to use seven card indexes to translate to a hand ranking without using a ton of memory?
Re: Need a Faster Poker Evaluation Method
The basic question in this situatation: have you compiled your program with all optimizations turned on? It can have major differences in speed, especially when a lot of math and arrays are in use.
I can't say anything about the poker calculations; you might have to post code so it would be possible to see where you might go wrong speed wise. I guess there aren't many here that have done poker related programming; I haven't.
Re: Need a Faster Poker Evaluation Method
Let's see what you already have.
Re: Need a Faster Poker Evaluation Method
Here's the code for the function. Most of it simply initializes the tables for evaluation. The actual code to eval the 7 cards is not very long. I have tried it in compiled mode and it works about 2x the speed (which is normal). In my new module, I will be using a type rather than an array of cards because I expect a small performance boost. I don't care about the speed of the init part, but I would like to speed up the hand eval significantly. Thanks.
Code:
Public Function RankHand(Card() As Long) As HANDRANKDATA
Static Init As Boolean
Dim x As Long, S As Long
If Init = False Then
Static BIT(0 To 30) As Long
If BIT(0) = 0 Then ' init the bit mask
For x = 0 To 30
BIT(x) = (2 ^ x)
Next
End If
Static BITCOUNT(0 To 8192 - 1) As Long ' highest bit is bit 12
If BITCOUNT(1) = 0 Then ' init the bit counts
For x = 0 To 8192 - 1
For B = 0 To 12
If (x And BIT(B)) Then
BITCOUNT(x) = BITCOUNT(x) + 1 ' max is 13
End If
Next
Next
End If
' handles any flushes
Static FlushQuickTable(0 To 8191) As HANDRANKDATA ' FLUSHDATA ' 13-bit check
For x = 0 To 8191
If (BITCOUNT(x) >= 5) And (BITCOUNT(x) <= 7) Then
' flush (default over s.flush)
FlushRank = 0
BCount = 0
For B = 12 To 0 Step -1
If (x And BIT(B)) Then
' this rank (b) is of the flush suit
' extract 5 highest ranks of the suit
FlushRank = FlushRank Or BIT(B)
BCount = BCount + 1
If BCount >= 5 Then Exit For ' five highest cards extracted
End If
Next
FlushQuickTable(x).HandType = Flush
FlushQuickTable(x).AlphaBits = FlushRank ' flushrank contains the highest flush mask
FlushQuickTable(x).BetaBits = 0
Call HandRankNumber(FlushQuickTable(x))
' test for s.flush, which would replace the flush mask
For B = 12 To 3 Step -1
For n = 0 To 4 ' need 5 consecutive bits for a straight
If (B = 3) And (n = 4) Then ' test for Ace in A-5 straight
If (x And BIT(12)) = 0 Then Exit For
Else
If (x And BIT(B - n)) = 0 Then Exit For
End If
Next
If (n > 4) Then ' straight flush
FlushQuickTable(x).HandType = StraightFlush
FlushQuickTable(x).AlphaBits = B ' high rank of the flush
FlushQuickTable(x).BetaBits = 0
Call HandRankNumber(FlushQuickTable(x))
Exit For
End If
Next
Else
' no flush, handtype = 0
FlushQuickTable(x).HandType = Invalid
FlushQuickTable(x).AlphaBits = 0
FlushQuickTable(x).BetaBits = 0
Call HandRankNumber(FlushQuickTable(x))
End If
Next
Dim Key() As Double ' keys for prime product combinations
ReDim Key(1 To 49205) ' 49205 total unique keys (50388 tested combinations)
Dim ST As String
Open App.Path & "\sortedkeys.txt" For Binary As #1
ST = String(LOF(1), 0)
Get #1, , ST
Close #1
S2 = Split(ST, vbCrLf)
ReDim Key(1 To UBound(S2) + 1)
For x = 0 To UBound(S2)
Key(x + 1) = Val(S2(x))
Next
Dim Prime() As Variant
Prime = Array(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41) ' primes for ranks
' all keys have 7 ranks. extract and sort the ranks
Dim KeyFactor() As Long
ReDim KeyFactor(1 To UBound(Key), 1 To 7) As Long
For x = 1 To UBound(Key)
TempKey = Key(x)
FactorIndex = 0
For R = 12 To 0 Step -1
For n = 1 To 4
If fmod(TempKey, Prime(R)) = 0 Then
FactorIndex = FactorIndex + 1
KeyFactor(x, FactorIndex) = R ' Prime(r)
TempKey = Fix(TempKey / Prime(R)) ' should still be an integer
End If
Next
Next
Next
' we have the 7 ranks in KeyProduct(x, 1-7)
Dim KeyRankCount() As Long
ReDim KeyRankCount(1 To UBound(Key), 0 To 12)
Dim KeyHandRank() As HANDRANKDATA
ReDim KeyHandRank(1 To UBound(Key))
' * = handled by above method
' NAME, HANDTYPE, PRIMARY, SECONDARY
' * StraightFlush, 9, high rank bit, 0
' FourOfAKind, 8, quad rank bit, kicker rank bit
' FullHouse, 7, trip rank bit, pair rank bit
' * Flush, 6, high flush bits (5), 0
' Straight, 5, high rank bit, 0
' ThreeOfAKind, 4, trip rank bit, kicker rank bits (2)
' TwoPair, 3, pair rank bits (2), kicker rank bit
' OnePair, 2, pair rank bit, kicker rank bits (3)
' HighCard, 1, kicker rank bits (5), 0
' Invalid, 0, 0, 0 [invalid hand]
Re: Need a Faster Poker Evaluation Method
(continuing previous code)
Code:
' evaluate each key as a hand result (to use in hash table)
For x = 1 To UBound(Key)
KeyHandRank(x).HandType = Invalid ' default value until hand found
Dim RankCount(0 To 12) As Long ' temp disposable array for tests
For n = 0 To 12
RankCount(n) = 0
Next
For n = 1 To 7
KeyRankCount(x, KeyFactor(x, n)) = KeyRankCount(x, KeyFactor(x, n)) + 1
RankCount(KeyFactor(x, n)) = RankCount(KeyFactor(x, n)) + 1
Next
Dim VectorCount(0 To 4) As Long
For n = 0 To 4
VectorCount(n) = 0
Next
For R = 12 To 0 Step -1
VectorCount(KeyRankCount(x, R)) = VectorCount(KeyRankCount(x, R)) + 1
Next
' straight test (if there is a straight, then it must be the high hand)
For R = 12 To 3 Step -1
For n = 0 To 4 ' need 5 consecutive bits for a straight
If (R = 3) And (n = 4) Then ' test for Ace in A-5 straight
If KeyRankCount(x, 12) = 0 Then Exit For
Else
If KeyRankCount(x, (R - n)) = 0 Then Exit For
End If
Next
If (n > 4) Then ' straight
KeyHandRank(x).HandType = Straight
KeyHandRank(x).AlphaBits = R
KeyHandRank(x).BetaBits = 0
Call HandRankNumber(KeyHandRank(x))
Exit For
End If
Next
If (KeyHandRank(x).HandType = Invalid) Then ' no straight, continue search
If (VectorCount(4) > 0) Then
' quads
r1 = HighRank(RankCount(), 4)
r2 = HighRank(RankCount(), 1)
KeyHandRank(x).HandType = FourOfAKind
KeyHandRank(x).AlphaBits = r1
KeyHandRank(x).BetaBits = r2
Call HandRankNumber(KeyHandRank(x))
ElseIf (VectorCount(3) = 2) Or ((VectorCount(3) = 1) And VectorCount(2) >= 1) Then
' full house
r1 = HighRank(RankCount(), 3)
r2 = HighRank(RankCount(), 2)
KeyHandRank(x).HandType = FullHouse
KeyHandRank(x).AlphaBits = r1
KeyHandRank(x).BetaBits = r2
Call HandRankNumber(KeyHandRank(x))
ElseIf (VectorCount(3) = 1) Then
' trips
r1 = HighRank(RankCount(), 3)
r2 = HighRank(RankCount(), 1)
r3 = HighRank(RankCount(), 1)
KeyHandRank(x).HandType = ThreeOfAKind
KeyHandRank(x).AlphaBits = r1
KeyHandRank(x).BetaBits = BIT(r2) Or BIT(r3)
Call HandRankNumber(KeyHandRank(x))
ElseIf (VectorCount(2) >= 2) Then
' 2 pair
r1 = HighRank(RankCount(), 2)
r2 = HighRank(RankCount(), 2)
r3 = HighRank(RankCount(), 1)
KeyHandRank(x).HandType = TwoPair
KeyHandRank(x).AlphaBits = BIT(r1) Or BIT(r2)
KeyHandRank(x).BetaBits = r3
Call HandRankNumber(KeyHandRank(x))
ElseIf (VectorCount(2) >= 1) Then
' 1 pair
r1 = HighRank(RankCount(), 2)
r2 = HighRank(RankCount(), 1)
r3 = HighRank(RankCount(), 1)
r4 = HighRank(RankCount(), 1)
KeyHandRank(x).HandType = OnePair
KeyHandRank(x).AlphaBits = r1
KeyHandRank(x).BetaBits = BIT(r2) Or BIT(r3) Or BIT(r4)
Call HandRankNumber(KeyHandRank(x))
Else
' high card only
r1 = HighRank(RankCount(), 1)
r2 = HighRank(RankCount(), 1)
r3 = HighRank(RankCount(), 1)
r4 = HighRank(RankCount(), 1)
r5 = HighRank(RankCount(), 1)
KeyHandRank(x).HandType = HighCard
KeyHandRank(x).AlphaBits = BIT(r1) Or BIT(r2) Or BIT(r3) Or BIT(r4) Or BIT(r5)
KeyHandRank(x).BetaBits = 0
Call HandRankNumber(KeyHandRank(x))
End If
End If
Next
' get prime list
Open App.Path & "\100000.txt" For Binary As #1
ST = String(LOF(1), 0)
Get #1, , ST
Close #1
S2 = Split(ST, vbCrLf)
Dim PrimeList() As Long
ReDim PrimeList(1 To (UBound(S2) + 1) * 9)
PIndex = 0
For n = 0 To UBound(S2)
For a = 1 To 20
S2(n) = Replace(S2(n), " ", " ")
Next
S2(n) = Trim(S2(n))
S3 = Split(S2(n), " ")
For a = 0 To UBound(S3)
PIndex = PIndex + 1
PrimeList(PIndex) = Val(S3(a))
Next
Next
' map each key value to a 2-level hash index that points to the hand rank
Static MAXKEY As Long
MAXKEY = (2 ^ 27)
For x = 1 To UBound(Key)
If Key(x) >= MAXKEY Then
Key(x) = fmod(Key(x), MAXKEY)
End If
Next
Static Hash() As HASHARRAY
Dim HashTest() As HASHTESTARRAY
Static PrimeA As Long
PrimeA = 17389 ' 2000th prime = 17389 (has good performance)
ReDim HashTest(0 To PrimeA - 1)
ReDim Hash(0 To PrimeA - 1)
For a = 0 To UBound(HashTest)
ReDim HashTest(a).Key(1 To 1000)
ReDim HashTest(a).KeyIndex(1 To 1000)
Next
For x = 1 To UBound(Key)
a = fmod(Key(x), PrimeA)
HashTest(a).KeyCount = HashTest(a).KeyCount + 1
'If (HashTest(a).KeyCount > UBound(HashTest(a).Key)) Then
' ReDim Preserve HashTest(a).Key(1 To UBound(HashTest(a).Key) + 1000)
'End If
HashTest(a).Key(HashTest(a).KeyCount) = Key(x)
HashTest(a).KeyIndex(HashTest(a).KeyCount) = x
Next
For a = 0 To UBound(HashTest)
For PIndexB = 1 To 100 ' find lowest prime that resolves collisions
PrimeB = PrimeList(PIndexB)
HashTest(a).Count = 0
HashTest(a).Prime = PrimeB
ReDim HashTest(a).ColCount(0 To PrimeB - 1)
For x = 1 To HashTest(a).KeyCount
Test = HashTest(a).Key(x)
M2 = fmod(Test, PrimeB)
If HashTest(a).ColCount(M2) = 0 Then
HashTest(a).ColCount(M2) = 1
HashTest(a).Count = HashTest(a).Count + 1
Else
Exit For ' collision, try next prime
End If
Next
If (x > HashTest(a).KeyCount) Then
' no collisions, make it permanent
Hash(a).Count = HashTest(a).Count
Hash(a).Prime = HashTest(a).Prime
ReDim Hash(a).Result(0 To (PrimeB - 1))
For x = 1 To HashTest(a).KeyCount
M2 = fmod(HashTest(a).Key(x), PrimeB)
Hash(a).Result(M2) = KeyHandRank(HashTest(a).KeyIndex(x))
Next
Exit For
End If
Next
Next
Init = True
End If
' Other modules: CardTable()
' This modules: Hash(), PrimeA, FlushQuickTable()
' Begin actual hand evaluation:
Dim P(1 To 7) As Long ' prime
Dim RB(1 To 7) As Long ' rankbit
Dim SB(1 To 7) As Long ' suitbit
Dim SV(1 To 7) As Long ' suit value (1-4)
Dim RV(1 To 7) As Long ' rank value (2-14)
For x = 1 To 7
If (Card(x) >= 1) And (Card(x) <= 52) Then
P(x) = CardTable(Card(x)).Prime
RB(x) = CardTable(Card(x)).RankBit '(1, 2, 4, 8, ... , 4096)
SB(x) = CardTable(Card(x)).SuitBit '(1, 2, 4, 8)
SV(x) = CardTable(Card(x)).Suit '(1-4)
RV(x) = CardTable(Card(x)).Value '(2-14)
Else
' hand will not be calculated correctly
Exit Function
End If
Next
' begin analysis
Dim SuitMask(1 To 4) As Long
For x = 1 To 7
SuitMask(SV(x)) = SuitMask(SV(x)) Or RB(x)
Next
' only 1 flush should be possible per board
' if you have a flush, then flush or s.flush will always be the highest hand type
For S = 1 To 4
If (FlushQuickTable(SuitMask(S)).HandType <> Invalid) Then
' flush or s.flush
RankHand = FlushQuickTable(SuitMask(S))
Exit Function
End If
Next
Re: Need a Faster Poker Evaluation Method
Code:
' check other combinations by prime multiplication
Dim Product As Double
Product = 1
For x = 1 To 7
Product = Product * P(x)
Next
' extract the hand from the hash
If Product >= MAXKEY Then
Product = fmod(Product, MAXKEY) ' limit to 2^27-1
End If
M1 = fmod(Product, PrimeA)
PrimeB = Hash(M1).Prime
M2 = fmod(Product, PrimeB)
RankHand = Hash(M1).Result(M2)
end function
Re: Need a Faster Poker Evaluation Method
Additional overview please, will look again later when I get home...
How is Card() as long argument interpreted? Interpreted based on an enum or bit-wise operation?
Re: Need a Faster Poker Evaluation Method
Don't have much time, it would probably be faster if your the one to implement my idea. It goes something like this, rather than making the processing work with the data structure of the cards, create different data structures (or views of the data) to facilitate processing. Create several arrays based on cards passed:
- an array(3) to hold cards by suit, first bit or 2^0 is ace as one up to bit 2^13 for ace as higher than king.
- an array(13) to hold counts of cards per value, if you have three kings then array(12) = 3
- an array(3) to hold counts of cards per suit.
Then you check existence of good hands starting at straight flush (royal flush is highest straight flush). You iterate from mask = 2^13
edit have to go will continue later
Re: Need a Faster Poker Evaluation Method
The Card() array contains 7 card indexes (range 1-52) which is used in an external lookup table CardTable(1 to 52) to retrieve rank/suit bits and other information for use in the bitmask and prime multiplication operations which lead to a hand ranking. 80% of the code initializes tables and other data; only a small portion actually does the operation on the Card() array.
I've pretty much got it near optimal using bitmasks and such. I was just considering a method to take the 7 card indexes (1-52) directly to give myself a result. For instance, the fastest possible way is a 7-dimensional array with each dimension being (1-52), and using the card indexes into this array (requiring only 7 simple operations). Of course, 52^7 elements is impractical from a memory standpoint, so the next best way might be to sort the cards and used the sorted indexes into a tree with 7 levels (there would still be ~134M elements, which is impractical but possible on my machine). If there is a really fast way to sort 7 numbers (1-52) then this might be the way to go. The third way is to combine the 7 indexes into a hash key used with a hash table, but it would most likely be slower. Without mapping out all the combinations, there might exist a method for quickly collapsing the card indexes into the ~5000 equivalence classes. I just have no idea what that might be.
What I need to do is process each 7-card hand to discover the EV (or win%) against 1-9 opponents (quick access table, but obviously many hands will collapse to the same basic type). There are 6,009,159 7-card hand types, and 4,824 5-card hand types possible from all 7-card hands.
Re: Need a Faster Poker Evaluation Method
You don't need 52 bits, use an array of integer (16 bits) and use 14 bits to store card value (eg ace is 2^0, two is 2^1...). That way cards are already sorted based on the bit value. So to check for straight flush:
Code:
Const STRMAXMASK = 15872 '2^13 +2^12 +2^11 +2^10 + 2^9 'note consecutive bits
Const STRMINMASK = 31 '2^0 +2^1 + 2^2 + 2^3 + 2^4
Private Function HasStrFlush() As Boolean
Dim intMask As Integer
Dim booFound As Boolean
intMask = STRMAXMASK
Do
If (intBySuit(0) AND intMask) = intMask Then
booFound = True: Exit Do
End If
If (intBySuit(1) AND intMask) = intMask Then
booFound = True: Exit Do
End If
If (intBySuit(2) AND intMask) = intMask Then
booFound = True: Exit Do
End If
If (intBySuit(3) AND intMask) = intMask Then
booFound = True: Exit Do
End If
intMask = intMask / 2
Loop Until intMask < STRMINMASK
If booFound And (intMask = STRMAXMASK) Then
Msgbox "Hand has royal flush"
ElseIf booFound Then
Msgbox "Hand has straigh flush"
End If
HasStrFlush = booFound
End Function
Checking for straight works same way but non suit dependent check or in loop check mask against variable intCards where:
intCards = intBySuit(0) OR intBySuit(1) OR intBySuit(2) OR intBySuit(3)
That just loops 10 times rather than 100K times, if a straight/royal flush is found then processing stops no need to check for quadro, full house, etc. You can modify the implementation to return weighted hands to differentiate same hands (eg full house of aces has more weight compared to full house of 4's).
Re: Need a Faster Poker Evaluation Method
I already made a weighted scheme in the code bank but the processing was not efficient... I am planning to update it with what we discuss here when I find the time to do so
Re: Need a Faster Poker Evaluation Method
>> That just loops 10 times rather than 100K times <<
Actually, what I meant was that I could do 100K hand rankings per second using my engine. I appreciate your example though. My code actually just has bitmasks for each of the 4 suits and does an OR of each card rank bit with the appropriate suit. It then indexes the bitmask into a table which is pre-processed and tells me if a flush/s.flush has occured. If no flush is triggered from the 4 comparisons, then it does the alternative method of prime multiplication and hashing.
My method is an adaptation of the method found here: http://www.suffecool.net/poker/evaluator.html. That page describes a 5-card evaluator, but I am extending the concept for 7-cards if possible.
Re: Need a Faster Poker Evaluation Method
If you add cards according to
Code:
Public Enum CardSuits
Clubs = 0
Spades = 1
Hearts = 2
Diamonds = 3
End Enum
Public Enum CardValues
Ace = 0
Two = 1
Three = 2
...
King = 12
End Enum
Public Function AddCard(Suit As CardSuits, Value As CardValues) As Long
AddCard = -1
If (intBySuits(Suit) And Value) > 0 Then Exit Function 'already exists
intBySuits(Suit) = intBySuits(Suit) Or (2^ Value)
If Value = Ace Then
intBySuits(Suit) = intBySuits(Suit) Or (2^(Value+13))
End If
AddCard = 0 'successful add
End Function
Then you've set up an ordered listing of the cards by suit which you'll use to determine royal flush, straight flush, straights. Sample of use already provided... lacking is the base values per hand type that will be added to card weights e.g. 900000+15872 for royal flushes, 800000+7936 for straight flush 9 to king. The value of intBySuits() already has weight info of the cards with max value 15872 and minimum 31.
For the other card types, during AddCard() maintain other arrays in addition to intBySuits() that count cards per suit (for determining flushes) and per value (for determining quadro, trio, pairs).
I believe this will be faster compared to iterating through arrays with upper boundaries in the thousands.
Re: Need a Faster Poker Evaluation Method
It doesn't actually iterate through an array. For the quick detection method, the bitmask is the index used to lookup a rank quickly in an array. If using a tree method, it's actually fast to index several arrays by card index. My primary problem might not even be the speed of rank eval, but rather reducing the number of combinations using several methods.
In the following code, my hand ranking method is fast, but the problem is that there are 134M board layouts and the number of opponents can vary from 1 to 9. I need to map out the Expected Value (EV) of every board and opponent count combination. This means there are over 1B simulation sets that need to be performed, which is too many to calculate. There must be a way to reduce the number of board types so that all boards can be mapped and the EV calculated.
Code:
Public Function EnumEV()
Dim a As Long, b As Long, c As Long, d As Long, e As Long
Dim f As Long, g As Long, h As Long, i As Long, j As Long
Dim x As Long, p As Long, TestCount As Long, n As Long
Dim TotalEV As Double
Dim UsedCards(1 To 52) As Boolean
TestCount = 1000
Dim OppHand() As HANDCARDS
Dim Rank() As Long
T = Timer
For a = 1 To 52 ' p1
UsedCards(a) = True
For b = a + 1 To 52 ' p2
UsedCards(b) = True
For c = b + 1 To 52 ' c1
UsedCards(c) = True
For d = c + 1 To 52 ' c2
UsedCards(d) = True
For e = d + 1 To 52 ' c3
UsedCards(e) = True
For f = e + 1 To 52 ' c4
UsedCards(f) = True
For g = f + 1 To 52 ' c5
UsedCards(g) = True
' 134M combinations
For p = 1 To 9 ' # of opponents
TotalEV = 0 ' reset EV counter
For x = 1 To TestCount ' # of random tests to run
If Abs(Timer - T) > 0.25 Then
Text = x & "/" & TestCount & " - opp: " & p & " - EV: " & Format(TotalEV / x, "0.000") & " - " & TotalEV & " - "
Text = Text & CardTable(a).Text & " " & CardTable(b).Text & " " & CardTable(c).Text & " "
Text = Text & CardTable(d).Text & " " & CardTable(e).Text & " " & CardTable(f).Text & " "
Text = Text & CardTable(g).Text
Window.Caption = Text
DoEvents
T = Timer
End If
ReDim OppHand(0 To 9) ' reset hands
For n = 0 To p ' current # of opps
If (n = 0) Then ' player pocket
OppHand(n).p1 = a
OppHand(n).p2 = b
Else ' random opp pocket selection
OppHand(n).p1 = GetRandomCard(UsedCards())
OppHand(n).p2 = GetRandomCard(UsedCards())
End If
' community cards
OppHand(n).c1 = c
OppHand(n).c2 = d
OppHand(n).c3 = e
OppHand(n).c4 = f
OppHand(n).c5 = g
Next
ReDim Rank(0 To 9) ' reset ranks for each player
MaxRank = 0
For n = 0 To p
Rank(n) = RankHand(HandArray(OppHand(n))).RankNumber
If Rank(n) > MaxRank Then
MaxRank = Rank(n)
End If
Next
PotSplit = 0
For n = 0 To p
If Rank(n) = MaxRank Then ' player has won or tied
PotSplit = PotSplit + 1
End If
Next
If Rank(0) = MaxRank Then
If PotSplit = 1 Then ' win
TotalEV = TotalEV + 1
Else ' tie/split
TotalEV = TotalEV + (1 / PotSplit)
End If
Else ' loss
TotalEV = TotalEV + 0
End If
For n = 1 To p ' undo randomly used cards from opps
UsedCards(OppHand(n).p1) = False
UsedCards(OppHand(n).p2) = False
Next
Next ' end of test run
' test result (log)
AveEV = (TotalEV / TestCount) ' range = 0-1
Next ' end of all opp test runs for this 7-card hand
UsedCards(g) = False
Next
UsedCards(f) = False
Next
UsedCards(e) = False
Next
UsedCards(d) = False
Next
UsedCards(c) = False
Next
UsedCards(b) = False
Next
UsedCards(a) = False
Next
End Function
Re: Need a Faster Poker Evaluation Method
Re: Need a Faster Poker Evaluation Method
A "board" consists of the 5 community cards that are dealt in Texas Holdem. My usage, however, is refering to the 7-cards formed by these 5 plus the player's own 2 cards (because they interact). Perhaps it is more accurate to say that there are 134M known "situations" that can occur by the end of a hand (there are actually many more because other player's cards are unknown, but for building a calculator we cannot make assumptions unless we are modeling players, which at this time I am not).
Re: Need a Faster Poker Evaluation Method
So your trying to develop the AI? So its intelligent enough to go for it or fold based on an incomplete hand (less than 7), correct?
Why not just base it on the current hand? Those with pairs or trios would obviously have better chances compared to those striving for 5 card dependent combinations.
Re: Need a Faster Poker Evaluation Method
Also, another simple metric for texas would be "how many of my two cards am I using for the best hand possible?" If the answer is zero or your only using held cards as kickers then you have no advantage over the other players even without checking the probabilities.
Another metric would be how much effect does your held cards have since its your advantage over other players. Such as considering only public cards best possible is a pair but when you include your 2 cards it becomes a straight then the effect considerable and worth bidding for. Your method for assigning weight to hand should not be limited to minimum 5 cards e.g. can handle two/three cards (held cards not yet included) with best possible pair/trio respectively.
Last metric would be how far away from you from the ideal hand (for straight flushes, quadro,etc) with how many draws left. If your just one card away with 2 draws then you still have a chance.
I suggested this cause the probabilities isn't the only metric. Where the cards are located (private/public) makes/breaks the advantage.
Re: Need a Faster Poker Evaluation Method
You do have some good insights into poker. All of those things make sense to consider for a human player, but for my program it has to have a quantitative way to calculate choices. In most cases, what has worked well for me is to make choices based on a certain ratio.
EVRatio = (mywin% / (100% / PlayerCount))
mywin% varies from 0-100% based on the cards in play (as the end of the hand approaches, there is less variance in the final probability). PlayerCount varies from 2-10 players. At a point in time, when I make a decision it is based on the EVRatio generally.
if EVRatio >= RaiseEV then Action.Raise
else if EVRatio >= 1.0 and mywin% > PotOdds then Action.Call
else Check/Fold
If we make the assumption that players are calling to the river with random hands, then we would call/raise any hand with EVRatio > 1.0. However, this is not true as players selectively play cards. My search for an optimal play algorithm is basically a simulation of 10 players with various EVRatio tolerances ("tightness"/"looseness"). Optimal play style is found when all players converge on a particular strategy. From previous data, preflop play suggests that ~20% of hands are profitable in professional tables with 10 players; this roughly equates to an EV tolerance of 1.3 preflop. The number of players will affect the EVRatio; there is a tendency for it to move toward 1.0 as the number of players decreases (applies to preflop only). What I need to figure out is the optimal tolerance after the flop based on the situation.
Note that optimal play refers to a strategy that considers win% and pot odds only, and does not attempt to read or predict opponents. Most people will deviate from the optimal play, and any deviation will result in their loss to you over the long run. This strategy does not maximize winnings, but it is designed to protect against losses and volatility based on bad luck. It applies only to limit poker, as no limit creates a "risk of ruin" scenario when players go all-in and must be handed using more complex methods.
I got my simulator up and running. I'm now waiting for it to converge on an optimal strategy.
I found some information that is useful for optimization: http://www.xtremevbtalk.com/archive/.../t-278840.html