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:

( const char * strHand )

{

StdDeck_CardMask theHand, theCard ;

StdDeck_CardMask_RESET ( theHand ) ; StdDeck_CardMask TextToPokerEvalstrHandStdDeck_CardMask theHand, theCardStdDeck_CardMask_RESETtheHand 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.

. 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” ) ; StdDeck_CardMask player1TextToPokerEvalStdDeck_CardMask player2TextToPokerEvalStdDeck_CardMask theBoardTextToPokerEval // 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 ; calculateHoldemMatchupStdDeck_CardMask player1CardConverterStdDeck_CardMask player2CardConverterStdDeck_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!