A Pokersource Poker-Eval Primer

Pokersource Poker-Eval, the open-source poker hand evaluation library, takes a lot of flack for being “low-level”. In The Great Poker Hand Evaluator Roundup, I wrote:

The Poker-Eval library is implemented in highly optimized, heavily macro’d C for unadulterated speed, but language mappings for .NET, Java, and Python are provided. Now, I’ll be honest. The first time I saw the poker-eval source code, I immediately unlearned about sixty-two months of best-practices software development…

That statement was a little tongue-in-cheek, but still: it seems like every time someone suggests Pokersource Poker-Eval, a disclaimer immediately follows:

Don’t use this library unless you’re a competent C programmer! Danger, Will Robinson!

Just the other day, for example, I was looking at poker-related questions on Stack Overflow when I came across this: How Do I Programatically Calculate Poker Odds?

Hello, I’m trying to write a simple game/utility to calculate poker odds. I know there’s plenty of resources that talk about the formulas to do so, but I guess I’m having trouble translating that to code. Particularly, I’m interested in Texas Hold-em …

I understand that there are several different approaches, one being that you can calculate the odds that you will draw some hand based on the cards you can see. The other approach is calculating the odds that you will win a certain hand. The second approach seems much more complex as you’d have to enter more data (how many players, etc.)

I’m not asking that you write it for me, but some nudges in the right direction would help :-)

One of the answers suggested using Pokersource:

Take a look at pokersource if you have reasonably strong C abilities. It’s not simple, I’m afraid, but some of the things you’re looking for are complex. The poker-eval program that uses the library will probably do much of what you want if you can get the input format correct (not easy either). Sites such as this one or this also use this library AFAIK.

Still, it could be worse, you could be wanting to calculate something tricky like Omaha Hi-lo…

Again with the disclaimer! And it occurred to me: you know what? All this FUD is really kind of bogus. As of this writing, Pokersource is still the best and most complete library of publically available poker-related code in the world. Period. What’s more: the Pokersource evaluator is extremely easy to use provided you understand the Pokersource way of doing things.

So I thought, why not put together a brief Pokersource Poker-Eval primer?


The Basics: Pokersource Grammar

The Pokersource evaluator represents a collection of N cards as a sequence of 52 bits, one bit for each card in the deck. This structure is called a mask and in code it’s represented by the StdDeck_CardMask type. A single StdDeck_CardMask value can store a single card, an entire N-card poker hand, or any other arbitrary collection of between 0 and 52 cards. Typically, StdDeck_CardMask values will be used to store player hands, board cards, and/or dead cards:

StdDeck_CardMask player1, player2, player3;
StdDeck_CardMask boardCards;

Pokersource also allows a card to be addressed/identified by index—as an integer between 0 and 51. A given card index can be converted to an StdDeck_CardMask by using the StdDeck_MASK macro:

StdDeck_CardMask theCard = StdDeck_MASK(cardIndex);

You can clear a given Pokersource mask (removing all cards from the mask) via the StdDeck_CardMask_RESET macro. It’s a good idea to do this before using the mask (similar to initializing a numeric variable to 0).

StdDeck_CardMask hand;
StdDeck_CardMask_RESET(hand);

You can combine Pokersource masks together to create a single mask containing all cards from both masks by using the StdDeck_CardMask_OR macro.

StdDeck_CardMask player1HoleCards = /* some value */
StdDeck_CardMask theBoard = /* some value */
StdDeck_CardMask combinedHand;
StdDeck_CardMask_OR(combinedHand, player1HoleCards, theBoard);

Since a single StdDeck_CardMask can “contain” between 0 and 52 cards, sometimes it’s helpful to be able to count the cards present in a particular mask. For this, you can use the StdDeck_numCards macro:

StdDeck_CardMask arbitraryCollectionOfCards = /* some value */
int cardCount = StdDeck_numCards(arbitraryCollectionOfCards);

Now you might be wondering: can the Pokersource evaluator handle text? The answer is yes. You can convert a textual description of a card (such as “Ah” or “2d”) into the proper index value by using the StdDeck_stringToCard macro:

int cardIndex = -1;
StdDeck_stringToCard(“Ah”, &cardIndex);

Which means that you can convert a textual description of a card into its corresponding mask value by using StdDeck_stringToCard followed by StdDeck_MASK:

int cardIndex = -1;
StdDeck_stringToCard(“Ah”, &cardIndex);
StdDeck_CardMask theCard = StdDeck_MASK(cardIndex);

This, of course, can be generalized to a function allowing you to convert text representing any number of cards into a single StdDeck_CardMask value:

StdDeck_CardMask TextToPokerEval(const char* strHand)
{
   StdDeck_CardMask theHand, theCard;
   StdDeck_CardMask_RESET(theHand);

   if (strHand && strlen(strHand))
   {
      int cardIndex = -1;
      char* curCard = const_cast<char*>(strHand);

      while (*curCard)
      {
         // Take the card text and convert it to an index (0..51)
         StdDeck_stringToCard(curCard, &cardIndex);
         // Convert the card index to a mask
         theCard = StdDeck_MASK(cardIndex);
         // Add the card (mask) to the hand
         StdDeck_CardMask_OR(theHand, theHand, theCard);
         // Advance to the next card (if any)
         curCard += 2;
      }
   }

   return theHand;
}

Going in the opposite direction, we can convert a Pokersource mask value to text by using the StdDeck_maskString and StdDeck_maskToString macros. The difference is that StdDeck_maskString manages the memory for the string internally, whereas StdDeck_maskToString copies the text to a user-supplied string buffer.

StdDeck_CardMask hand = /* some player hand */
char* hand = StdDeck_maskString(hand);

Similarly, we can take a card index and get a text value representing that card using the StdDeck_cardToString macro:

char cardText[3];
int cardIndex = /* some card index */
StdDeck_cardToString(cardIndex, cardText);

Another popular way of representing card values is as a combination of rank and suit. Pokersource allows you to create an StdDeck_CardMask value for a specific card by specifying its rank and suit in the StdDeck_MAKE_CARD macro, which returns the card’s index:

int cardIndex = StdDeck_MAKE_CARD(someRankValue, someSuitValue);
StdDeck_CardMask = StdDeck_MASK(cardIndex);

Note that Pokersource has a specific numeric definition of each card rank:

#define StdDeck_Rank_2      0
#define StdDeck_Rank_3      1
#define StdDeck_Rank_4      2
#define StdDeck_Rank_5      3
#define StdDeck_Rank_6      4
#define StdDeck_Rank_7      5
#define StdDeck_Rank_8      6
#define StdDeck_Rank_9      7
#define StdDeck_Rank_TEN    8
#define StdDeck_Rank_JACK   9
#define StdDeck_Rank_QUEEN  10
#define StdDeck_Rank_KING   11
#define StdDeck_Rank_ACE    12
#define StdDeck_Rank_COUNT  13
#define StdDeck_Rank_FIRST  StdDeck_Rank_2
#define StdDeck_Rank_LAST   StdDeck_Rank_ACE

As well as each suit. Pokersource bucks convention here by ordering suits Hearts-Diamonds-Clubs-Spades rather than Clubs-Diamonds-Hearts-Spades but it doesn’t really matter.

#define StdDeck_Suit_HEARTS   0
#define StdDeck_Suit_DIAMONDS 1
#define StdDeck_Suit_CLUBS    2
#define StdDeck_Suit_SPADES   3
#define StdDeck_Suit_FIRST    StdDeck_Suit_HEARTS
#define StdDeck_Suit_LAST     StdDeck_Suit_SPADES

Evaluating Hands with Pokersource

Pokersource Poker-Eval is, first and foremost, a hand evaluator. It takes a given N-card poker hand and returns a number which can be compared with the number returned for other hands to determine the winner. There are two evaluation macros you’ll typically use:

  • StdDeck_StdRules_EVAL_N. The full evaluator.
  • StdDeck_StdRules_EVAL_TYPE. A pared-down evaluator which returns only the hand category (e.g., Full House, Quads, One Pair, etc.)

So in order to determine the winner in a game of Texas Hold’em where Player 1 has “AhAd” and Player 2 has “KcKs” on a board of “2d4c4sKh9d”, you’ll write code similar to this:

// Say we start with something like this…
StdDeck_CardMask player1 = TextToPokerEval(“AhAd”);
StdDeck_CardMask player2 = TextToPokerEval(“KcKs”);
StdDeck_CardMask theBoard = TextToPokerEval(“2d4c4sKh9d”);

// Get each player’s full 7-card hand into his mask
StdDeck_CardMask_OR(player1, player1, theBoard);
StdDeck_CardMask_OR(player2, player2, theBoard);

// Evaluate each player’s hand
int player1Val = StdDeck_StdRules_EVAL_N(player1, 7);
int player2Val = StdDeck_StdRules_EVAL_N(player2, 7);

if (player1Val > player2Val)
   ;// Player 1 wins. Do something.
else if (player1Val < player2Val)
   ;// Player 2 wins. Do something.
else
   ;// Tie. Do something.

Using the StdDeck_StdRules_EVAL_TYPE evaluator is exactly the same, except the value returned is an integer representing the category of the hand.

That’s literally all there is to it.

Monte Carlo and Exhaustive Enumeration with Pokersource

It’s a little-known fact, but Pokersource ships with built-in support for both Monte Carlo simulation and Exhaustive Enumeration. In fact, we can leverage the Pokersource enumeration library to build a full-fledged poker calculator with support for multiple opponents with arbitrary hand ranges or distributions.

Pokersource provides Monte Carlo and Exhaustive Enumeration functionality through the following macros, all of which live in the enumerate.h header file.

  • DECK_ENUMERATE_x_CARDS
  • DECK_ENUMERATE_x_CARDS_D
  • DECK_MONTECARLO_N_CARDS_D
  • DECK_ENUMERATE_COMBINATIONS_D
  • DECK_ENUMERATE_PERMUTATIONS_D
  • DECK_MONTECARLO_PERMUTATIONS_D

Using these macros is extremely simple provided you understand the format. Basically, each macro is going to “visit” or “generate” certain cards, repeatedly, using either exhaustive enumeration or Monte Carlo. For each set of visited/generated cards, the macro is going to execute a piece of arbitrary code provided by you.

This will be clearer if we look at some source code. Here’s how you’d calculate the equity of a typical preflop matchup…

  • Player 1: [Ah Ac]
  • Player 2: [Kh Kc]

…using exhaustive enumeration of all possible outcomes.

void calculateHoldemMatchup
{
   // Create player hands…
   StdDeck_CardMask player1 = CardConverter::TextToPokerEval(“AhAc”);
   StdDeck_CardMask player2 = CardConverter::TextToPokerEval(“KhKc”);
   StdDeck_CardMask boardCards;

   // Add player cards to “dead” or “used” cards
   StdDeck_CardMask usedCards;
   StdDeck_CardMask_OR(usedCards, player1, player2);

   // Create an array to tally wins
   double wins[2] = { 0.0 };
   int numberOfTrials = 0;

   // Enumerate all possible 5-card boards, excluding boards which
   // contain one or more player hole cards. This will call ‘evalSingleTrial’
   // once for each unique board.
   DECK_ENUMERATE_5_CARDS_D(StdDeck, boardCards, usedCards,
      evalSingleTrial(player1, player2, boardCards, wins, numberOfTrials); );

   // Convert each player’s win tally to equity..
   double player1Equity = (wins[0] / numberOfTrials) * 100.0;
   double player2Equity = (wins[1] / numberOfTrials) * 100.0;

   // Results:
   // Player 1: 82.64%
   // Player 2: 17.26%
}

void evalSingleTrial(StdDeck_CardMask player1, StdDeck_CardMask player2,
                     StdDeck_CardMask board, double wins[], int& numberOfTrials)
{
   // Combine each player’s hole cards with the 5-card board
   StdDeck_CardMask_OR(player1, player1, board);
   StdDeck_CardMask_OR(player2, player2, board);

   // Evaluate each player’s hand
   int p1Val = StdDeck_StdRules_EVAL_N(player1, 7);
   int p2Val = StdDeck_StdRules_EVAL_N(player2, 7);

   // Tally wins
   if (p1Val > p2Val)
      wins[0] += 1.0;
   else if (p1Val < p2Val)
      wins[1] += 1.0;
   else
   {
      wins[0] += 0.5;
      wins[1] += 0.5;
   }

   numberOfTrials++;
}

The above code exhaustively enumerates all 1,712,304 possible outcomes (the number of unique 5-card boards that can be drawn from the 48 remaining cards in the deck) and runs the evaluator for each, tallying per-player wins which are then converted into percentage equities. If you wanted to use Monte Carlo instead, you’d use DECK_MONTECARLO_N_CARDS_D.

The other enumeration macros all have this same structure: you tell Pokersource what you want enumerated or randomly sampled, and it does the grunt work, executing your user-provided code once for each enumerated/sampled outcome.

The ability to generically enumerate/randomly sample specific subsets of cards in the presence of dead cards is enough firepower to build a full-fledged poker calculator such as the one we built in Multiway Isometric Ranged Equity Calculation in Poker, Part 1. And yet this post has really only scratched the surface of what is possible with Pokersource. We haven’t discussed Pokersource’s support for other poker variants (Omaha, Stud, etc.), or how the Pokersource evaluator can be extended to support still other variants.

In conclusion, when looking for poker evaluation / calculation code, Pokersource should always be your first stop. I’m not saying it’s always the right library for the job, but you won’t find another publically available library that has equivalent functionality. I encourage you to explore the Pokersource library and who knows, perhaps join the Pokersource email distribution list and say hello to Loic Dachary, Michael Maurer, and some of the other maintainers.

Long live Pokersource!

Comments

  • Loic Dachary says:

    Thank you for this introduction to poker-eval, it’s a great way to start with pokersource. I would like to mention that we (pokersource developers) are looking for help to implement new features in [url=http://pokersource.info/]pokersource[/url]. If you are interested, please send a mail to [url=mailto:pokersource-users@gna.org]pokersource-users@gna.org[/url] or come visit the chat room at [url]http://irc.freenode.net/[/url] #pokersource

  • Anonymous says:

    Hey James, one of your 2+2 ‘fans’ here. I stumble across every once in a while some Pokersource code in the software forum. never quite could figure out just what the hell all the different macros meant. (and I’m a C programmer). This will come in handy for me.

    To Loic: I really think the pokersource would benefit from a good set of docs. Ive searched and searched for an ‘API guide’ but have never found one. Just bits and pieces on different forums.

  • proppy says:

    What about including this post (or part of it) in poker-eval future releases, as TUTORIAL.txt for example ?

    @James Devlin, would you agree to release this tutorial under a permissive License ?

  • Loic Dachary says:

    I agree that pokersource needs documentation. The best documentation at the moment are the test cases (poker-engine is fully covered and poker-network will soon be). But it’s difficult for a beginner. I would love to see a native english speaker bootstrap this effort.

  • Bill Mill says:

    Long ago I modified hcmpn.c to make it into a nice quick command-line calculator I could use in-game; unfortunately I seem to have lost the code. I can’t believe I didn’t think to release it!

  • Darmo says:

    I am with affiliate programs pokersource , just reffer that team for all :)
    Thay have great offer’s.
    Regards

  • Coding the Wheel says:

    @James Devlin, would you agree to release this tutorial under a permissive License ?

    I have no problem with that. Most of the content on this site is under the Do What the F-k You Want To Public License by fiat. But I think Pokersource deserves a formal set of docs!

    @Bill Mill: Ah, good old hcmpn.c. I know it well.

  • Anonymous says:

    @James, trying to make a poker table simulation in a first step at developing a poker bot.
    Can I use “StdDeck_ StdRules _EVAL” to find the best hand for one player out of a given set of cards? Ie, if I passed all 5 combinations of 7 cards, from a TexasHoldEm table, would I get the best hand the player can make? Also, can I use pokereval to figure out the probability of making a certain hand, assuming that the next card dealt is random and my hole cards and the table cards are dead?

  • Bruno says:

    Great introduction to the pokersource code. Once I tried understanding it but I gave up. Now knowing the basics I think I’ll try one more time to use it. Big thanks.

  • Mulberry Outlet says:

    It is really a nice post, it is always great reading such posts, this post is good in regards of both knowledge as well as information. Very fascinating read, thanks for sharing this post here.

  • Heyy says:

    Hello,
    What do you #include??

  • Leave a Reply