How would one go about writing a poker calculator?
Plug in 1-X starting hands then return the percentage of wins.
I'm not familiar with .NET and stuff. I probably should mention that I'm not a professional programmer myself. But there surely are a lot of great libraries in pure C.
Using C++ and i5 CPU I personally get about 4.4 million hands/s in Monte Carlo with 3 players and 2.3 million with 6 players. But that's just raw unoptimized Monte-Carlo. Brute-force enumeration goes at the rate of 190M hands/sec. So your 205,476,480 boards don't look too scary to me. This number is actually wrong: you should have calculated it as combin(48,5) = 1,712,304
. At 190M/sec that's an instant. So I can assume that the bottleneck is hand evaluation algorithm.
Here you go:
A LOT of code on hand evaluation presented in the article "The Great Poker Hand Evaluator Roundup": XPokerEval. I would recommend you pokersource since it has been ported to all kinds of languages. TwoPlusTwo evaluator shows great enumeration benchmark but failes at Monte Carlo due to cache misses.
Back to good ol' C. Steve Brecher's Holdem Showdown. Really nice piece. I had great time disecting and optimizing it. Google it, I can post only 1 link :(
Ashelly's Ace_eval. It's a golfed program with size under 600 bytes and no lookup tables. Ungolfed version works quite fast. I really liked its hand value format, so I use it myself.
Andrew Prock's Pokerstove. This one made me wanna quit even trying programming. It utilizes highly optimized enumeration algorithm. Trillions of hands/sec is not out of the ordinary. Look up adrian20xx's posts on twoplustwo and PokerAI for detailed explanation.
As far as Monte Carlo goes I noticed that the bottleneck is also the deck shuffle. I've come up with this algorithm:
#define PUT_CARD_TO_BACK(X) {tmp=deck[N];deck[N--]=deck[X];deck[X]=tmp;}
//put card in the back of the deck
void shuffleDeal (int myCard1,int myCard2, int nPlayers, int*deck){
int N=51,i,k;
PUT_CARD_TO_BACK(myCard1);
PUT_CARD_TO_BACK(myCard2);
for (i=0;i<(nPlayers*2+5);i++){
k=rand()%(N+1);
PUT_CARD_TO_BACK(k);
}
}
Here I have to generate only necessary amount of random numbers,given cards and randomly picked cards are put in the back of the deck in specified order and I can use them as board or hole cards like this:
#define BOARD deck[45],deck[46],deck[47],deck[48],deck[49]
#define HERO deck[50],deck[51]
#define OPP1 deck[43],deck[44]
In case of small numbers of hands needed to be evaluated (less than 100M) I think you should use exhuastive enumeration. Otherwise 100,000 of Monte Carlo iterations works just fine. The output of a program is entirely up to you depending on what you are trying to analyze. Hope I've helped in some way:)
for(c1 = 0; c1 < 52; c1++) { if (CardsExtracted[c1]) continue; for(c2 = c1 + 1; c2 < 52; c2++) { if (CardsExtracted[c2]) continue; for(c3 = c2 + 1; c3 < 52; c3++) { if (CardsExtracted[c3]) continue; for(c4 = c3 + 1; c4 < 52; c4++) { if (CardsExtracted[c4]) continue; for(c5 = c4 + 1; c5 < 52; c5++) { if (CardsExtracted[c5]) continue;
That's a naive way. The option is to optimize it in the way similar to pokerstove, but I don't know if it worth the effort.
Features important to me:
break down on all the hands
percent straight flush down to pct high card
need this for debug anyway
further break down on all hands
when it wins / ties did it do so as what hand (straight flush down to high card)
(my app does not currently do that - going to add it)
option to run as heads up against each
so instead of running like JJ against AA, KK, AK separately get it in one
(the boards are different - JJ veruss AA does not remove KK, and AK)
Design:
card class
properties rank, suit, and out
using a Boolean property for out (in a hand) was much more convenient than removing cards from the deck that were out
so on start just create a deck of 52 and use it for basically every thing
player class
properties wins, ties, losses
from those three every thing else is calculated
the Unicode symbol for a club looked a lot like spade so I used a shamrock
order the hands (hand score)
gave strait flush a value of 8 down to 0 for high hand
gave ace a value of 14 down to two a value of 2
in the case of a straight add the ace with a value of 1
for kickers include the made hand as part start of the kicker
hand and kicker are all just two digits
hand * 100000000 + kicker.0 * 1000000 + kicker.1 * 10000 + kicker.2 * 100 + kicker.3
so ace over king boat is
614130000
for flush and high card it could actually go into 5 kickers but I just let that be a tie
shuffle
rather than shuffle whole deck just grab the required number of card randomly
I just used .NET random
evaluate board
shuffle then one player at a time get the order (hand score)
record win, tie, and loss to the player
This took me about 20 hours but I am a developer
I moved to a new version of .NET and Visual Studio and wanted a project to practice with
I a mid range PC I get about 10,000 shuffles in 20 seconds. A 100,000 takes about 3 minutes.
Can get up to a 10% error with just 10,000 shuffles but for just planning that can be enough. On the higher % the error is smaller. The smaller % like 2 7 off suite is a bigger error but that is the stuff you are not usually interested in.
On a faster machine with 8 core could process 8 hands in parallel so would get 100,000 down to the 10 second range.
The online calculators that are instantaneous I guess process all combinations at like 20 million shuffles each and store that. I am not really sure how they do it.
For speed could stop calculating a hand when you know it is dead. But that would be a lot more logic and I wanted the detail on all hands.
In machine learning you can do something called pruning but I am not strong enough for that and again I wanted the detail.
4 hands is not twice as long as 2 as same number of shuffles. And you get better accuracy for the same number of shuffles as there are less possible boards.
With the brute force approach running all board is not practical. With two hands there are 48*47*46*45*44 boards = 205,476,480. Even on at 100,000 in 10 seconds that is like 6 hours.
At 10 million a Monte Carlo method is going to be very accurate.