1

How would one go about writing a poker calculator?

Plug in 1-X starting hands then return the percentage of wins.

2 Answers 2

2

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:

  1. 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.

  2. 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 :(

  3. 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.

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

3
  • Thanks. I am getting some deviation on 100,000 and it does not even go away at 1 million. I think you are correct about combin(48,5). I need to think about how to enumerate them. And I don't want to use a library or pre-run simulations. I want to write a standalone poker calculator.
    – paparazzo
    Commented Apr 26, 2016 at 22:26
  • All combinations are enumerated like this 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.
    – RICHnsk
    Commented Apr 27, 2016 at 11:20
  • Wow - somebody actually used my evaluator! Glad you liked it.
    – AShelly
    Commented Nov 28, 2018 at 18:54
2

Features important to me:

  • total and pct on win loss tie
  • ratio for hand odds
    give a tie 1/2 win
  • 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.

enter image description here

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.