The Great Poker Hand Evaluator Roundup

Introduction

This page is a listing of publically available poker hand evaluators, for Hold’em and other games, in C, C++, C#, Java, and other programming languages, with brief descriptions, sample usage, and complete source code, along with gratuitous pictures of scantily-clad ninjas. Downloading and building these hand evaluators piecemeal can be a pain in the you-know-what…

Some assembly required

…so I’ve packaged the complete source code for every evaluator into a single, easy-to-build library: XPokerEval.

[more]

If you’re a poker hand evaluation guru, some of this will be old hat to you. On the other hand, if you’re new to the subject of poker hand evaluation, it might come in handy. Enjoy.

The Pokersource Poker-Eval Evaluator

Description:

The Pokersource poker-eval library is probably the most widely-used poker hand evaluator in the galaxy.  It’s open-source, it’s fast, it’s over ten years old, and it can not only analyze your poker hands but make you a cup of coffee, interpolate the doomsday trajectory of near-Earth asteroids, and fix the grease-clot in your kitchen sink.

poker-eval is a C library to evaluate poker hands. The result of the evaluation for a given hand is a number. The general idea is that if the evaluation of your hand is lower than the evaluation of the hand of your opponent, you lose. Many poker variants are supported (draw, holdem, omaha, etc.) and more can be added. poker-eval is designed for speed so that it can be used within poker simulation software using either exhaustive exploration or Monte Carlo.

Poker-eval has a ton of functionality, including support for multiple poker variants (Hold’em, Omaha, Stud 7, etc.), non-standard decks, low and high-low games, enumerations, quarks, leptons, synchronicities, and strange attractors. It can be built on about a dozen different platforms, including Windows, *nix, and the alien mother ship OS first popularized in Independence Day and later legitimized by Jakob Nielsen.

Uploading the save-the-planet virus into the alien mother ship using the X'yg'yec'nar42 protocol

Pretty cool.

Implementation:

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:

#define INNER_LOOP_HOLDEM                                
  INNER_LOOP({                                            
    StdDeck_CardMask _hand;                              
    StdDeck_CardMask _finalBoard;                        
    StdDeck_CardMask_OR(_finalBoard, board, sharedCards);
    StdDeck_CardMask_OR(_hand, pockets[i], _finalBoard);  
    hival[i] = StdDeck_StdRules_EVAL_N(_hand, 7);        
    loval[i] = LowHandVal_NOTHING;                        
    err = 0;                                              
  })

The Pokersource library isn’t exactly going to be winning any awards for clarity or courtesy, but then again, once you understand how to use the library, it doesn’t really matter. Poker-Eval is unapologetically designed for speed. If you’ve got a problem with that, take it up with this guy:

Mr. T

Four things and four things only, in this universe:

  • bitwise manipulation
  • lookup tables
  • bitwise manipulation
  • lookup tables

Everything is expressed either as a sequence of bits on which various operations are performed, or as a lookup table from which precomputed values are grabbed, often by taking the output of an obscure but clever bitwise manipulation and treating it as an index, or otherwise doing esoteric things with code, like boiling the orange sponge donkey.

Magic, basically. This doesn’t work the same way as a game of live blackjack, that requires a post of its own.

And just as in Cactus Kev, the result of evaluation for a hand is a number (called a hand value or HandVal in Pokersource lingo) which can be compared to the number for any other hand to determine a winner.

However, unlike Cactus Kev, the number so returned is not an equivalence value.

Poker-Eval guarantees that if Hand A beats Hand B, the hand value for Hand A will be higher than the hand value for Hand B. And it guarantees that, if Hand A ties Hand B, the hand value for both hands will be equal. But the actual hand values returned by the Pokersource evaluator are different than those returned by the Cactus Kev evaluator.

Card Format:

A poker hand is (internally) represented as a sequence of 52 bits, one for each card in the deck. This is called a hand mask or card mask (and sometimes just a “hand” or a “card” or a “mask” in order to be vague) and it can be used to store N number of cards, where N is any number between 0 and 52.

Using card masks, we can represent an entire poker hand, such as the Royal Flush of Spades…

       11111000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

Or we can represent a single card, such as the 7 of Diamonds…

       00000000 00000000 00000000 00000000 00000001 00000000 00000000 00000000

Another cool thing about this format is its ability to induce epileptic seizures when stared at for longer than a few seconds. Luckily, the Pokersource library provides a number of macros/functions to protect you from the underlying representation. But who’s going to protect you, gentle programmer, from the macros? That’s another question…

Usage:

There are five (that’s 0101) key abstractions you’ll want to know about before tangling with this evaluator:

  • Card Mask. A 52-bit value containing 1 bit for each card. But we have to stick it in an 8-byte integer, so 64 bits with 12 dead. Type: StdDeck_CardMask.
  • Card Index. A number between 0 (Two of Hearts) and 51 (Ace of Spades) which uniquely identifies a particular card. Simple enough. Type: int.
  • Hand Value. The value of a given poker hand, which can be tested against other HandVals to determine the winner. Type: HandVal.
  • Hand Category. The type of a poker hand, such as Two Pair or Full House. Type: int.
  • Evaluator. Just to make things as confusing as possible, the Poker-Eval evaluator has different evaluators. For example, the StdDeck_StdRules_EVAL_TYPE function is a fast N-card evaluator designed to return a hand’s category only, whereas the StdDeck_StdRules_EVAL_N function is designed to return the hand value for a hand. There are also separate evaluators for non-standard games. And then there are macros to map these back and forth in typical C-language style. Type: function.

For more information, check out the XPokerEval.Pokersource.Test project, where I’ve placed a few sample usages, and you can find numerous others out there around the net.

Limitations:

Yeah right. With support for multiple poker variants, multiple platforms, multiple language mappings, and because of its sheer speed, the Pokersource evaluator is like the bully who takes all the kids’ lunch money in the schoolyard, the bully your parents told you not to be scared of because he’d leave you alone if you only stood up to him, because all bullies are sissies in Perfect, so you stood up to him that one sunny afternoon and it turns out sorry, kid, your parents were wrong. He’s not anything like a sissy, and YOU, well, in the immortal words of Jack Handey:

If you define cowardice as running away at the first sign of danger, screaming and tripping and begging for mercy, then yes, Mr. Brave man, I guess I’m a coward

The Pokersource evaluator is, well, kind of like that (?). At least, it mops the floor with every other evaluator save one: that near-mythical potpourri of hand evaluation ninjitsu, the Two Plus Two evaluator.

Not your average ninja

But we have to walk before we can run.

Cactus Kev’s 5-Card Evaluator

Description:

A few years ago, Kevin Suffecool came up with a ridiculously freaking clever algorithm for 5-card poker hand recognition. When it was first introduced, the Cactus Kev evaluator made quite a splash, and a (private, unfortunately) 7-card version was even incorporated into commercial poker software, as Kevin describes. A slightly modified version of the Cactus Kev evaluator is also used by the Two Plus Two evaluator, not to mention Paul Senzee’s 7-card evaluator, and the original Cactus Kev equivalence tables are used in Moritz Hammer’s DAG evaluator.

This evaluator gets around, baby. Like a record.

Implementation:

The Cactus Kev evaluator uses prime numbers, bitwise manipulation, lookup tables, and bilatitudinal quasi-isometric spin widgets in order to map a collection of five cards to an equivalence class between 1 (Royal Flush) and 7,462 (7-high). Like most of the other evaluators in this roundup, the Cactus Kev evaluator converts a poker hand to a number which can be compared to other poker hands. But the Cactus Kev evaluator was the first to use formal equivalence class values for those numbers, i.e., a consecutive, 1-based ordering of hands from most powerful to least.

Central to the Cactus Kev algorithm is the idea of associating a prime number with each card rank:

After a lot of thought, I had a brainstorm to use prime numbers. I would assign a prime number value to each of the thirteen card ranks, in this manner:

Rank Deuce Trey Four Five Six Seven Eight Nine Ten Jack Queen King Ace
Prime 2 3 5 7 11 13 17 19 23 29 31 37 41

 

The beauty of this system is that if you multiply the prime values of the rank of each card in your hand, you get a unique product, regardless of the order of the five cards. In my above example, the King High Straight hand will always generate a product value of 14,535,931. Since multiplication is one of the fastest calculations a computer can make, we have shaved hundreds of milliseconds off our time had we been forced to sort each hand before evaluation.

The prime number is then embedded into a special 4-byte representation of the card…

Cactus Kev's 4-byte card format

…after which a variety of bitwise operations and lookup tables are applied to map the five-card poker hand to a specific equivalence class. I won’t rehash the details of his algorithm here, but they’re well worth reading, as the Cactus Kev evaluator is used and referenced all over the place.

Card Format:

The Cactus Kev evaluator uses a 4-byte integer to represent each card, with bits set according to the format above.

Usage:

First, you’ll want to get your cards into the Cactus Kev format. You can do this either manually or by calling the init_deck function:

// Initialize the deck with Cactus Kev-style card values
int deck[52];
init_deck(deck);

Second, create an array of five cards and pass it into the eval_5hand function. This will return the hand’s official equivalence value. You can get the hand category from the equivalence value by calling the hand_rank function, if necessary:

// Let the hand[5] array be an array of five 4-byte integers,
// each representing a single card in Cactus Kev’s 4-byte card
// format.
int hand[5] = { /* Cactus Kev card values */ };
int eqValue = eval_5hand( hand );
int handCategory = hand_rank(eqValue);

Here’s what a typical hand comparison might look like using the Cactus Kev evaluator:

int lookupHand(int[] theHand)
{
   return eval_5hand( theHand );
}

// Return 1 if hand1 > hand2, -1 if hand1 < hand2
// or zero (0) if they are equal.
int compareTwoHands(int[] hand1, int[] hand2)
{
   int hand1Value = lookupHand(hand1);
   int hand2Value = lookupHand(hand2);

   if (hand1Value < hand2Value)
      return 1;
   else if (hand1Value > hand2Value)
      return -1;
   else
      return 0; // the hands are equal!
}

The only thing to keep in mind: the Cactus Kev evaluator orders hands from 1 (strongest) to 7,462 (weakest) so a hand with value 2302 beats a hand with value 3402.

Limitations:

The Cactus Kev evaluator only handles 5-card poker hands, making it unsuitable for 7-card games such as Hold’em or Omaha. The Cactus Kev source code does include a 7-card evaluation function but it’s unoptimized (as the author admits) and should be avoided in favor of faster 7-card evaluators. Also, the Cactus Kev evaluator should never be used as-is; always include Paul Senzee’s perfect hash optimization.

Cactus Kev’s 5-Card Evaluator with Perfect Hash

Description:

After reading about the Cactus Kev evaluator, a programmer by the name of Paul Senzee took up the source code and made it even faster.

…I came upon Cactus Kev’s Poker Hand Evaluator which is a killer fast five card hand evaluator. Reading through his algorithm you’ll notice that the last step for any yet unclassified hand is a binary search through a list of values. Most hands end up in that search which is the most time-consuming part of the algorithm.

An Optimization

Replacing the binary search with a precomputed perfect hash for the 4888 values in the list yields a significant improvement over the original. The original test code included with Kevin’s source (using eval_5hand) runs in 172 milliseconds on my machine and with my eval_5hand_fast it runs in 63 milliseconds. Yay, an improvement of 2.7 times!

Programmers: forever trying to one-up each other. A 2.7 times improvement is nothing to sneeze at, but we need 1.21 gigawatts of power to blow this pop-stand and head back to the future. Otherwise the evaluator is identical to the original Cactus Kev evaluator described above. In fact, whenever we say “Cactus Kev’s evaluator” we really mean “Cactus Kev’s evaluator with Paul Senzee’s optimization”. Never use the Cactus Kev evaluator without the Senzee optimization. And by the way, Paul also has a 7-card evaluator. Don’t get them confused.

Implementation:

Exactly as in the original Cactus Kev evaluator, except that Paul Senzee’s code replaces Suffecool’s binary search with a perfect hash technique which I’ll show here for novelty’s sake:

// magic
unsigned find_fast(unsigned u)
{
   unsigned a, b, r;
   u += 0xe91aaa35;
   u ^= u >> 16;
   u += u << 8;
   u ^= u >> 4;
   b  = (u >> 8) & 0x1ff;
   a  = (u + (u << 2)) >> 19;
   r  = a ^ hash_adjust[b];
   return r;
}

// Paul Senzee’s optimized version of Kevin Suffecool’s eval_5cards function
int eval_5hand_fast(int c1, int c2, int c3, int c4, int c5)
{
   int q = (c1 | c2 | c3 | c4 | c5) >> 16;
   short s;

   // check for flushes and straight flushes
   if (c1 & c2 & c3 & c4 & c5 & 0xf000) return flushes[q];

   // check for straights and high card hands
   if ((s = unique5[q])) return s;

   // otherwise use hash-based lookup
   return hash_values[find_fast((c1&0xff)*(c2&0xff)*(c3&0xff)*(c4&0xff)*(c5&0xff))];
}
 

The hashing technique is based on Bob “the Hashmaster” Jenkin’s perfect hashing algorithm which I believe Bob, who’s as smart as any ten people, brought with him from his home planet of Xy’torrrrn, where it was used in interdimensional wormhole calibration. Good for a little light reading by the fireplace.

Card Format:

Same as in the original Cactus Kev evaluator.

Usage:

As for the Cactus Kev evaluator, but instead of calling eval_5hand, plug in Senzee’s replacement function, eval_5hand_fast.

Limitations:

Same as per the original Cactus Kev evaluator.

Cactus Kev’s 5-Card Evaluator with Perfect Hash in C#

Description:

Shortly after Paul Senzee published his improvement to the Cactus Kev evaluator, a .NET programmer by the name of Mike Benson came along and ported it all to C#.

Some of you know that I have been playing around with writing poker calculators and poker game simulations. These days, my preferred language is C# so I decided to undertake my project in C#. I happened to run across Cactus Kev’s Poker Hand Evaluator and it looked pretty slick. I have already written my own evaluator in C# and it was pretty fast but this looked like a screamer.

I did the port of the calculator including the modification written by Paul Senzee over to C#. This did not prove to be too difficult and it was done in about 4 hours.

Even if you don’t plan on using the Cactus Kev evaluator, Mike’s post is worth a read as he touches on some interesting performance differences between C++ and C# such as yes C/C++ is still faster than C#. Sorry, people.

The Cactus Kev method does not use logical operators and the C++ version is only 6% faster than the C# version. However, the C++ version using the Paul Senzee modification with the chunk of logical operators is 37% faster in C++ than in C#. That’s quite a difference.

As if one wasn’t enough, Mike actually wrote two ports of the evaluator: one in C# and one in C++, both of which can be downloaded from http://thebensons.org/blog/2006/11/05/c-vs-c-performance-part-ii (you’ll see the links toward the bottom of the page), and exactly one-half of which are included in the XPokerEval project which accompanies this article.

Implementation:

It’s a direct port of the Cactus Kev Improved Hash evaluator from C to pure C#, perfectly performed. What more could you want?

Card Format:

Same as in the original Cactus Kev evaluator.

Usage:

Same as in the original Cactus Kev evaluator, except Suffecool’s original C methods have become static methods inside the PokerLib class:

int [] deck = new int[52];
int [] hand = new int[5];
int [] freq = new int[10];

// Init the deck in the Cactus Kev format..
PokerCalculator.PokerLib.init_deck(deck);

int a, b, c, d, e, handCategory;
ushort handValue;

// loop over every possible five-card hand
for(a=0;a<48;a++) {
   hand[0] = deck[a];
   for(b=a+1;b<49;b++) {
      hand[1] = deck[b];
      for(c=b+1;c<50;c++) {
         hand[2] = deck[c];
         for(d=c+1;d<51;d++) {
            hand[3] = deck[d];
            for(e=d+1;e<52;e++) {
               hand[4] = deck[e];

               // Get the hand’s equivalence class
               handValue = PokerCalculator.PokerLib.eval_5hand_fast(hand);
               // Get the hand’s category..
               handCategory = PokerCalculator.PokerLib.hand_rank(handValue);

               freq[handCategory]++;
            }
         }
      }
   }
}

Sort of the “ah hell, I don’t really want to build a 26-object hierarchy to represent this C-level API so I’ll just create a single class facade and hammer together a few static methods” approach to porting. Good enough for me, and good enough for you, and honi soit qui mal y pense!

Limitations:

Same as the original Cactus Kev evaluator.

The Pokersource Poker-Eval Evaluator for C#

Description:

A C# interface to the Pokersource Poker-Eval evaluator. Written by Johan Euphrosine and included with recent Poker-Eval distributions. Useful for .NET programmers who want to use Pokersource, but who hate (with a fiery Dracula passion) the C-language macro-ized version of it. But if that’s you, consider using the Keith Rule evaluator as it has a richer feature set.

Implementation:

The native C Pokersource code is packaged into a C++ wrapper DLL which can be accessed from a .NET assembly via P/Invoke. As this evaluator is really a thin wrapper around the Pokersource evaluator, it has roughly the same implementation characteristics as that evaluator.

Card Format:

Same as in the original Pokersource evaluator.

Usage:

Here’s how you’d perform a typical hand-vs.-hand analysis:

public void TestPokenum()
{
    // Set up a hand with pockets aces
    StdDeck.CardMask pocket1 = new StdDeck.CardMask();
    pocket1.Set(“As”);
    pocket1.Set(“Ac”);

    // Set up another hand with pocket Queens
    StdDeck.CardMask pocket2 = new StdDeck.CardMask();
    pocket2.Set(“Qs”);
    pocket2.Set(“Qc”);
    StdDeck.CardMask[] pockets = { pocket1, pocket2 };

    // …on a board of Ks Kc Kh
    StdDeck.CardMask board = new StdDeck.CardMask();
    board.Set(“Ks”);
    board.Set(“Kc”);
    board.Set(“Kh”);

    // Don’t include any of the above cards in the enumeration!
    StdDeck.CardMask dead = new StdDeck.CardMask();
    dead.Set(“As”);
    dead.Set(“Ac”);
    dead.Set(“Qs”);
    dead.Set(“Qc”);
    dead.Set(“Ks”);
    dead.Set(“Kc”);
    dead.Set(“Kh”);

    // Enumerate all possible hands!
    int npockets = 2;
    int nboard = 3;
    int niter = 1;
    int orderflag = 0;
    EnumResult result = Enum.Sample(Game.holdem, pockets, board, dead,
                                    npockets, nboard, niter, orderflag);
}

This code is considerably cleaner than the equivalent code written against the native C-language Pokersource routines, and marginally slower, but otherwise, good stuff.

Limitations:

Probably the biggest single problem with this evaluator is that it requires you to package the native Pokersource library as a DLL and export various methods that can be called through P/Invoke. Now hear me well: depending on the platform, compiler, and poker variant you’re targeting, this (building Pokersource as a DLL rather than a static LIB) can be about as much fun as repeatedly bashing your forehead into a brick wall. Pokersource wants to be built as a static library even though it’s perfectly capable of being built as a DLL.

I could ramble on about why this is so, but to heck with it. The code is haunted. I’m out.

Me, after a 32-hour poker session

The Pokersource Java API (Enumeration, Evaluation, and SAIE)

Description:

The Pokersource Java API is a collection of Java source code included with certain versions of the Pokersource Poker-Eval distribution:

The PokerSource Java API gives Java programs the ability to use the fast poker hand evaluator and enumerators in the poker-eval C library. The Java layer also defines higher-level structures that aren’t available in the C library and provides algorithms for working with these structures. The most significant concept available only in the Java layer is the probabilistic hand distribution. The packages provide algorithms for computing pot equity based on subjective belief probabilities of hand distributions.

This is necessary if you want to court Java developers, because no Java developer in his right mind would ever use the C-language version of Pokersource. You would have to handcuff him to a desk, deny him food and drink, perhaps bring out the old Chinese water torture gag. But anyway, the API is divided into three packages. Quoting from the documentation:

  • org.pokersource.enum. Provides classes that enumerate or sample the possible outcomes of a poker hand, define hand distributions, define subjective belief probabilities over hand distributions, and compute subjective pot equity based on these beliefs.
  • org.pokersource.eval. Provides access to the fast poker hand evaluators in the C poker library.
  • org.pokersource.game. Provides utilities for encoding poker cards and hands and converting to and from string representations.

The ability to compute hand vs. hand-range equities across multiple opponents out of the box makes this a win for Java developers. The only problem is that finding the Java API can be a little tough. For whatever reason, it’s not included in the latest Pokersource distribution (poker-eval-134.0.tar.gz, as of this writing). Instead you have to go back to poker-eval-src-121.tar.gz.

poker-eval distributions

Just to make things as confusing as possible, those version numbers will probably change sometime after this article is published, and knowing me, I’ll be too lazy to go in and update this section. So do a cursory check to make sure you’re downloading the latest and greatest.

The Java API is also included in the XPokerEval library which accompanies this article. You want the XPokerEval/XPokerEval.Pokersource/java directory:

There’s even some JavaDoc stuff in there to help you get started.

Implementation:

We’ll examine the implementation of these libraries more closely in a future update as this goes beyond basic hand recognition and into the world of hand-range analysis across multiple opponents. (And yes, that’s Coding-the-Wheel-ese for “I don’t really want to plunge into a two-page explanation of this topic right now, since this post is already longer than I am tall.”)

Card Format:

Same as in the original Pokersource evaluator.

Usage:

Each of the three packages includes a handful of JUnit-compatible tests. These are probably your best resource for learning how to use this evaluator. Here’s a good one:

public void testPotEquity()
{
   // Compare to “pokenum -h ks kh ad td 9c 8c — kd jd th / As 2h”.
   double[] ev = new double[pocketRanks.length];
   Enumerate.PotEquity(Enumerate.GAME_HOLDEM, 0, pocketRanks, pocketSuits,
                  boardRanks, boardSuits, deadRanks, deadSuits, ev);
   assertEquals(0.531707317073, ev[0], 1e-10);
   assertEquals(0.392682926829, ev[1], 1e-10);
   assertEquals(0.075609756098, ev[2], 1e-10);

   ev = new double[pockets.length];
   Enumerate.PotEquity(Enumerate.GAME_HOLDEM, 0, pockets, board, dead, ev);
   assertEquals(0.531707317073, ev[0], 1e-10);
   assertEquals(0.392682926829, ev[1], 1e-10);
   assertEquals(0.075609756098, ev[2], 1e-10);
}

Full-fledged computation of SAIE (subjective all-in equity) in a dozen lines of code, give or take. Java programmers: make sure to lord this over your C++ and C# counterparts, who have to hand-roll a lot of this functionality.

Limitations:

Few to none. With all of the power of the Pokersource library and some of its speed, and with the ability to perform SAIE computation, not to mention leaping tall buildings in a single bound, this library is a strong choice for Java developers, especially those who want robust SAIE computation.

Keith Rule’s C# Port of the Pokersource Evaluator

Description:

One of my favorite all-time CodeProject riffs is Keith Rule’s Fast Texas Hold’em Evaluation and Analysis.

Recently, I was laid up with a prolonged illness. During that time, I needed to entertain myself. I quickly determined that daytime TV was not worth watching. Instead, I started playing Texas Holdem online. A few thousand hands later, I determined that the medication I was taking was interfering with my ability to concentrate. This limited the skill level that I could consistently play at. So instead of continuing to play Texas Holdem poorly, I decided to write a software to analyze poker.

The first thing I realized I needed was a fast way to iterate through and evaluate Texas Holdem hands. After searching the net and trying several libraries, I found that nothing met my needs. Native C# code was too slow, and the best C library (poker-eval) was very difficult to connect to C# using interop. So I started writing my own C# library.

After a few days of work, I was able to evaluate a few hundred thousand hands a second. This was several orders of magnitude slower than I needed. So I looked at poker-eval again. After working through this gnarly, heavily macroed and table driven C library, I realized that the techniques they were using should work with C#. I ported the portion of the tables and evaluation functions that I needed to analyze Texas Holdem. The resulting code is definitely fast enough for most of my needs.

Here’s the “rule”: if you’re building in C#, and you want to use the Pokersource evaluator, and your game is Texas Hold’em, use Keith Rule or you’re fired. Fired from this site. While it’s true that the Pokersource library includes a perfectly good P/Invoke interface for C#, Keith’s code, especially in later incarnations, offers a lot more functionality when it comes to the game of Hold’em.

Implementation:

Keith’s evaluator packages the flat, C-level functionality of the Pokersource evaluator into higher-level abstractions (such as the Hand class) while retaining the low-level, bit-shifting and table-lookup retro disco-dancing goodness of the original. (And you thought encapsulation was just a word they used in classrooms.)

Card Format:

As in the original Pokersource evaluator.

Usage:

The Rule evaluator is documented so well I can’t add much of value other than to give you a taste for the client-side usage. Here’s how you’d decompose a Hold’em hand into its Pokersource hand value:

// Pocket Aces in the hole, with a board of 2d 3d 4d 5d Ac (= A2345 straight flush)
HoldemHand.Hand hand = new HoldemHand.Hand(“Ah Ad”, “2d 3d 4d 5d Ac”);
uint handVal = hand.HandValue;

The Rule evaluator also allows you to compute the equity of two or more hands, either preflop or given some combination of board cards:

// Compare the equity of AhAd vs. Tc2c preflop

string[] pockets = { “AdAh”, “Tc2c” };
long[] wins = new long[2];
long[] ties = new long[2];
long[] losses = new long[2];
long totalHands = 0;

HoldemHand.Hand.HandOdds(pockets, “”, “AdAh2cTc”, wins, ties, losses, ref totalHands);

This really only scratches the surface, of course. Check out Keith’s CodeProject article for the details.

Limitations:

Unlike the Pokersource evaluator on which it’s based, the Rule evaluator only supports Texas Hold’em.

Steve Brecher’s HandEval Evaluator

Description:

Steve Brecher’s open-source Hold’em Showdown tool uses an enumeration/evaluation library called HandEval:

Hold’Em Showdown is a program that you can download and then run on your desktop.   It prompts for information from you which specifies a Texas Hold’Em poker “all-in” situation for two or more players, and then gives you the chances of each player winning (or tying). It works by enumeration rather than simulation. Thus its results are a tabulation of all possibilities of remaining board cards and are exact, rather than an approximation based on an arbitrary number of simulated deals.

Steve played a crucial role in the development of the Two Plus Two evaluator. In fact, the de facto summation of the Two Plus Two algorithm is a test harness in which the Two Plus Two evaluator runs side-by-side with two other evaluators, one of which is the Pokersource evaluator and the other of which is…you guessed it…Steve Brecher’s HandEval. (Check out the XPokerEval.Test3 project to see this in action.)

Unfortunately, the HandEval library is somewhat out of date. In Steve’s words:

Although Hold ‘Em Showdown is more than adequate for typical usage on hands with specific hole cards, it is obsolete for more general scenarios involving ranges of hands (example:  any pair, any suited connectors 98 or better, any two Broadway cards) or scenarios involving players with “random” hands.  (Hold ‘Em Showdown can do enumerations involving up to two players with “random” hole cards, but it can take a long time.)

Every so often I stop by Steve’s site to see what’s cooking, hoping to see an update, and each time I’m disappointed. Maybe Steve will stumble across this post and give us an update.

Implementation:

I’ve never had the time to take a microscope to Steve’s code, so I’m not ashamed to tell you I have no clue how it works

/* This macro uses the variables: Mask_T ranks, c, d, h, s */
#define mStraightAndOrFlush(N)                              
    j = 0;                                                  
    mFlushOrStraightFlush(c,N) else                        
    mFlushOrStraightFlush(d,N) else                        
    mFlushOrStraightFlush(h,N) else                        
        /* total cards in other suits <= N-5; spade flush:*/
        if (!(i = straightValue[s]))                       
            return Value(FLUSH) | hi5RanksMask[s];         
        else                                                
            return (Value(STRAIGHT_FLUSH) - Value(STRAIGHT)) + i;
    if ((i = straightValue[ranks]) != 0)                   
        return i;

#define hand0 ((int)hand & 0x1FFF)
#define hand1 (((int)hand >> 13) & 0x1FFF)
#define hand2 ((int)(hand >> 26) & 0x1FFF)
#define hand3 ((int)(hand >> 39))

…but one thing’s for sure: if you enjoyed dissecting the Pokersource code, you’ll love poking through the innards of the HandEval library. Good times, good times.

Card Format:

Steve’s library uses a format very similar to the format used by the Pokersource and Senzee7 evaluators: a sequence of 52 bits, one for each card. Quoting from the comments in the source code:

A hand is 0 or more cards represented in four 13-bit masks, one mask per suit, in the low-order 52 bits of a long (64 bits). In each mask, bit 0 set (0×0001) for a deuce, …, bit 12 set (0×1000) for an ace. Each mask denotes the ranks present in one of the suits.

If you’d rather not know the gory details, you can use the Encode(rank, suit) macro which is defined as follows:

#define Encode(rank, suit) (1i64 << (suit*13 + rank - 2))

And you’re off! 

Usage:

The best way to learn how to use HandEval is to take a look at the two places where it’s used:

  • In the Hold’em Showdown tool
  • In Steve Brecher’s side-by-side test of the Two Plus Two, Pokersource, and HandEval evaluators

Full source code for both are, you guessed it, included with the XPokerEval distribution accompanying this article.

Limitations:

As noted above, the Brecher evaluator is obsolete when it comes to performing hand range computations. However the Brecher evaluator does offer support for multiple games, which very few of the other standalone evaluators do.

Moritz Hammer’s DAG evaluator

Description:

In A Hand Evaluator for 7-Card Poker Games, Moritz Hammer describes how he used a directed acyclic graph to perform speedy 7-card evaluation.

Cactus Kev has published a very interesting hand evaluator for 5-card games, which assigns each 5-card-hand to an equivalence class which identifies the “worth” of the hand - see the webpage for a very good explanation. A purportedly faster approach uses perfect hashing. I did not want to come up with a perfect hashing for 7-card games (frankly, I would not have known how to do it), but followed a more direct approach that employs a directed acyclic graph (DAG)

 

This is a relatively new evaluator and I don’t know how much time Moritz has had to optimize it, but it’s valuable as a clever exposition of a classic computer science technique which has a ton of practical applications. In fact, the infamous Two Plus Two evaluator described below uses a similar approach.

Implementation:

Take a look at the original post (linked above). There, Moritz explains how a DAG might be constructed for a simple three-card poker game. Extrapolate that outwards to a 52-card deck, and you’ll have a good idea of how this evaluator works. Sort of like figuring out how the 4th dimension works, by looking at the relationship between the 2nd and 3rd dimensions. By the way, just in case it’s not clear from Moritz’s post: The Hammer requires two files:

  • A file, eqcllist, containing the 7,462 poker hand equivalence classes, adapted from the Cactus Kev evaluator.
  • A file, carddag, containing the directed acyclic graph (DAG).

The Hammer ships with both of the above files, as well as the Java code to generate the carddag file, as it’s several megabytes in size. Other than that, the core evaluation code is C++.

Card Format:

Integers, with values between 0 and 51.

Usage:

Assuming you have the eqcllist and carddag files in place, you can call the handeval_init function to initialize the evaluator, and then call the calculate_equivalence_class method as necessary to evaluate hands.

// Load the 2 files needed by the evaluator…
// I’ve modified this function to take 2 filenames
// (in the original code these filenames were hardened)
handeval_init(“eqcllist”, “carddag”);

// Init counters and hand category frequencies…
int count = 0;
int freq[9];
memset(freq, 0, sizeof(freq));

// Used to store our 7-card hand
char hand[7];
handeval_eq_class* eqClass = NULL;

printf(“Enumerating and evaluating all 133,784,560 possible 7-card poker hands… );

for (hand[0] = 0; hand[0] < 46; hand[0]++) {
   for (hand[1] = hand[0]+1; hand[1] < 47; hand[1]++) {
      for (hand[2] = hand[1]+1; hand[2] < 48; hand[2]++) {
         for (hand[3] = hand[2]+1; hand[3] < 49; hand[3]++) {
            for (hand[4] = hand[3]+1; hand[4] < 50; hand[4]++) {
               for (hand[5] = hand[4]+1; hand[5] < 51; hand[5]++) {
                  for (hand[6] = hand[5]+1; hand[6] < 52; hand[6]++)
                  {
                     // Get the hand’s equivalence class
                     eqClass = calculate_equivalence_class(hand);

                     // eqClass->type yields the hand’s category
                     freq[eqClass->type]++;
                     count++;
                  }
               }
            }
         }
      }
   }
}

Note that rather than returning a single number, the evaluator returns a pointer to a handeval_eq_class object which contains various information such as the hand’s number and its human-readable description.

Limitations:

7-card games only.

Timmy’s PokerSim Library

Description:

I love the pokerai.org forums, mainly because of threads like these:

Well, I’m going to make my simulation library open source. This started out as a static library for my own use, but I figured I could make it a DLL and help some folks out.

Anyway, here are the features.
-Written in C++
-Uses the poker-eval library for speed and low memory usage
-Exposes a raw hand rank method
-Includes enumeration functions and Monte Carlo functions
-Can tell you about your hand, such as pair, top pair, flush draw, can an opponent have a straight, etc
-Includes usage example in java
-Includes full source code, compiled DLL (for any real language), and compiled java DLL (uh, for java ;) )
-VERY simple interface
-Can be used for anything you want (although I would like if you gave me credit)

Hey, free code!

Introducing the PokerSim library, a relatively recent, still mostly unknown addition to the poker hand evaluation roundup. This library has some cool functionality, though on the whole it feels like a work in progress. But if, for example, you were wanting to see how poker enumeration and simulation is done in C++, or if you were thinking about building a wrapper of some sort around the Pokersource evaluator, this would be a good place to start.

Implementation:

The PokerSim library is implemented in C++ (but can be used in Java and other languages), and uses the Pokersource Poker-Eval evaluator internally. The code is fairly easy to follow if you’re familiar with how Poker-Eval works:

// PokerSim uses the poker-eval evaluator internally…
unsigned int RankHand(const Hand& shand)
{
    CardMask e; //Used to hold a hand we are evaluating.
    CardMask_OR(e, shand.ours, shand.common);
    return Hand_EVAL_N(e, shand.size);
}

The enumeration and Monte Carlo code is a little trickier, but still pretty much business as usual.

Card Format:

The PokerSim evaluator uses the Pokersource evaluator internally, so it can work with those mask-and-integer card formats. But it’s easier to specify your cards using standard card notation (“AsAc” for example) as shown below.

Usage:

Gotta love any evaluator which lets you compute equity vs. a random opponent in one line of code:

// How does 9c9h fare against a single random opponent?
SimResults r;
SimulateHand(“9c 9h”, &r);
printf(“9c9h wins %.1f%% of the time.”, r.winSd * 100);
// OUTPUT: “9c9h wins 71.7% of the time.

The evaluator can perform hand vs. hand and hand vs. random hand evaluation for a single opponent, and hand vs. hand (but not hand vs. hand range) evaluation against multiple opponents. It also knows how to extract tactical hand elements into a PostFlopState structure.

///General info about a post flop hand.
struct PostFlopState
{
    ///Do we use both of our hole cards?
    bool UsesBoth: 1;
    bool UsesOne: 1;
    bool UsesNone: 1;

    ///What is our actual hand rank?
    bool IsNoPair: 1;
    bool IsOnePair: 1;
    bool IsTwoPair: 1;
    bool IsTrips: 1;
    bool IsStraight: 1;
    bool IsFlush: 1;
    bool IsFullHouse: 1;
    bool IsQuads: 1;
    bool IsStFlush: 1;

    bool IsTopPair: 1;
    bool IsMiddlePair: 1;
    bool IsBottomPair: 1;
    bool IsOverPair: 1;
    bool IsOverCards: 1;
    bool IsStraightDrawPossible: 1;
    bool IsStraightPossible: 1;

    bool IsFlushDrawPossible: 1;
    bool IsFlushPossible: 1;
    bool IsFullHousePossible: 1;

    bool Is3Flush: 1; ///<Do we have exactly 3 cards to a flush?
    bool Is4Flush: 1; ///<Do we have exactly 4 cards to a flush?

    unsigned int FlushOuts: 4;
    unsigned int StraightOuts: 4;
    unsigned int BetterOuts: 4;
};

This starts to bridge the gap between rote hand evaluation and hand recognition, in which we program a piece of software to “understand” a hand in the same way a human might. The above structure parallels that understanding in a basic way. 

Limitations:

Only works with Texas Hold’em.

The Senzee7 Evaluator

Description:

Paul Senzee is well known, in poker-programming circles, for his perfect hash optimization to the original Cactus Kev evaluator and for being an all-around programming BAMF. Not many people are aware that he also has a working 7-card evaluator:

Inspired by the interest in my 5-card poker hand code that plugs into Cactus Kev’s evaluator, I’ve decided to revisit my unholy 7-card evaluator and make a faster?, cleaner one that I can then post up here.

The Senzee7 evaluator, as I like to call it, hasn’t gotten as much attention as it deserves, probably because it takes some work to get up and running, and possibly because it’s somewhat slower than (for example) the Two Plus Two evaluator. But it’s a brilliant example of the power of hashing, which is way more powerful than any single programming technique has a right to be.

Implementation:

The Senzee7 evaluator represents a poker hand as a sequence of 52 bits, of which exactly 7 bits are set—one for each card.

        01100000 00000000 00011000 00001000 00000000 00000000 0101

If you do that, and you treat this bit sequence as a number (for example, an 8-byte integer), you’ll end up with a unique value for each of the 133,784,560 combinations of 7-card poker hands. The largest possible value would be 4,468,415,255,281,664—that’s four-and-a-half thousand trillion gazillion or roughly four-and-a-half quadrillion—corresponding to this bit-pattern:

        11111110 00000000 00000000 00000000 00000000 00000000 0000

The smallest possible value would be the humble 127, corresponding to this bit pattern:

        00000000 00000000 00000000 00000000 00000000 00000111 1111

Given sufficient hardware, you could simply use these 52-bit hand values to index into a lookup table containing the hand’s equivalence class, as Paul explains:

Now, if we had unlimited memory, we could just use this number as an index into an enormous and very sparse array. Unfortunately, this array would have 2^52 (4.5 quadrillion) entries. Assuming two bytes per entry, that would require 9 petabytes of memory! So we need to somehow hash this number into a much smaller space. It turns out that the number of possible combinations of 7 items among 52 is about 133 million (52 choose 7), so ideally, we could somehow hash the 52 bit number into a number between 0 and 133 million that uniquely identifies a given hand.

In other words, you could theoretically recognize any 7-card hand by:

  1. Converting it to a 52-bit number as shown above.
  2. Taking this number as an index into an enormous precomputed lookup table with 4.5 quadrillion entries, most of which are empty.
  3. The value at the index would be the official equivalence class of the hand.

Easy! But until the people at Area 51 decode the trick of peta-scale memory from top-secret alien hardware, that approach is not going to work. Instead we have to compact the size of the lookup table to something we can manage on a typical multi-gigabyte system. And there are only (approximately) 133 million 7-card poker hands, so why not size our lookup table at 133 million members?

This is the reason for hashing: to convert the raw 52-bit hand values into consecutive quantities between 0 and 133 million (approximately). We can then take this hashed quantity and use it to index into a smaller (still large at ~266MB, but feasible) precomputed lookup table containing our hand equivalence values. Our revised 7-card recognition algorithm then looks like this:

  1. Convert the hand to a 52-bit number.
  2. Hash this number to an index between 0 and roughly 133 million.
  3. Use this index to grab a value from a 266MB (133 million entries, 2 bytes per entry) lookup table.
  4. The value so obtained is the official equivalence class of the hand.

Let’s see how it works in practice.

Usage:

Some assembly required!

The Senzee7 evaluator isn’t really a hand evaluator per se, it’s a set of instructions for building a hand evaluator around a particular hashing method. A single file is provided, index52c7.h, which contains a single function of the same name: index52c7. The purpose of this function is to perform the hashing described above, i.e., massaging a 52-bit hand mask into a number between 0 and 133 million.

That’s all. The rest of the code you have to write yourself:

  • The code to generate the 266MB lookup table and populate it with equivalence values
  • The code to use the 266MB lookup table
  • The code to massage hand values into a 52-bit format

But fear not. A full version of this code is provided in the XPokerEval.Senzee7 project accompanying this article. Here’s how you’d go about building that big ol’ 266MB lookup table:

// Allocate memory for 133,784,560 two-byte entries…
short int* hand_lookup = new short int[LOOKUP_TABLE_SIZE];

// Initialize a deck of cards using a 64-bit integer
// (having exactly one bit set) for each card.
__int64 virginDeck[52];
for (int c = 0; c < 52; c++)
    virginDeck[c] = (0x1LL << c);

// Counters & card masks…
int c0, c1, c2, c3, c4, c5, c6;
__int64 h0, h1, h2, h3, h4, h5, h6;
int cactuskev_hand[7];

// Enumerate all 7-card hands…
for (c0 = 0; c0 < 46; c0++) {
   h0 = virginDeck[c0]; // first card
   cactuskev_hand[0] = DeckIndexToCactusKevCard(c0);
   for (c1 = c0+1; c1 < 47; c1++) {
      h1 = h0 | virginDeck[c1]; // 2nd card
      cactuskev_hand[1] = DeckIndexToCactusKevCard(c1);
      for (c2 = c1+1; c2 < 48; c2++) {
         h2 = h1 | virginDeck[c2]; // 3rd card
         cactuskev_hand[2] = DeckIndexToCactusKevCard(c2);
         for (c3 = c2+1; c3 < 49; c3++) {
            h3 = h2 | virginDeck[c3]; // 4th card
            cactuskev_hand[3] = DeckIndexToCactusKevCard(c3);
            for (c4 = c3+1; c4 < 50; c4++) {
               h4 = h3 | virginDeck[c4]; // 5th card
               cactuskev_hand[4] = DeckIndexToCactusKevCard(c4);
               for (c5 = c4+1; c5 < 51; c5++) {
                  h5 = h4 | virginDeck[c5]; // 6th card
                  cactuskev_hand[5] = DeckIndexToCactusKevCar d(c5);
                  for (c6 = c5+1; c6 < 52; c6++) {
                     h6 = h5 | virginDeck[c6]; // 7th card
                     cactuskev_hand[6] = DeckIndexToCactusKevCard(c6);

                     int hashedIndex = index52c7(h6);
                     hand_lookup[hashedIndex] = eval_7hand(cactuskev_hand);
                  }
               }
            }
         }
      }
   }
}

// Okay, the 266MB lookup table has been assembled. Save it.
// (using retro C-style IO here, replace as necessary…)
FILE * fout = fopen(“SENZEE7.DAT”, “wb”);
if (fout)
{
   fwrite(hand_lookup, 2, LOOKUP_TABLE_SIZE, fout);
   fclose(fout);
}

Note that we’re using Cactus Kev’s original (unoptimized!) eval_7hand function to compute the equivalence class for each 7-card hand. That’s fine as the lookup table generation code doesn’t have to be performant since we’ll only be generating this file one time.

Once we’ve got our 266MB lookup table built and saved, we’re ready to actually use the evaluator. Here’s how you’d go about enumerating and evaluating all possible 7-card poker hands:

// Load the SENZEE7.DAT file, or build it if it can’t be found…
short int* theTable = load_table(“SENZEE7.DAT”);
if (!theTable)
   return -1;

// Set up a new deck. Let each card be a 52-bit sequence with
// exactly one bit set. The “0x1LL << c” sets the cth bit.
__int64 virginDeck[52];
for (int c = 0; c < 52; c++)
   virginDeck[c] = (0x1LL << c);

// Store hand category (flush, two pair, etc) frequencies here..
int freq[9];
memset(freq, 0, sizeof(freq));

// Counters and card masks
int c0, c1, c2, c3, c4, c5, c6;
__int64 h0, h1, h2, h3, h4, h5, h6;

printf( Enumerating and evaluating all 133,784,560 million possible 7-card hands… );

for (c0 = 0; c0 < 46; c0++) {
   h0 = virginDeck[c0]; // 1st card
   for (c1 = c0+1; c1 < 47; c1++) {
      h1 = h0 | virginDeck[c1]; // 2nd card
      for (c2 = c1+1; c2 < 48; c2++) {
         h2 = h1 | virginDeck[c2]; // 3rd card
         for (c3 = c2+1; c3 < 49; c3++) {
            h3 = h2 | virginDeck[c3]; // 4th card
            for (c4 = c3+1; c4 < 50; c4++) {
               h4 = h3 | virginDeck[c4]; // 5th card
               for (c5 = c4+1; c5 < 51; c5++) {
                  h5 = h4 | virginDeck[c5]; // 6th card
                  for (c6 = c5+1; c6 < 52; c6++) {
                     h6 = h5 | virginDeck[c6]; // 7th card

                     // Hash the raw hand value to a good index
                     // using Paul Senzee’s index52c7 method.
                     int hashedIndex = index52c7(h6);
                     // Fetch the hand’s equivalence class using this index
                     int eqClass = theTable[hashedIndex];
                     // Fetch the hand’s category using Cactus Kev’s hand_rank
                     int handCategory = hand_rank(eqClass);
                     // Increment the appropriate spot in the hand category array
                     freq[handCategory-1]++;

                     // Above 4 lines of code can be combined:
                     //freq[ hand_rank(theTable[index52c7(h6)])-1 ]++;
                  }
               }
            }
         }
      }
   }
}

Again, keep in mind that the only function provided by the Senzee7 evaluator is index52c7. The other functions such as load_table and so forth have to be written by you. (And again, you can find sample implementations in the XPokerEval.Senzee7 project.)

Limitations:

The biggest problem with this evaluator is a) the fact that you have to write some code to complete it and b) its performance relative to the Two Plus Two and Pokersource evaluators.

The Two Plus Two Evaluator

Description:

Way back in 2006 a newcomer to the 2+2 poker forums opened Pandora’s box by asking the following question:

Hi all, new user but was told this was a good poker forum!

Not sure if this is the right place to post, please move if it is not.

Anyway, I’ve finished writing a 7 card hand evaluator for poker, and I’m managing to get around 14-15 million hands evaluated per second (14,000 to 15,000 per millisecond). I’ve had a look around, and is anyone aware of any software that can beat this?

Tests where done on a P4 2.4ghz PC with 1GB Ram.

Thanks for your time!

The result was an epic, twenty-four page discussion which should be required reading for every programmer. That discussion yielded one of the cleverest pieces of software in approximately forever, and what is arguably the fastest publically available poker evaluator in the world: the Two Plus Two evaluator, also referred to as the “RayW evaluator” after Ray Wotton, who first put together the reference version of the code, though as near as I can tell, the credit should go like this:

  • Ray Wotton. Reference version of the code.
  • mykey1961. Suggested the table-driven approach.
  • Steve Brecher. Side-by-side test harness, etc.
  • jukofyork. Combinadics support, Bose-Nelson.
  • Kevin Suffecool. Equivalence classes.
  • Andrew Prock. For participating, and for writing PokerStove.
  • D&L. For keeping the discussion going with some good ideas.
  • Various others on the 2+2 list…

In the Wild West of poker evaluation code, this one is a six-gun slinging, dual-wielding, down and dirty son of a Booch. And don’t you forget it.

I'm your huckleberry

Implementation:

The Two Plus Two evaluator consists of a large lookup table containing some thirty-two million entries (32,487,834 to be precise).

That’s all! Class dismissed.

In order to lookup a given 7-card poker hand, you trace a path through this table, performing one lookup per card. When you get to the last card, the value so obtained is the official equivalence value of the hand. (Note: the lookup table can also be used to perform 5-card and 6-card evaluation.)

Imagine:

You’re standing on the edge of a chessboard which stretches to the far horizon. Instead of 64 squares, there are, eh, around 32 million. Someone deals you a random 7-card poker hand. You look at your first card, which happens to be the Seven of Hearts. Looking around you, you notice that there are 52 squares nearby, each of which is labeled with a specific card in the deck. So you move to the square labeled “Seven of Hearts” because that’s what your first card was.

You then look at your second card, which happens to be the Ace of Diamonds. Well, if you look down at your feet, you’ll notice that the square you’re on contains a set of directions:

  1. If your second card is the Ace of Spades, go to Square #43,072.
  2. If your second card is the Ace of Hearts, go to Square #43,073.
  3. If your second card is the Ace of Diamonds, go to Square #43,074.
  4. And so on, for each of the remaining cards.

So you follow the directions, and move on over to Square #43,073. When you get there, you take a look at your third card, which happens to be a lowly Deuce of Clubs. Once again, you look down at your feet:

  1. If your third card is the Ace of Spades, go to Square #192,093.
  2. If your third card is the Ace of Hearts, go to Square #192,094.
  3. …and so forth
  4. If your third card is the Deuce of Clubs, go to Square #293,304.

Once again, you follow the directions, and head over to square 293,304, which takes a couple days. Here you look at your fourth card, follow the directions taking you to yet another square, where you look at your fifth card, and so forth, until you’ve gotten to the square for the seventh and last card. Glancing down at your feet one last time, you see a number, glowing crimson and shooting out sparks of electricity:

1582

That number tells you everything you need to know about everything. It is (you guessed it) the official Cactus Kev-ian equivalence value of your 7-card hand! (Okay, slight simplification here. In practice, this number is actually the hand’s equivalence value, merged with the hand’s category. But you get the point).

Expressing the above “chessboard metaphor” in code, we get something like this: a dozen of the most succinct, aggressively optimized lines you will ever lay eyes on:

// The “chessboard” / lookup table.
int HR[32487834];

// Navigate the lookup table
int GetHandValue(int* pCards)
{
    int p = HR[53 + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    return HR[p + *pCards++];
}

A lookup table, and seven array lookups, to evaluate any 7-card poker hand in any order in no time flat. Somebody call Dr. Masaaki Hatsumi, because this is straight software development ninjitsu.

Yet another ninja

See, I promised you scantily-clad ninjas and I delivered.

Usage:

Before you can use the Two Plus Two evaluator, you have to generate the HANDRANKS.DAT file which contains the lookup table. At 123 megabytes, this file is too large to casually pass around; so instead you have to run some code to generate it on your machine. This will only need to be done once.

If you’re using the XPokerEval library, you can find the Two Plus Two table generation code in the XPokerEval.TwoPlusTwo project. Build and run this project (it will take a few minutes) and you’re good to go.

Once that’s done, using the evaluator is easy:

// The one and only lookup table.
int HR[32487834];

// Initialize the 2+2 evaluator by loading the HANDRANKS.DAT file and
// mapping it to our 32-million member HR array. Call this once and
// forget about it.
int InitTheEvaluator()
{
    memset(HR, 0, sizeof(HR));
    FILE * fin = fopen(“HANDRANKS.DAT”, “rb”);
    // Load the HANDRANKS.DAT file data into the HR array
    size_t bytesread = fread(HR, sizeof(HR), 1, fin);
    fclose(fin);
}

// Given a group of 7 cards, return the hand category & rank. Let
// pCards be (a pointer to) an array of seven integers, each with
// a value between 1 and 52.
int GetHandValue(int* pCards)
{
    int p = HR[53 + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    return HR[p + *pCards++];
}

// Set up a 7-card poker hand, perform the lookup into the table, and
// extract the category (full house, two pair, flush, etc.) and rank
// (within that category) of the hand. These numbers can be used to
// compare the hand to other hands.
void DoSomeWork()
{
    int myCards[] = { 2, 6, 12, 14, 23, 26, 29 };
    int handInfo = GetHandValue(myCards);
    int handCategory = handInfo >> 12;
    int rankWithinCategory = handInfo & 0x00000FFF;
}

One more thing: although the Two Plus Two evaluator uses equivalence classes, it tweaks the format slightly. Cactus Kev’s equivalence classes establish an ordering of hands from 1 to 7,462. The Two Plus Two evaluator reorders these 7,462 equivalence classes within hand categories: flush, full house, two pair, and so forth. So for each evaluated hand you end up with two pieces of information: 

  • A hand category 
  • A rank within that category

And that’s why you’ll frequently see this, to retrieve the hand category:

    int handCategory = handInfo >> 12;

And this, to retrieve the rank within the category:

    int rankWithinCategory = handInfo & 0x00000FFF;

Just a slightly different way of structuring the same information.

Limitations:

The Two Plus Two evaluator only supports 5-card, 6-card, and 7-card evaluation.

Naive Hand Evaluators

Description:

No poker hand evaluator roundup would be complete without mentioning what is probably the most common type of poker hand evaluator: the naive hand evaluator.

Also known as: any hand evaluator which tries to evaluate a poker hand in a way which intuitively makes sense. Thousands of naive evaluators have been written, thanks to end-of-chapter programming exercises like this one:

Write a program to deal 1,000 random 5-card poker hands, and for each hand dealt, determine the type of poker hand it constitutes: high card, one pair, two pair, three of a kind, straight, flush, full house, four of a kind, straight flush, and royal flush.

You might even have written one or two of these yourself. I know I have.

Implementation:

Naive hand evaluators follow the common-sense approach to hand evaluation. If your hand evaluator…

  • Sorts the hand, prior to evaluating it.
  • Iterates across the hand, collecting rank and suit information as you go.
  • Makes specific tests to check if the hand is a certain type of hand, such as a Flush.
  • When hands are compared, first compares the hand category, and when the hand categories are equal, compares the individual “pieces” of the hand.

…then your hand evaluator might be a redneck naive.

If you want to see a perfect specimen of naive hand recognition, check out some old hand recognition code by K.N. King. Now, obviously K.N. King wasn’t trying to build a performant hand evaluator: he was trying to demonstrate the C programming language. And there’s nothing wrong with “naive” code, just so long as it’s not so time-consuming as to restrict your ability to perform enumerations and simulations on the fly, in a real-time poker bot or strategy tool setting.

Card Format:

Varies.

Usage:

Varies.

Limitations:

Naive hand evaluators are slow, plain and simple, at least compared to other evaluators. All that sorting, iterating, and conditional branching takes time. While naive evaluators can be built which are fast enough for basic Monte-Carlo style analysis, there’s probably never a reason to use them, considering there’s so much solid, highly-optimized code available for free.

The XPokerEval Library

Download the XPokerEval source code (1.8MB).

Well, I hope it’s clear that there’s a lot of hand evaluation source code out there. Finding, downloading, and building this source code can be a lot of fun, if fixing obscure compiler errors and platform dependency issues into the wee hours of the morning, growing steadily more and more enraged, is your idea of fun.

If it’s not your idea of fun, then you might want a clean, easy-to-build library which contains tested source code for multiple poker hand evaluators. You might want something you can download, build, and use, without fussing around with the fact that the Cactus Kev evaluator uses a drand48 function not found on Windows, or that you have to scrape the 2+2 evaluator source code, like roadkill, from an obscure forum archive.

The purpose of the XPokerEval library is to take all this open-source code and group it into an easy-to-build Visual Studio solution for Windows platforms. Now, I know what you non-Visual Studio, non-Windows types are thinking: we get the shaft, again. But you haven’t been forgotten. The XPokerEval distribution includes the original Java sources, just not as formal parts of the Visual Studio solution. And although the C/C++ code uses Windows-specific constructs, it does so in a superficial way.

  • For example, I’ve used GetTickCount to provide basic evaluator timing (poor man’s benchmarking).
  • For example, I’ve replaced the default RNG used by many evaluators with a Mersenne Twister implementation which may or may not be platform-independent. Haven’t checked.

So you would need to go through the code and spot-replace whatever doesn’t work. But if you build software on *nix, you’ve probably gotten pretty good at this. What can I say, hand evaluation code is liquid and organic stuff, forever requiring tweaking.

Conclusion

This article will be updated from time to time, as new evaluators appear, and as changes are made to existing evaluators. If you know of an evaluator not included in this list, or you are the author of such an evaluator and want it included or removed, let me know.

Apologies for the C, C++, and C# language bias in this article, and I hope I’ve made it clear that most of these evaluators, including the Two Plus Two evaluator, exist in multiple languages. If you’re a Java programmer, I recommend you check out some recent pokerai.org hand evaluation contests, where you’ll find a plethora of excellent evaluators for Java, in addition to those mentioned here.

One day soon we’ll discuss how to take all this hand evaluation code and use it to perform full-on SAIE analysis for multiple opponents and hand ranges and in general, take over the planet with our poker software. Until then, good luck, and leave us a comment if you have liked or disliked what you’ve witnessed here today.

Comments

  • Anonymous says:

    LOL!

    Fantastic post!

    Brav-o!

    Andale!

    All except for the part with the Grudge. That sucked. Also, the 4th link is broken.

  • Terry Smith says:

    Awesome post! Thank you for the Ninja Chicks! (and the poker stuff)

  • Chris says:

    Hot Damn!

    Fantastic post!

    Really, really digging this series. Keep up the GREAT work!

  • Anonymous says:

    Woot! Timmy’s PSim Library for the win. By far the easiest to use from any language.

  • Andrew G. says:

    w00t. Great post.

  • Terry Smith says:

    By the way folks, did you notice that TWICE now, after an intermitable wait, I’ve posted Daniel Radcliffe related posts and we have almost immediately received a poker bot posting.

    Although this is completely chance, I take total credit for it.

  • Anonymous says:

    Nice, glad it finally came!

  • Ian says:

    Wow. This looks sweet. I’ll have to go over it in detail when I have more time. Great job.

  • Ed K. says:

    James do you have any metrics on these evaluators.. I know the 2+2 is "the fastest" but on the 2+2 thread it looks like poker-eval still wins for a 1-million random hands evaluation… I see all the test proj have basic timing info, what are your results? Maybe we can get a followup "battle of the evaluators" post that goes into that, though I’d also like to see some more botting mechanics picking up from part 4-7

    ie Can we get a trashbot or a rules-based foldbot up and running?

  • Ed K. says:

    Edit: I mean a bot which uses one of the above evaluators so actually has an idea whether it has "garbage" or "non-garbage"… shouldn’t be too hard right?

  • Anonymous says:

    LOL @ Terry’s comment. I have noticed the same thing…. but remember w/ great power comes great responsibility. The ‘Radcliffe technique’ is so powerful it needs to be used cautiously.

  • Terry Smith says:

    True. But nevertheless, I shall acquire a spandex uniform, and when not running the poker bot I’m going to build after reading all the posts to come, I shall fight crime as RADCLIFFE POST MAN!

    Who wants to be my teenage sidekick?

  • Anonymous says:

    Single most detailed discussion of this topic I’ve ever read, and great links & discussion of the source material. Keep it coming!

    By the way, you forgot the UoA evaluator…

  • Terry Smith says:

    Hi - I don’t know if you noticed this, but the RSS feed is not updated, after a day. It’s been instant up to now.

  • Poker Forums says:

    Nice post mate keep up the good work!

  • Anonymous says:

    "I’ve packaged the complete source code for every evaluator into a single, easy-to-build library"

    you are the "Prince of Poker Bots"

  • Jason says:

    Thanks for this series. I wrote a poker bot about 3 years back that broke even on Party Poker at low stakes. I abandoned it after realising that I could make more money without the bot and have since lost the code :(

    This series is really making me want to give it another go.

  • Atte says:

    Hi.

    Just wondering, what’s the card-format with 2+2 evaluator? There’s straight integer for each card, but is for instance number 2 an ace or deuce?

  • Numb Nuts says:

    Thank you for the Hand Evaluators. Like many others however, I am not quite sure what to do with them. When I bring it up in VS 2008 I get "not supported" messages for some of the projects (which we are warned about in the ABOUT.TXT file). So I was trying to get something usable from the CactusKev Perfect Hash Test. Is that a reasonable choice to start with? Will it eventually produce an EXE file, or is it not that easy?

  • Paul Senzee says:

    This is absolutely phenomenal and extremely informative. Thank you so much for all this work (and for finishing my seven hand evaluator!)

    One of these days I’ll have to get back to looking at hand evaluation..

  • Coding the Wheel says:

    [i]>So I was trying to get something usable from the CactusKev Perfect Hash Test. Is that a reasonable choice to start with? Will it eventually produce an EXE file, or is it not that easy? [/i]

    So there are two projects per evaluator: the evaluator itself, and an executable (.exe) which tests the evaluator, or at least demonstrates sample usage. Now if you do a Rebuild all you should find a number of .exe files being emitted into the /bin directory. Just run and you should, assuming everything’s working, see some output. You could also build them individually, eg, right-click the project and "Build Only".

    I’ll be posting an updated version of the library shortly with some cleanup but yes, assuming a normal VS2005/2008 box you should be able to build and run the .exe’s.

    [i]>Thank you so much for all this work (and for finishing my seven hand evaluator!) [/i]

    Thanks Paul, and index52c7.h is all that’s needed! Let us know if you get some time to do more hand-eval stuff.

  • Alex C says:

    I just downloaded this project and built it in VS2008. I tried running the PokerSource.KeithRule.Test.exe and was getting an argument exception to the HandAnalysis function. It appears you are passing in the pocket and board cards as dead cards which causes the problem. I believe the function automatically places the pocket and board cards in the list of dead cards without having to explicitly tell it to. I just thought I would mention it here for anyone else running into this problem and in case you decide to update the project later. Changing the dead cards string being passed to HandAnalysis to and empty string fixes the problem so as I understand it you should rarely need to pass anything for the dead cards. Is there a (non-special) case where you would need to pass this parameter?

  • ehsanul says:

    Thanks a lot for this James. An interesting read, and funny as usual, which is great. Took a long time to get started with reading the whole blog, it being dauntingly long, but it was definitely worth the read. I hope to be building the project you’ve provided as soon as I get Windows back.

  • Nyx says:

    Thumbs up! & Thank you!

  • Jens says:

    This were some difficulties I encountered trying to make the java api work in eclipse after downloading poker-eval-src-121

    I don’t have windows or visual studio, so, here’s what I did

    first, trying to build didn’t work

    ./configure

    running make will give errors about the enum keyword being used
    so you have to compile using java 1.4
    change the makefile in the java dir and ad after JAVAC = javac:
    JAVACOPTS += -source 1.4

    running make now wil give errors about including bits/stdio
    (because the make file includes to much dirs)
    change the line
    JNI_INCLUDES := $(addprefix -I, $(shell find $(JDKHOME)/include -type d))
    to something like
    JNI_INCLUDES := -I /usr/lib/jvm/java-6-sun-1.6.0.06/include/ -I /usr/lib/jvm/java-6-sun-1.6.0.06/include/linux/

    (you have to include the two dirs that include jni.h and jni_md.h)

    now make
    it should complete now
    copy poker-eval-src-121/lib/libpoker.so and poker-eval-src-121/lib/libpokerjni.so to /usr/lib/

    after this you should be able to pass all the tests in eclipse after importing the java dir
    (first add junit-3.7.jar and jakarta-oro-2.0.6.jar to the build path)

  • Adrian20XX says:

    In the way you are approaching this, by focusing on evaluation only, and not in evaluation in relation to enumeration, you’ll get to perfomances that are very very far from the achivable performance, by more than one order of magnitude.
    http://forumserver.twoplustwo.com/45/software/programming-efficient-evaluators-common-cards-games-299960/

  • Anonymous says:

    Adrian,
    You are a day late, and a dollar short.

  • Anonymous says:

    The 2+2 evaluator uses a 120+ megabyte lookup table. The test case in the XPokerEval package times it with a deep, 7 level nested loop, which makes the table lookups very cache-friendly.

    If this is not the case with your application (e.g. you use the code in non-exhaustive simulations where a random number generator is involved), the results may vary, that is, the evaluator will become much slower due to bad cache behavior.

  • Anonymous says:

    Excellent articles so far!

    I understand what the hand categories are, in the TwoPlusTwo test, but what does the salt represent?

    If I can figure that out, I’ll be happy :)

  • Anonymous says:

    Ahh, I see. It’s a ranking of the hand dealt against all other hand values. I was expecting SAIE, but I suppose that SAIE needs to be calculated separately, based on the values gotten here. Thanks! :)

  • Anonymous says:

    James Devlin, you do great work! Thank you very much!
    PS: Sorry for my English =)

  • Kyle says:

    Ugh C++. I’m new to programming and starting to get good with VB.NET(3.5). Could somebody help me out with using the Two Plus Two evaluator with VB.NET? Thanks alot!

  • Anonymous says:

    omg, this might seem like a really, really dumb question, but… when passing cards to the Two + Two evaluator function, how are they enumerated from 1-52?

    As in, is it
    1 = Ac
    2 = 2c
    3 = 3c

    Or what? Please help, thanks!

  • Anonymous again says:

    2c = 1 2d = 2 2h = 3 2s = 4
    3c = 5 3d = 6 3h = 7 3s = 8
    4c = 9 4d = 10 4h = 11 4s = 12
    5c = 13 5d = 14 5h = 15 5s = 16
    6c = 17 6d = 18 6h = 19 6s = 20
    7c = 21 7d = 22 7h = 23 7s = 24
    8c = 25 8d = 26 8h = 27 8s = 28
    9c = 29 9d = 30 9h = 31 9s = 32
    Tc = 33 Td = 34 Th = 35 Ts = 36
    Jc = 37 Jd = 38 Jh = 39 Js = 40
    Qc = 41 Qd = 42 Qh = 43 Qs = 44
    Kc = 45 Kd = 46 Kh = 47 Ks = 48
    Ac = 49 Ad = 50 Ah = 51 As = 52

    For any of the rest of you who might be confused.
    "Hand Ranks" "Index"

  • SteveT says:

    Does the Two+Two evaluator evaluate a 7 card hand when only 5 or 6 cards are currently known.

    In other words can it say when we input 5 or 6 cards that there is a flush draw, straight draw, up-down straight draw, gunshot draw?

    Which would be rather handy for evaluating your situation on the flop and turn.

  • Anonymous says:

    The Two Plus Two Evaluator is my favorite by far for its simplicity and the fact that it works like a graph, allowing me to, for example, traverse 5 community cards and then evaluate every possible hole card to determine how a given set of hole cards compares with all the possibilities very quickly.

    However, I am having trouble getting it to work with 5-card evaluation. This would be useful for analyzing your current hand statistics after the flop (pass in the 3 community cards from the flop and then traverse every possible set of hole cards and see where yours ranks up).

    Naively I tried to do simply this:
    int LookupHand(int* pCards)
    {
    int p = HR[53 + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    return HR[p + *pCards++];
    }

    Simply the same code for 7-card evaluation but with a couple of traversal lines taken out. That fails to work and I end up getting invalid categories like 1739 when doing this.

    Could you tell me what I’m doing wrong? I tried looking up further documentation for the Two Plus Two Evaluator but found none on the web and I tried reading a lot of the lengthy discussion on the forum about it but couldn’t find anything (though it is incredibly long and I must admit I skimmed it).

  • Anonymous says:

    SteveT: it won’t do it automatically, but it’s easy enough to tell when there is a flush or straight draw. Just enumerate all the possible remaining cards with the 6 cards you have and then see if any of them makes a straight or flush. If so, you have a straight/flush draw. You can also compute the chances of hitting it based on how many of those remaining cards make the flush/straight out of the total remaining number of cards.

  • Loic Dachary says:

    Hi,

    I’m impressed by the lack of communication. I’ve been the maintainer of pokersource (and the poker-eval library) for many years now. And we’ve never seen you on our mailing lists once or even on our IRC channel. Evaluating Free Software code involves communication. Code is a language between human beings because it’s interpreted by the computer on behalf of someone, ultimately ;-)

    You are kindly invited to talk with us, Free Software poker developers. We are a friendly community and welcome visitors and new members warmly. You can find us at Pokersource Users <pokersource-users@gna.org> and irc.freenode.net#pokersource

  • Anonymous says:

    how to implement
    ‘int GetHandValue(int* pCards)’ method
    for
    "just 5" cards
    or
    just "6 cards" ?

  • Mathias says:

    Hello!
    I have the same problem as Anonymous who posted on 10/16/2008 9:19:41 PM (60 days ago). I did the same and end up with results that are incorrect. any help would be very appreciated!

  • Eli says:

    Hello!

    This is an amazing article, I came by accidently and it really got me into it !!
    * Salut ! *

    My question is how can I determine which 5 cards actually compose the returned rank?
    At the moment I’m experimenting with the 2+2 algorithm.

    Example:
    Ah Ac As Ad 2h 3D 4S

    Winning hand is ‘Ah Ac As Ad 4h’ (Four of a Kind).
    I’d like to be able to retrieve those int values in an array.

    Maybe someone here already figured it out?

    This is some really interesting stuff, too shame I’m no math guru (:

    Anyway, thank you all authors and poster for this.

    Eli, Israel.

  • Eli says:

    Want to note that this is not understandable and if someone could shed a light then I think that my previous question would be answered:

    2+2
    generate_table.cpp

    [quote]
    if (numcards == 6 || numcards == 7)
    {
    // an extra, If you want to know what the handrank when there is 5 or 6 cards
    // you can just do HR[u3] or HR[u4] from below code for Handrank of the 5 or 6 card hand
    HR[IDnum * 53 + 53] = DoEval(IDs[IDnum]); // this puts the above handrank into the array
    }
    [/quote]

    .. Anyone?

    Eli.

  • Mathias says:

    Eli: I don’t think the code you pasted has anything today with the code excerp you pasted. this is just a hint how to calculte the the handrank for a 5- or 6-card hand, like if you want to emulate draw poker hands.
    i dont know no the answer to your question, but keep in my mind, that sometimes, there are more than one possibilities(for example AAAAKKK).

  • Mathias says:

    Anonymous:

    the LookUpHand-function for a five-card hand looks like this:

    int LookupHand(int* pCards)
    {
    int p = HR[53 + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards];
    return HR[p];
    }

  • Anonymous says:

    And for 6 cards it’s this:

    int LookupHand(int* pCards)
    {
    int p = HR[53 + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards];
    return HR[p];
    }

    In other words, for a 7 card hand you do 7 jumps, for a 6 card hand you do 6, and for a 5 card hand you do 5. But for 5 and 6 card hands, rather than returning the final value for ‘p’, you return HR[p].

  • W. Marvel says:

    Code that will look up a hand of length n using the TwoPlusTwo table (note that only 5, 6, 7 are valid length values, you will get a -1 return for any other value).

    This might be useful if you’re going to be dealing with evaluating hands of different lengths.

    [code]
    // After lookup of at least a full hand of cards, HR[p] holds hand strength
    int LookupCards(int* pCards, int len)
    {
    int p = HR[53 + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];

    switch (len)
    {
    case 6:
    p = HR[p + *pCards++];
    // No break - falling through on purpose

    case 5:
    // Return it directly
    return HR[p];

    case 7:
    p = HR[p + *pCards++];
    return HR[p + *pCards++];

    default:
    // Only handles 5, 6, 7
    return -1;
    }
    }
    [/code]

  • brian says:

    hi. i’m brian from denmark.

    and this page is damn impressive. i got the idea that i would build an poker simulator, evaluator. but with the math put into it on this page here. hmm. i can see, i’m simple not good enough to do it or it would take years researching.
    but thz. for a super insight into the poker programming world.

  • Cactus Kev says:

    Every now and then, I google my name just to see what "teh Internets" have to say about me. This time, I was pleasantly surprized to find your penultimate poker evaluator article. Well done indeed! Clever writing, love the ninja hotties, and quite an enjoyable read. As a former Math/CompSci major, I wish they would have taught this sort of stuff back when I was a n00b programmer. Of course, back then, it was punched cards and FORTRAN. Old school, baby! Anyways, kudos on your exemplary article.

  • Dick says:

    James, Thanks for all that you have done!

    I saw a request for any info on how to implement 2+2 in VB from 10/30/08
    If anyone has anything showing VB code, I would appreciate it very much.

    I think memset is my major problem.

    I also have an interest in PSIM. Struggleing with the bit map in GetHandState

    Thank you

  • Henk says:

    Are there also any poker strategy "calculators"? Open source of course. Because this, as far I understand, calculates the cards winning potential based on mathematics… right? But there’s a whole lot more to it, such as:

    - Strategy.
    - The way your oponents play. So you need sort of a oponent analyzer for that.
    - Scripts that determines/suggests the style of play against certain oponents. Obviously this one is a bit harder.
    - And probably I missed out on some things. But you get the point.

    So what would be really cool is if you also have some sort of library that has things for each and every single module that should go inside a pokerbot. Maybe then we can even help eachother to create the ultimate solution. Or something close, anyway :)

  • Stefan says:

    Is anyone else having trouble with the 2+2 giving accurate results?

    I’ve heavily debugged the creation of the precalculated values, and can’t seem to track down where the problem is coming from.

    However, it is evaluating all straights incorrectly, they come back as a very low hand (around 10).

    I’ve verified the straights are calculated correctly and placed into the pre-calc array with the right value, but something must be amiss in the way the pointers work… Just can’t get reliable values out of it… Is there some trick I’m missing?

  • Valentin says:

    Hi. Please, can you help me?
    Is it possible to know all 5 card’s ranks that was involved in Texas Hold’em combination, using Steve Brecher Hand_7_Eval?
    For example, if I have high card combination, is it possible to determine 5 biggest cards (High + 4 kickers)?

  • Eliot says:

    Great summary!

    I admit, I was "naive" and used 40 computers (the nice thing about working at a university) to run 100k rounds of the game for a Monte Carlo sim to get the house edge of a poker based casino game. It only took a full weekend for each computer to do its 2500 round share (a round consists of 2334960 hand evaluations — so 2500 rounds is a petite 5,837,400,000 hands).

    Using Cactus Kev’s fast, I’m down to one machine in just over 4 hours for 100k rounds of the game. I compute that it will take 130 days to run a full cycle for this casino game using CK.

    I have to evaluate a total of 166,882,860,144,000 5 card hands for the full cycle (yeah, only 166.9 trillion — small) … I’m going to try 2+2

    Thanks CK for pointing out this article and for the great humor here.

  • chuck chode says:

    "…unlearned about sixty-two months of best-practices software development…"
    the perfect line. made me laugh thx

  • Marc says:

    Anyone have an idea of how much of a slow-down it would be to use a memory-mapped file in the 2+2 evaluator?

    Thanks,
    Marc.

  • Marc says:

    So I ran a test using [url]http://en.wikibooks.org/wiki/Optimizing_C%2B%2B/General_optimization_techniques/Input/Output[/url]. Some pretty interesting behavior. First time I ran XPokerEval.TwoPlusTwo.Test.exe, it took 5187 milliseconds. Subsequent runs happen in roughly 600 milliseconds (some system caching going on?). I am guessing the real test would be to do some Monte Carlo evaluations.

  • Orwellophile says:

    I think there is a problem with the 2+2 LUT implementation. No matter how I use it, I am getting results that don’t match PokerStove.

    Rather than paste the pages of code, I thought I’d try and find a specific example… it should show KK beating QQ.

    4d 5c Ah 9c Js Kh Ks Result: 333d
    4d 5c Ah 9c Js Qh Qs Result: 333d

    Code is running under Unix (32 bit), has also been tested in Win32, and .dat file was compiled under windows. MD5 for the .dat file is 5de2fa6f53f4340d7d91ad605a6400fb

    I have written a Monte Carlo and an iterative "Hole vs Unknown" function, but neither is usable due to the 5-10% inaccuracy.

    The only error I can conceive is that I misunderstood the numbering of the cards - see the top of the full code listing for an array showing my understanding.

    Code summary:

    // 4d 5c Ah 9c Js Kh Ks
    int h1[] = {4-1 + 13, 5-1, 14-1 + 26, 9-1, 11-1 + 39, 13-1 + 26, 13-1 + 39, 0};
    int h2[] = {4-1 + 13, 5-1, 14-1 + 26, 9-1, 11-1 + 39, 12-1 + 26, 12-1 + 39, 0};

    v1 = GetHandValue(h1);
    v2 = GetHandValue(h2);

    Complete code (will compile under win32 with "cl.exe test.cpp" or under Unix with "make test.c" :

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    char *card_names[] = { "??",
    "2c", "3c", "4c", "5c", "6c", "7c", "8c", "9c", "Tc", "Jc", "Qc", "Kc", "Ac",
    "2d", "3d", "4d", "5d", "6d", "7d", "8d", "9d", "Td", "Jd", "Qd", "Kd", "Ad",
    "2h", "3h", "4h", "5h", "6h", "7h", "8h", "9h", "Th", "Jh", "Qh", "Kh", "Ah",
    "2s", "3s", "4s", "5s", "6s", "7s", "8s", "9s", "Ts", "Js", "Qs", "Ks", "As",
    "Er"
    };

    int HR[32487834];

    // MD5: 5de2fa6f53f4340d7d91ad605a6400fb HandRanks.dat
    int InitTheEvaluator()
    {
    memset(HR, 0, sizeof(HR));
    FILE * fin = fopen("HandRanks.dat", "rb");
    if (!fin) {
    perror("fopen");
    exit(1);
    }
    size_t bytesread = fread(HR, 1, sizeof(HR), fin);
    if (bytesread != sizeof(HR)) {
    printf("Read %i bytes instead of %i.
    ", bytesread, sizeof(HR));
    exit(1);
    }
    fclose(fin);
    }

    void print_cards(int *cards) {
    int *pi = cards;
    for ( ; *pi ; pi++ )
    printf("%s ", card_names[*pi]);
    putchar(‘
    ‘);
    }

    int GetHandValue(int* pCards)
    {
    int p = HR[53 + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    return HR[p + *pCards];
    }

    int main(int argc, char *argv[]) {
    int h1[] =
    {4-1 + 13, 5-1, 14-1 + 26, 9-1, 11-1 + 39, 13-1 + 26, 13-1 + 39, 0};
    // 4d 5c Ah 9c Js Kh Ks

    int h2[] =
    {4-1 + 13, 5-1, 14-1 + 26, 9-1, 11-1 + 39, 12-1 + 26, 12-1 + 39, 0};
    // 4d 5c Ah 9c Js Qh Qs

    // Another example…
    // int h1[] = {2+39-1, 10+26-1, 9+13-1, 9-1, 5+39-1, 12+26-1, 12+13-1, 0};
    // 2s Ts 9d 9c 5s Kh Kd
    // int h2[] = {2+39-1, 10+26-1, 9+13-1, 9-1, 5+39-1, 13+26-1, 13+13-1, 0};
    // 2s Ts 9d 9c 5s Ah Ad

    int v1, v2;

    InitTheEvaluator();

    v1 = GetHandValue(h1);
    v2 = GetHandValue(h2);

    print_cards(h1); print_cards(h2);
    printf("v1: %x
    v2: %x
    ", v1, v2);
    }

  • Coding the Wheel says:

    [i]>I think there is a problem with the 2+2 LUT implementation. No matter how I use it, I am getting results that don’t match PokerStove.[/i]

    Tweak your card values as follows and everything should work — 2+2 expects per-rank ordering, not per-suit.

    2c = 1 2d = 2 2h = 3 2s = 4
    3c = 5 3d = 6 3h = 7 3s = 8
    4c = 9 4d = 10 4h = 11 4s = 12
    5c = 13 5d = 14 5h = 15 5s = 16
    6c = 17 6d = 18 6h = 19 6s = 20
    7c = 21 7d = 22 7h = 23 7s = 24
    8c = 25 8d = 26 8h = 27 8s = 28
    9c = 29 9d = 30 9h = 31 9s = 32
    Tc = 33 Td = 34 Th = 35 Ts = 36
    Jc = 37 Jd = 38 Jh = 39 Js = 40
    Qc = 41 Qd = 42 Qh = 43 Qs = 44
    Kc = 45 Kd = 46 Kh = 47 Ks = 48
    Ac = 49 Ad = 50 Ah = 51 As = 52

    Hope that helps.

  • Coding the Wheel says:

    Don’t forget to take the value returned and bitwise-AND it (val & 0x00000FFF) to get the actual equivalence value for the hand. Or right-shift (val >> 12) to extract the category…

  • Orwellophile says:

    Beautiful, and timely.

    16,777,217 evaluations in 8 seconds

    Random : 14.52
    Ad Ac : 84.94
    tie : 0.54

    PokerStove confirms:

    2,097,572,400 games 2.234 secs 938,931,244 games/sec

    Hand 0: 14.52% 00.27% { random }
    Hand 1: 84.93% 00.27% { AcAd }

    Must be the added glow of the ubiquitous Bob Jenkins within my code (ISAAC random number generator).

    Now if you’ll excuse me, I have to shower until I scrub the stench of stochastic sampling out of my skin.

  • Orwellophile says:

    // Written for GCC, download BJ’s ISAAC (see URL below) & compile all with 
    // c99-gcc -O2 -fUNROLL_LOOPS -o this this.c rand.c 
    //
    // "Just because it’s ugly, doesn’t mean it’s fast."
    //
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include "rand.h"    // YABJF: http://www.burtleburtle.net/bob/rand/

    enum TWO_PLUS_CARD {
          Wildcard,
          _2c, _2d, _2h, _2s, _3c, _3d, _3h, _3s,
          _4c, _4d, _4h, _4s, _5c, _5d, _5h, _5s,
          _6c, _6d, _6h, _6s, _7c, _7d, _7h, _7s,
          _8c, _8d, _8h, _8s, _9c, _9d, _9h, _9s,
          _Tc, _Td, _Th, _Ts, _Jc, _Jd, _Jh, _Js,
          _Qc, _Qd, _Qh, _Qs, _Kc, _Kd, _Kh, _Ks,
          _Ac, _Ad, _Ah, _As, Error1, Error2
    };

    char *card_names[] = { "??",
          "2c", "2d", "2h", "2s", "3c", "3d", "3h", "3s",
          "4c", "4d", "4h", "4s", "5c", "5d", "5h", "5s",
          "6c", "6d", "6h", "6s", "7c", "7d", "7h", "7s",
          "8c", "8d", "8h", "8s", "9c", "9d", "9h", "9s",
          "Tc", "Td", "Th", "Ts", "Jc", "Jd", "Jh", "Js",
          "Qc", "Qd", "Qh", "Qs", "Kc", "Kd", "Kh", "Ks",
          "Ac", "Ad", "Ah", "As", "Error1", "Error2" 
    };

    // The one and only lookup table.
    int HR[32487834];
    randctx rctx;

    void print_cards(int *cards) {
       int *pi = cards;
       for ( ; *pi ; pi++ ) 
          printf("%s ", card_names[*pi]);
       putchar(‘
    ‘);
    }

    inline
    __attribute__((always_inline))
    int GetHandValue(int* pCards)
    {
       int p = 53;
       for ( ; *pCards ; p = HR[p + *pCards++] ) ;
       return p;  
    }

    int InitTheEvaluator()
    {
       randinit(&rctx, TRUE);
       FILE *fin;
       return (fin = fopen("HandRanks.dat", "rb")) 
          && fread(HR, sizeof(HR), 1, fin) && !fclose(fin);
    }

    int MonteTest(enum TWO_PLUS_CARD firstHoleCard, enum TWO_PLUS_CARD secondHoleCard) {
       const int start_time = time(NULL);
       int a, b, hole[3] = {firstHoleCard, secondHoleCard, 0};  // Load hole with KK
       unsigned int card, cardnum, *pi, samples = 0, val[9];
       int randcard[8] = {0,0,0,0,0,0,0,0};
       int win[9]      = {0,0,0,0,0,0,0,0,0}; 
       int lose[9]     = {0,0,0,0,0,0,0,0,0};

       long long unsigned int mask, deadcards = (1LL << 53) - 1; 

       for (pi = hole; *pi; pi ++) 
          deadcards ^= 1LL << (*pi - 1);

       while (samples++ < 0×00800000) {
          mask = deadcards;

          for (cardnum = 0; cardnum < 7; ) {
             long long unsigned int maskmod;

             card = rand(&rctx) % 52;
             maskmod = 1LL << card;

             if (mask & maskmod) {
                mask ^= maskmod;
                randcard[cardnum ++] = ++card;
             }
          }

          a = GetHandValue(randcard);
          for (pi = hole, cardnum=7; *pi; randcard[--cardnum] = *pi++) ; 
          b = GetHandValue(randcard);

          if (a < b) win[1] ++;
          else if (a > b) win[0] ++;
       }

       printf("%i evaluations in %i seconds
    ", samples, time(NULL) - start_time);

       printf("Random : %0.2f
    ", 100.0 * win[0] / samples);
       printf("%s %s  : %0.2f
    ", 
             card_names[firstHoleCard], 
             card_names[secondHoleCard], 
             100.0 * win[1] / samples);
       printf("tie    : %0.2f
    ", 
             100.0 * (samples - win[0] - win[1]) / samples);
    }

    int main(int argc, char **argv) { 
       return InitTheEvaluator() && MonteTest(_Ad, _Ac); 
    }

    /* I hope it makes you feel as dirty reading it, as I did writing it */

  • Orwellophile says:

    Iterative version (5 card enumeration of board, plus 2 card enumeration of random hand).

    Sampled 2,097,572,400 boards in 28 seconds.

    Ad Ac wins: 1781508418 84.93
    Random wins: 304661670 14.52
    Ties : 11402312 0.54

    I’m passing the torch to someone who feels like optimizing it. I broke my head trying to do the Eternity II puzzle.

    It’s an adaptation of James’ Enumeration test for the 2+2 LUT - and it fits into the previously posted source (for requirement, functions, init)… or might be squeeze into James’ original 2+2 project - I haven’t tried.

    If you could just return it to me, when it’s about… 30 times faster than it is now? That would be swell.

    #define DECLARE_MASK(x)    long long unsigned int x = (1LL << 53) -1
    #define MASK_CLEAR(x)      x = 0
    #define MASK_FILL(x)       x = (1LL << 53) -1
    #define MASK_XOR_BIT(x,y)  x ^= 1LL << (y-1)
    #define MASK_GET_BIT(x,y)  (x & (1LL << (y-1)))
    #define MASK_AND(d,s1,s2)  d = s1 & s2
    #define MASK_COPY(d, s)    d = s
    #define MASK_PRINT(d, m)   printf("%s %llx
    ", d, m)

    int EnumerateAll7CardHands(enum TWO_PLUS_CARD holeCard1, enum TWO_PLUS_CARD holeCard2)
    {
       int index, time_start = time(NULL);
       int u0, u1, u2, u3, u4, u5, u5a;
       enum TWO_PLUS_CARD c0, c1, c2, c3, c4, c5, c6;
       int holeHand;
       long long int holeHand_wins=0, randHand_wins=0, samples=0;
       int handTypeSum[10];  // Frequency of hand category (flush, 2 pair, etc)

       DECLARE_MASK(holeMask);
       DECLARE_MASK(mask5);
       DECLARE_MASK(mask6);
       DECLARE_MASK(maskTmp);

       memset(handTypeSum, 0, sizeof(handTypeSum));  // do init..

       MASK_XOR_BIT(holeMask, holeCard1);
       MASK_XOR_BIT(holeMask, holeCard2);

       for (c0 = 1; c0 < End; c0++) {
          if (!MASK_GET_BIT(holeMask, c0)) continue;

          printf("%3.1f%% complete… %2.1f vs %2.1f
    ", 
                100.0 * c0 / End,
                100.0 * holeHand_wins / samples,
                100.0 * randHand_wins / samples
                );
          fflush(stdout);
          
          u0 = HR[53+c0];

          for (c1 = c0+1; c1 < End; c1++) {

             if (!MASK_GET_BIT(holeMask, c1)) continue;
             u1 = HR[u0+c1];

             for (c2 = c1+1; c2 < End; c2++) {

                if (!MASK_GET_BIT(holeMask, c2)) continue;
                u2 = HR[u1+c2];

                for (c3 = c2+1; c3 < End; c3++) {
                   if (!MASK_GET_BIT(holeMask, c3)) continue;
                   u3 = HR[u2+c3];

                   for (c4 = c3+1; c4 < End; c4++) {
                      if (!MASK_GET_BIT(holeMask, c4)) continue;
                      u4 = HR[u3+c4];

                      // *********************
                      // ** ORW: Hole Cards **
                      // *********************
                      
                      u5a = HR[u4+holeCard1];
                      holeHand = HR[u5a+holeCard2];
                      handTypeSum[holeHand >> 12]++;

                      MASK_COPY(mask5, holeMask);
                      
                      MASK_XOR_BIT(mask5, c0);
                      MASK_XOR_BIT(mask5, c1);
                      MASK_XOR_BIT(mask5, c2);
                      MASK_XOR_BIT(mask5, c3);
                      MASK_XOR_BIT(mask5, c4);
                      
                      for (c5 = 1; c5 < End-1; c5++) {
                         
                         // This loop doesn’t use "continue" in the hope that it can be UNROLLED by compiler.
                         if (MASK_GET_BIT(mask5, c5)) {
                            u5 = HR[u4+c5];

                            for (c6 = c5+1; c6 < End; c6++) {

                               if (MASK_GET_BIT(mask5, c6)) {

                                  int randHand;  // Declare in local scope to improve compiler optimization.
                                  randHand = HR[u5+c6];

                                  if (randHand > holeHand) {
                                     randHand_wins ++;
                                  } 
                                  else if (holeHand > randHand) {
                                     holeHand_wins ++;
                                  }
                                  samples++;
                               } // mask(c6
                            } // for(c6
                         } // mask(c5
                      } // for c5
                   }
                }
             }
          }
       }

       printf("Sampled %lld boards in %d seconds.
    ", samples, time(NULL) - time_start);
       printf("%s %s wins:       %16lld %0.2f 
    ", 
             card_names[holeCard1], card_names[holeCard2],
             holeHand_wins, (100.0 * holeHand_wins) / samples);
       printf("Random wins:      %16lld %0.2f 
    ", randHand_wins, (100.0 * randHand_wins) / samples);
       printf("Ties       :      %16lld %0.2f 
    ", (samples - holeHand_wins - randHand_wins), (100.0 * (samples - holeHand_wins - randHand_wins)) / samples);

       printf("The players hand won by:
    ");
       printf("BAD:              %d
    ", handTypeSum[0]);
       printf("High Card:        %d
    ", handTypeSum[1]);
       printf("One Pair:         %d
    ", handTypeSum[2]);
       printf("Two Pair:         %d
    ", handTypeSum[3]);
       printf("Trips:            %d
    ", handTypeSum[4]);
       printf("Straight:         %d
    ", handTypeSum[5]);
       printf("Flush:            %d
    ", handTypeSum[6]);
       printf("Full House:       %d
    ", handTypeSum[7]);  
       printf("Quads:            %d
    ", handTypeSum[8]);
       printf("Straight Flush:   %d
    ", handTypeSum[9]);

       return 1;
    }

  • Orwellophile says:

    Note to self, stop using for loops, or buy a faster computer and/or buy more Knuth books…

    #include <time.h>
    #include <stdio.h>

    main() {
    int time_start;
    int i,j,k,l,m,a,b;
    long long int count = 0;
    time_start = time(NULL);

    for (i=0; i<48; i++)
    for (j=i+1; j<49; j++)
    for (k=j+1; k<50; k++)
    for (l=k+1; l<51; l++)
    for (m=l+1; m<52; m++)
    for (a=0; a<51; a++)
    for (b=a+1; b<52; b++)
    count ++;

    printf("Count: %lli took %i seconds.
    ", count, time(NULL) - time_start);
    }

    Count: 3,446,220,960 took 6 seconds.

  • Frode Nesbakken says:

    I have a somewhat naive approach to my poker hand evaluator, but I think it should be relatively fast. It is by NO means as fast as using precomputed hashed evaluation results, but it ranks and evaluates 1 - 2.000.000+ hands per seconds (I cant remember exactly as it’s been a few years since I wrote it and admittedly, I kind of lost interest in it since).

    The cards are presented in an 8 bits bitmask, the first four bits make the rank (and also is the index in the sorted hand table) and the last 4 make up the brand.

    I want bore you with all the details, so I just might ship the code. There are probably quite a few optimization possibilities here…If you find it interesting, I will mail you the complete source code.

    A cursory glance at the code and I think there might be a bug in it involving evaluation of full house where you have two three of a kinds (in 7 card games), but I am not sure….

    The code is in C#

    using System;

    namespace TexasClient
    {
    /// <summary>
    /// Summary description for Player.
    /// </summary>
    public class Hand
    {
    #region Static Members
    static byte [] cardCounter;

    static int [] brandCounter;
    static int [] brandAndOperator;
    #endregion

    #region Private Members

    int [] hand = new int[14];
    int pocket;
    int eval;
    byte flushBrand;

    #endregion

    #region Public Properties
    public int Pocket
    {
    get { return pocket; }
    set { pocket = value; }
    }

    public int EvaluationValue
    {
    get { return eval; }
    }
    #endregion

    #region Constructor(s)
    public Hand( string pocketCards )
    {
    pocket = (int) CardConverter.GetHandBits( pocketCards );
    sortHand( pocket );
    }

    public Hand ( int pocketCards )
    {
    pocket = pocketCards;
    sortHand( pocket );
    }

    public Hand ()
    {}
    #endregion

    #region Public Methods
    public void AddCards( long deckHand )
    {
    sortHand( deckHand );
    }

    public void AddCards( string deckHand )
    {
    long cards = CardConverter.GetHandBits( deckHand );
    sortHand( cards );
    }

    public int EvaluateHand()
    {
    // — Counters and indexes
    int curHand=0,
    cardCnt=0,
    brandCnt=0,
    kickerCnt=0,
    straightCnt=0,
    strflushCnt=0;

    // — Hand Identifiers
    int fkind = 0,
    tkind = 0,
    pair = 0,
    straight=0;

    int [] kickers = new int[7];
    hand[0] = hand[13];

    // — Go through all our cards
    for ( byte i=13; i>0; i- )
    {
    // — Any cards of this kind?
    curHand = hand[i];
    if ( curHand != 0 )
    {
    // — Yup, we got something here…
    byte idx = (byte) (curHand & 0xFF);

    cardCnt += cardCounter[ idx ];
    brandCnt += brandCounter[ idx ];

    strflushCnt += brandCounter[ idx ];
    strflushCnt &= brandAndOperator[ idx ];
    straightCnt++;

    if ( straightCnt == 5 )
    straight = i;

    if (( strflushCnt & 0×4444 ) != 0 ) {
    strflushCnt += brandCounter[ (hand[i-1] & 0xFF )];
    flushBrand = (byte) ((strflushCnt & 0xF000) >= 0×5000 ? 0×08 :
    (strflushCnt & 0x0F00) >= 0×0500 ? 0×04 :
    (strflushCnt & 0x00F0) >= 0×0050 ? 0×02 :
    (strflushCnt & 0x000F) >= 0×0005 ? 0×01 : 0);

    if ( flushBrand != 0 ) {
    eval = (int) EvalBonus.StraightFlush + (i+4);
    return eval;
    }
    }

    if ( cardCounter[ idx ] == 4 )
    // — we have four of a kind
    fkind = i;
    else if ( cardCounter[ idx ] == 3 ) {
    // — we have three of a kind
    if ( tkind > 0 )
    tkind += (byte) i<<4;
    else
    tkind = i;
    }
    else if ( cardCounter[idx ] == 2 ) {
    // — we have a pair
    if ( pair > 0x0F ) { // < — already a higher pair
    kickers[kickerCnt++] = i;
    kickers[kickerCnt++] = i;
    }
    else if ( pair > 0×00 )
    pair += (i<<8);
    else
    pair += i;
    }
    else if ( cardCounter[ idx ] == 1 )
    kickers[kickerCnt++] = i;
    }
    else {
    straightCnt = 0;
    strflushCnt = 0;
    }
    }

    // — Check if the straight wraps…
    if ( straightCnt >= 5 ) {
    eval = (int) EvalBonus.Straight + straight;
    return eval;
    }
    else if ( straightCnt == 4 ) {
    if ( hand[0] != 0 ) {
    eval = (int) EvalBonus.Straight + 4;
    return eval;
    }
    }

    // — Check four of a kind
    if ( fkind > 0 )
    {
    if ( tkind > 0 )
    kickers[0] = tkind;
    else if ( pair > 0 )
    if ( kickers[0] < pair )
    kickers[0] = pair;

    eval = (int) EvalBonus.FourOfAKind + 16 * fkind + kickers[0];
    return eval;
    }
    // — Check full house
    else if ( tkind > 0 ) {
    // — We have three of a kind
    // — Do we have another three of a kind? (i.e. full house)
    if ((tkind & 0x00F0) > 0 ) {
    eval = (int) EvalBonus.FullHouse + 16 * (tkind & 0x000F) + ((tkind & 0x00F0)>>4);
    return eval;
    }
    // — Do we have a pair??? (i.e. full house?)
    else if ( pair > 0x0F ) {
    eval = (int) EvalBonus.FullHouse + 16 * (tkind) + (pair & 0x00FF);
    return eval;
    }
    else if ( pair > 0 ) {
    eval = (int) EvalBonus.FullHouse + 16 * (tkind) + pair;
    return eval;
    }
    }

    // — Check flush / straight
    flushBrand = (byte) ((brandCnt & 0xF000) >= 0×5000 ? 0×08 :
    (brandCnt & 0x0F00) >= 0×0500 ? 0×04 :
    (brandCnt & 0x00F0) >= 0×0050 ? 0×02 :
    (brandCnt & 0x000F) >= 0×0005 ? 0×01 : 0);

    eval = EvalFlush( flushBrand );
    if ( eval > 0 )
    return eval;

    // — Check for three of a kind
    if ( straight > 0 ) {
    eval = (int) EvalBonus.Straight + straight;
    return eval;
    }
    else if ( tkind > 0 ) {
    eval = (int) EvalBonus.ThreeOfAKind + 256 * tkind + 16 * kickers[0] + kickers[1];
    return eval;
    }
    // — Check for two pairs
    else if ( pair > 0 ) {
    if ( pair > 0x0F ) {
    eval = (int) EvalBonus.TwoPairs + 256 * ( pair & 0x000F ) + (16 * (pair>>8)) + kickers[0];
    return eval;
    }
    else {
    eval = (int) EvalBonus.OnePair + 4096 * pair + 256 * kickers[0] + 16 * kickers[1] + kickers[2];
    return eval;
    }
    }

    // — We have nothing, calculate kickers
    eval = (int) kickers[0] * 65536 +
    kickers[1] * 4096 +
    kickers[2] * 256 +
    kickers[3] * 16 +
    kickers[4];

    return eval;
    }

    public string GetPocketHand()
    {
    string tmp = CardConverter.GetCardString( ( pocket & 0xFF00 ) >> 8 ) + " ";
    return tmp + CardConverter.GetCardString( ( pocket & 0x00FF ));
    }

    public string GetValueLiteral()
    {
    if ( eval == 0 )
    EvaluateHand();

    if ( eval > (int) EvalBonus.StraightFlush )
    return "Straight Flush";
    else if ( eval > (int) EvalBonus.FourOfAKind )
    return "Four of a Kind";
    else if ( eval > (int) EvalBonus.FullHouse )
    return "Full House";
    else if ( eval > (int) EvalBonus.Flush )
    return "Flush";
    else if ( eval > (int) EvalBonus.Straight )
    return "Straight";
    else if ( eval > (int) EvalBonus.ThreeOfAKind )
    return "Three of a kind";
    else if ( eval > (int) EvalBonus.TwoPairs )
    return "Two Pairs";
    else if ( eval > (int) EvalBonus.OnePair )
    return "One pair";
    else if ( eval > (int) EvalBonus.HighCard )
    return "High card";
    else
    return "";
    }

    public string GetPokerHand()
    {
    string pokerHand = "";
    string tmp;
    int val = EvaluateHand();
    byte idx = 0;

    if ( val > (int) EvalBonus.StraightFlush )
    {
    val -= (int) EvalBonus.StraightFlush;
    // — Now we know where the straight flush started…
    byte brand = (byte) (flushBrand<<4);

    if ( val == 4 )
    pokerHand += "A" + CardConverter.GetBrand( brand ) + " ";
    else
    pokerHand += CardConverter.GetCardString( brand + val - 4) + " ";

    pokerHand += CardConverter.GetCardString( brand + val - 3 ) + " ";
    pokerHand += CardConverter.GetCardString( brand + val - 2 ) + " ";
    pokerHand += CardConverter.GetCardString( brand + val - 1 ) + " ";
    pokerHand += CardConverter.GetCardString( brand + val ) + " ";
    }
    else if ( val > (int) EvalBonus.FourOfAKind )
    {
    val -= (int) EvalBonus.FourOfAKind;
    idx = (byte) (val/16);
    tmp = CardConverter.GetValue( idx );
    pokerHand += tmp + "s ";
    pokerHand += tmp + "h ";
    pokerHand += tmp + "c ";
    pokerHand += tmp + "d ";

    idx = (byte) (val - idx*16);
    tmp = CardConverter.GetValue( idx );
    if ( (hand[ idx ] & 0×08) != 0 ) pokerHand += tmp + "s ";
    else if ( (hand[ idx ] & 0×04) != 0 ) pokerHand += tmp + "h ";
    else if ( (hand[ idx ] & 0×02) != 0 ) pokerHand += tmp + "c ";
    else if ( (hand[ idx ] & 0×01) != 0 ) pokerHand += tmp + "d ";
    }
    else if ( val > (int) EvalBonus.FullHouse )
    {
    val -= (int) EvalBonus.FullHouse;

    idx = (byte) (val / 16);
    tmp = CardConverter.GetValue( idx );
    if ( (hand[ idx ] & 0×08) != 0 ) pokerHand += tmp + "s ";
    if ( (hand[ idx ] & 0×04) != 0 ) pokerHand += tmp + "h ";
    if ( (hand[ idx ] & 0×02) != 0 ) pokerHand += tmp + "c ";
    if ( (hand[ idx ] & 0×01) != 0 ) pokerHand += tmp + "d ";

    val -= idx*16;
    idx = (byte) (val);
    tmp = CardConverter.GetValue( idx );
    if ( (hand[ idx ] & 0×08) != 0 ) pokerHand += tmp + "s ";
    if ( (hand[ idx ] & 0×04) != 0 ) pokerHand += tmp + "h ";
    if ( (hand[ idx ] & 0×02) != 0 ) pokerHand += tmp + "c ";
    if ( (hand[ idx ] & 0×01) != 0 ) pokerHand += tmp + "d ";
    if ( pokerHand.Length > 15 )
    pokerHand = pokerHand.Substring(0, 14);
    }
    else if ( val > (int) EvalBonus.Flush )
    {
    val -= (int) EvalBonus.Flush;

    byte brand = (byte) (flushBrand << 4);
    idx = (byte) (val / 65536); pokerHand += CardConverter.GetCardString( brand + idx ) + " "; val-=idx*65536;
    idx = (byte) (val / 4096); pokerHand += CardConverter.GetCardString( brand + idx ) + " "; val-=idx*4096;
    idx = (byte) (val / 256); pokerHand += CardConverter.GetCardString( brand + idx ) + " "; val-=idx*256;
    idx = (byte) (val / 16); pokerHand += CardConverter.GetCardString( brand + idx ) + " "; val-=idx*16;
    idx = (byte) val; pokerHand += CardConverter.GetCardString( brand + idx ) + " ";
    }
    else if ( val > (int) EvalBonus.Straight )
    {
    val -= (int) EvalBonus.Straight;
    if ( val == 4 )
    pokerHand += "A" + GetAnyBrand( hand[13] ) + " ";
    else
    pokerHand += CardConverter.GetValue( val-4 ) + GetAnyBrand( hand[val-4]) + " ";

    pokerHand += CardConverter.GetCardString( val-3 ) + GetAnyBrand( hand[val-3] ) + " ";
    pokerHand += CardConverter.GetCardString( val-2 ) + GetAnyBrand( hand[val-2] ) + " ";
    pokerHand += CardConverter.GetCardString( val-1 ) + GetAnyBrand( hand[val-1] ) + " ";
    pokerHand += CardConverter.GetCardString( val ) + GetAnyBrand( hand[val] ) + " ";
    }
    else if ( val > (int) EvalBonus.ThreeOfAKind )
    {
    val -= (int) EvalBonus.ThreeOfAKind;
    idx = (byte) (val/256);
    tmp = CardConverter.GetValue( idx );
    if ( (hand[ idx ] & 0×08) != 0 ) pokerHand += tmp + "s ";
    if ( (hand[ idx ] & 0×04) != 0 ) pokerHand += tmp + "h ";
    if ( (hand[ idx ] & 0×02) != 0 ) pokerHand += tmp + "c ";
    if ( (hand[ idx ] & 0×01) != 0 ) pokerHand += tmp + "d ";

    val -= idx*256; idx = (byte) (val/16);
    pokerHand += CardConverter.GetCardString( idx ) + GetAnyBrand( hand[ idx ] ) + " ";

    val -= idx*16; idx = (byte) val;
    pokerHand += CardConverter.GetCardString( idx ) + GetAnyBrand( hand[ idx ] );
    }
    else if ( val > (int) EvalBonus.TwoPairs )
    {
    val -= (int) EvalBonus.TwoPairs;
    idx = (byte) (val/256);
    tmp = CardConverter.GetValue( idx );
    if ( (hand[ idx ] & 0×08) != 0 ) pokerHand += tmp + "s ";
    if ( (hand[ idx ] & 0×04) != 0 ) pokerHand += tmp + "h ";
    if ( (hand[ idx ] & 0×02) != 0 ) pokerHand += tmp + "c ";
    if ( (hand[ idx ] & 0×01) != 0 ) pokerHand += tmp + "d ";

    val -= idx*256;
    idx = (byte) (val/16);
    tmp = CardConverter.GetValue( idx );
    if ( (hand[ idx ] & 0×08) != 0 ) pokerHand += tmp + "s ";
    if ( (hand[ idx ] & 0×04) != 0 ) pokerHand += tmp + "h ";
    if ( (hand[ idx ] & 0×02) != 0 ) pokerHand += tmp + "c ";
    if ( (hand[ idx ] & 0×01) != 0 ) pokerHand += tmp + "d ";

    val -= idx*16;
    pokerHand += CardConverter.GetValue( val ) + GetAnyBrand( hand[val] );
    }
    else if ( val > (int) EvalBonus.OnePair )
    {
    val -= (int) EvalBonus.OnePair;

    idx = (byte) (val/4096);
    val -= idx * 4096;
    tmp = CardConverter.GetValue( idx );
    if ( (hand[ idx ] & 0×08) != 0 ) pokerHand += tmp + "s ";
    if ( (hand[ idx ] & 0×04) != 0 ) pokerHand += tmp + "h ";
    if ( (hand[ idx ] & 0×02) != 0 ) pokerHand += tmp + "c ";
    if ( (hand[ idx ] & 0×01) != 0 ) pokerHand += tmp + "d ";

    idx = (byte) (val/256); val -= idx*256;
    pokerHand += CardConverter.GetValue( idx ) + GetAnyBrand( hand[idx] ) + " ";

    idx = (byte) (val/16); val -= idx*16;
    pokerHand += CardConverter.GetValue( idx ) + GetAnyBrand( hand[idx] ) + " ";
    pokerHand += CardConverter.GetValue( val ) + GetAnyBrand( hand[val] );
    }
    else
    {
    idx = (byte) (val/65536); val -= idx*65536;
    pokerHand += CardConverter.GetValue( idx ) + GetAnyBrand( hand[idx] ) + " ";
    idx = (byte) (val/4096); val -= idx*4096;
    pokerHand += CardConverter.GetValue( idx ) + GetAnyBrand( hand[idx] ) + " ";
    idx = (byte) (val/256); val -= idx*256;
    pokerHand += CardConverter.GetValue( idx ) + GetAnyBrand( hand[idx] ) + " ";
    idx = (byte) (val/16); val -= idx*16;
    pokerHand += CardConverter.GetValue( idx ) + GetAnyBrand( hand[idx] ) + " ";
    pokerHand += CardConverter.GetValue( val ) + GetAnyBrand( hand[val] );
    }

    return pokerHand.Trim();
    }

    public void ClearHand( bool keepPocket )
    {
    for (int i=0; i<14; i++ )
    hand[i] = 0;

    if ( keepPocket )
    sortHand( pocket );
    }

    #endregion

    #region Public Static Methods
    public static void InitCounters()
    {
    cardCounter = new byte [16];
    brandCounter = new int[16];
    brandAndOperator = new int[16];

    for ( int i=0; i<16; i++ ) {
    cardCounter[i] = countBits( (byte) i);

    if (( i & (byte) Brand.Spades ) != 0 ) {
    brandCounter[i] += 0×1000;
    brandAndOperator[i] += 0xF000;
    }

    if (( i & (byte) Brand.Hearts ) != 0 ) {
    brandCounter[i] += 0×0100;
    brandAndOperator[i] += 0x0F00;
    }

    if (( i & (byte) Brand.Clubs ) != 0 ) {
    brandCounter[i] += 0×0010;
    brandAndOperator[i] += 0x00F0;
    }

    if ( ( i & (byte) Brand.Diamonds ) != 0 ) {
    brandCounter[i] += 0×0001;
    brandAndOperator[i] += 0x000F;
    }
    }
    }

    #endregion

    #region Private Methods
    private string GetAnyBrand( int val )
    {
    val = val & 0x000F;
    if ( (val & 0×08) != 0 ) return "s";
    if ( (val & 0×04) != 0 ) return "h";
    if ( (val & 0×02) != 0 ) return "c";
    if ( (val & 0×01) != 0 ) return "d";
    return "";
    }
    private int EvalFlushStraight( int brandCount, int straight )
    {
    // — Get flush brand (0 if no flush)
    int brand = (brandCount & 0xF000) >= 0×5000 ? 0×08 :
    (brandCount & 0x0F00) >= 0×0500 ? 0×04 :
    (brandCount & 0x00F0) >= 0×0050 ? 0×02 :
    (brandCount & 0x000F) >= 0×0005 ? 0×01 : 0;

    if ( brand == 0 && straight == 0 )
    return 0;

    if ( straight != 0 && brand == 0 )
    return (int) EvalBonus.Straight + straight;
    else if ( straight == 0 && brand != 0)
    return EvalFlush( brand );
    else {
    // — Do we have a straight flush
    int cnt = 0;
    for ( int i=straight; i >= 0; i- ) {
    if (( hand[i] & brand ) != 0 )
    cnt++;
    else
    cnt=0;

    if ( cnt == 5 )
    return (int) EvalBonus.StraightFlush + (i+4);
    }

    return EvalFlush( brand );
    }
    }

    private int EvalFlush( int brand )
    {
    if ( brand == 0 )
    return 0;

    int eval = (int) EvalBonus.Flush;
    int fac = 65536;
    for (int i=13; i>0 && (fac > 0); i-)
    {
    if (( hand[i] & brand ) > 0 )
    {
    eval += i*fac;
    fac >>= 4;
    }
    }
    return eval;
    }

    private void sortHand( long inHand )
    {
    byte bits = 8;
    byte card = (byte) inHand;

    while ( card != 0 )
    {
    int idx = card & 0x0F;

    byte brand = (byte) ((card & 0xF0) >> 4);
    hand[ idx ] += brand;

    /*if ( hand[idx] == 16 )
    return;*/

    card = (byte) (inHand >> bits);
    bits += 8;
    }
    }

    private static byte countBits( byte b )
    {
    byte cnt=0;
    int flag = 1;
    for ( int i=0; i<8; i++ )
    {
    if ( ( b & flag ) != 0 )
    cnt++;

    flag <<= 1;
    }
    return cnt;
    }

    #endregion
    }

    public enum EvalBonus
    {
    StraightFlush = 8000000,
    FourOfAKind = 7000000,
    FullHouse = 6000000,
    Flush = 5000000,
    Straight = 4000000,
    ThreeOfAKind = 3000000,
    TwoPairs = 2000000,
    OnePair = 1000000,
    HighCard = 0
    }
    }

  • Beverly Hills says:

    i like to use and evaluator when i play poker. I will definitely help you game in the future. I think this is a must to use. I use them all the time.

  • Houen says:

    Hi! Great list. This saved me DAYS making my own hand evaluator.
    There is an error in the shuffle_deck method of the cactuskev C# version, however, on line 118:

    n = (int)r.Next(0, 52);

    causes infinite loop (and forcing exit of loop causes the hand freq’s to go off)
    the line should be;

    n = (int)r.Next(1, 52);

    which works perfectly

  • Houen says:

    Whoops, reverse the above. Change the 1,52 to 0,52 and that is the fix

  • Houen says:

    Found another typo in the C# version of cactuskev’s algorithm:

    Line 140: else if (( hand[i] & 0×2000 ) == 0×200)

    should be

    else if (( hand[i] & 0×2000 ) == 0×2000)

    Otherwise you’ll get no hearts in your deck

  • makattan kan gelmesi says:

    I really like this post. Thanks for this article, Anyone got any more info about it? I am now your blog’ s rss follower. you are now in my bookmarks.

  • ask bir hayal izle says:

    ask bir hayal izle

  • Anonymous says:

    Anyone know if it is possible to pull in the TwoPlusTwo HandRanks.dat file using C#? I was thinking it could be done with a FileStream and then BinaryReader. Is this possible? I thought I read that if the created the binary file using C++ then you need to use the same to read it.

    Thanks.

  • Greg Bray says:

    Just wanted to let you know that the card format for Keith Rule’s C# Port is not exactly the same as in the original Pokersource evaluator. It still uses an unsigned long with one bit to represent each card, but it does not use buffer bits to word-align each suit. The order of card values is the same (ie "0bAKQJT98765432") but the order of suits is Spades Hearts Diamonds Clubs. Each suit is offset by 13 bits (instead of 16 in the original), but you can still use bit shifting to create a hand. Here is an example using the HandEvaluator.dll assembly from IronPython:

    HoldemHand.Hand.MaskToString(int(’1111111111111′,2))
    ‘Ac Kc Qc Jc Tc 9c 8c 7c 6c 5c 4c 3c 2c’

    HoldemHand.Hand.MaskToString(int(’1111111111111′,2) <<13)
    ‘Ad Kd Qd Jd Td 9d 8d 7d 6d 5d 4d 3d 2d’

    HoldemHand.Hand.MaskToString(int(’1111111111111′,2) << (13*2))
    ‘Ah Kh Qh Jh Th 9h 8h 7h 6h 5h 4h 3h 2h’

    HoldemHand.Hand.MaskToString(int(’1111111111111′,2) << (13*3))
    ‘As Ks Qs Js Ts 9s 8s 7s 6s 5s 4s 3s 2s’

  • Lukas says:

    Hi !

    First of all, great post!!

    I just finished implementing a showdown calculator, during my effort to write a poker-AI (which was a more difficult task than i thought).

    It supports Cactus Kev’s algorithm, which i adopted for 7 hand evaluation and the 2+2 method. Check it out at [url]http://www.potbot.net[/url]

    Of course, you are thanked on my site ;)

    Cheers,
    Lukas

  • Alix Martin says:

    Nice post. While I’m at it, I’ll ask for advice : how do I get pokersource to work with my javacentric stuff ? I got the org.pokersource neatly linked, but it looks like I actually have to compile some C code to make it work.

    My options are :
    - an antique VC++ studio (5.0), which I didn’t get to eat the makefile
    - trying to get CDT integrated into Eclipse/Ganymede (tried that in the past and failed)
    - Cygwin
    - MINGW

    I’m pretty clueless with C++ IDEs…

  • Søren Houen says:

    I’ve created a version of the CactusKev evaluator in Actionscript 3 for use in Flash projects. It can be found here:

    http://blog.houen.net/coding/actionscript-3-poker-hand-evaluator/

    I’ve tested it to calculate all 2.5 mio 5-card hands in 6.3 seconds, and 14.000 7-card texas hold’em poker hands per second.

    Also: If you’re bored check out my gamesite: http://www.thesegamesrock.com ;)

  • club penguin cheats says:

    This time, I was pleasantly surprized to find your penultimate poker evaluator article. Well done indeed! Clever writing, love the ninja hotties, and quite an enjoyable read. As a former Math/CompSci major, I wish they would have taught this sort of stuff back when I was a n00b programmer. Of course, back then, it was punched cards and FORTRAN. Old school, baby! Anyways, kudos on your exemplary article.

  • Ollison says:

    The order of card values is the same (ie "0bAKQJT98765432") but the order of suits is Spades Hearts Diamonds Clubs. [url=http://www.universaldegrees.com/degree/online-phd.asp]phd online[/url] | [url=http://www.universaldegrees.com/universaldegrees/programs/bachelors-degree-program.asp]online bachelors degree[/url] | [url=http://www.universaldegrees.com/universaldegrees/programs/masters-degree-program.asp]masters degree program[/url]

  • Ollison says:

    i like to use and evaluator when i play poker. I will definitely help you game in the future. I think this is a must to use. I use them all the time. [url=http://affiliates.universaldegrees.com]Corllins University[/url] | [url=http://www.universaldegrees.com/]degrees[/url]

  • A.K. says:

    Hi !

    I’m confused!

    When i run the code below i get the results below.
    If I don’t have missunderstood something totally
    { 2, 6, 12, 14, 23, 26, 29 } == 2d 3d 4s 5d 7h 8d 9c
    which in turn is a total crap hand.
    Why do I get Category 1 as result (STRAIGHT_FLUSH)??

    Could anyone help me out ??

    CODE:

    #include <windows.h>
    #include <tchar.h>
    #include <iostream>
    #include <fstream>
    using namespace std;

    // The handranks lookup table- loaded from HANDRANKS.DAT.
    int HR[32487834];

    // This function isn’t currently used, but shows how you lookup
    // a 7-card poker hand. pCards should be a pointer to an array
    // of 7 integers each with value between 1 and 52 inclusive.
    int LookupHand(int* pCards)
    {
    int p = HR[53 + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    p = HR[p + *pCards++];
    return HR[p + *pCards++];
    }

    void LookupSingleHands()
    {
    printf("Looking up individual hands…

    ");

    // Create a 7-card poker hand (each card gets a value between 1 and 52)
    int cards[] = { 2, 6, 12, 14, 23, 26, 29 };
    int retVal = LookupHand(cards);
    cout<< "FuckingValue"<<retVal<<’
    ‘;
    printf("Category: %d
    ", retVal >> 12);
    printf("Salt: %d
    ", retVal & 0x00000FFF);
    ofstream File;
    File.open("TEST.txt",ios::app);
    File <<"int cards[] = { 2, 6, 12, 14, 23, 26, 29 };"<<"retVal"<<retVal<<"Caregory"<<(retVal >> 12)<<"Salt "<<(retVal & 0x00000FFF);
    File.close();
    }

    int _tmain(int argc, _TCHAR* argv[])
    {
    printf("Testing the Two Plus Two 7-Card Evaluator
    ");
    printf("—————————————-

    ");

    // Load the HandRanks.DAT file and map it into the HR array
    printf("Loading HandRanks.DAT file…");
    memset(HR, 0, sizeof(HR));
    FILE * fin = fopen("HandRanks.dat", "rb");
    if (!fin)
    return false;
    size_t bytesread = fread(HR, sizeof(HR), 1, fin); // get the HandRank Array
    fclose(fin);
    printf("complete.

    ");

    // Enumerate all 133,784,560 possible 7-card poker hands…
    // EnumerateAll7CardHands();
    LookupSingleHands();

    std::cin.get();

    return 0;
    }

    RESULT:

    int cards[] = { 2, 6, 12, 14, 23, 26, 29 }; retVal 4145 Category 1 Salt 49

  • Still on vb6 says:

    I just started writing a bot and saw that someone (a year ago) wanted help in using HandsRank.dat in VB.

    Dim longbuffer(32487834) As Long ‘ receives long read from file
    Dim stringbuffer As String ‘ receives string read from file
    Dim hFile As Long ‘ handle of the open file
    Dim bytes() As Byte
    Dim hand(7) As Long

    Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" _
    (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)

    Private Sub Command1_Click()

    Get hFile, 1, stringbuffer
    bytes = StrConv(stringbuffer, vbFromUnicode)
    stringbuffer = ""
    ‘Debug.Print bytes
    CopyMemory longbuffer(LBound(longbuffer)), bytes(LBound(bytes)), _ (UBound(bytes) - LBound(bytes) + 1)

    hand(1) = 14
    hand(2) = 22
    hand(3) = 23
    hand(4) = 30
    hand(5) = 33
    hand(6) = 36
    hand(7) = 50
    p = longbuffer(53 + hand(1))
    p = longbuffer(p + hand(2))
    p = longbuffer(p + hand(3))
    p = longbuffer(p + hand(4))
    p = longbuffer(p + hand(5))
    p = longbuffer(p + hand(6))
    p = longbuffer(p + hand(7))
    Debug.Print p
    End Sub

    Private Sub Form_Load()
    hFile = FreeFile
    Open "……..HandRanks.dat" For Binary As hFile
    If hFile = -1 Then ‘ the file could not be opened
    Debug.Print "Unable to open the file — probably does not exist."
    End ‘ abort the program
    End If

    End Sub

  • Jokipii says:

    I managed to compress Two plus Two Evaluator look up table from original 32487834
    to 21242728 entries. Working Java version and explanation how compression works can be found at http://pokerai.org/pf3/viewtopic.php?p=28409#p28409 If wanted, it can also split table and further compress last level by saving it as 16 bit chars. Result is LUT that takes little less than 65MB.

  • Club Penguin Cheats says:

    Ya gotta love Mr. T.

  • Virtual Worlds For Kids says:

    Is he really a spokesman for Vox? That’s funny.

  • Anonymous says:

    Nothing for Python users.Waste of time 4 me :(

  • Coding the Wheel says:

    > Nothing for Python users.Waste of time 4 me :(

    The pokersource library has Python bindings, and you can hit the .NET libraries through IronPython. I believe someone has also done a Python version of the 2+2 evaluator…. those are just the options mentioned in this post. There are definitely others.

  • joker2d says:

    How to get all cards value using in combination?
    I use The Two Plus Two Evaluator
    I need all cards sort by his rang in combination
    If you know how I can get this please give me code example!

  • tanning beds says:

    Your article is very appealing to me.I like this article, this article that i learned a lot of knowledge.

  • tea says:

    Thank you for sharing.I like [url=http://www.ygshtea.com/] 铁观音 [/url]

  • peterson says:

    nice post thanks for sharing it..
    assignment writing assignment help

  • Personal Blog says:

    I was very encouraged to find this site. I wanted to thank you for this special read. I definitely savored every little bit of it and I have you bookmarked to check out new stuff you post.

  • Richard Toner says:

    What the fuck?
    So long # of outs X 4 pre turn and # of outs X 2 pre river.
    Seems I need to read 72 hours of blog posts to know that I can’t beat the fucking computer.

  • PetGrowerNewBee says:

    Hi James,

    somewhere in the comments of your outstanding posts somebody called you a dork and you confimed clearly. I am not able to evaluate this because I don’t know you personally. I just see this marvelous pages of lines of beautifull deep thinking spirit.

    But something has obviousely to be a little bit screwy in your head:

    I refer to this sequence in your post above:

    **”See, I promised you scantily-clad ninjas and I delivered.**

    **Usage:”**

    Can you imagine what a normal staight forward thinking man is waiting for, after this???

    And you contiue:

    **”Before you can use the Two Plus Two evaluator……”**

    **;-)**

    Hm, it was not easy for me to express my deep respect and highes esteem for you and your work without just duplicating what was already expressed before by others.

    Thank you very much for sharing all of this with us!

  • Guy Qube says:

    **’Xcuse me for being a dope, how do you get the 2+2 evaluator to do its magic with 5 or 6 cards?**

    I’ve tried various things (zeros for the unkowns, duplicating known cards, only looking up 5 or 6 times) but none of them appear to give a meaningful result. What (most likely obvious thing) am I missing?

    Cracking site btw I gives me all kind of naughty ideas ;)

  • Susancai says:

    Thank you for sharing listing of publically available poker hand evaluators. Awesome! latex mattress

  • Anonymous says:

    A few comments up someone pointed out what he thought was an error in the C# version of cactus Kev.

    He Wrote:

    Found another typo in the C# version of cactuskev’s algorithm:
    Line 140: else if (( hand[i] & 0×2000 ) == 0×200)
    should be
    else if (( hand[i] & 0×2000 ) == 0×2000)
    Otherwise you’ll get no hearts in your deck

    Is he correct?

    Oh and great post!!

  • ralf madson says:

    I just stumbled upon your blog and wanted 4 Seasons Wine Club Review to say that I have really enjoyed reading your blog posts. Thanks for the post.

  • Chris Oei says:

    I’ve posted a Java implementation of the TwoPlusTwo evaluator at GitHub:

    https://gist.github.com/832122

    It uses the same HandRanks.dat file that the C implementation uses (it swaps endian-ness
    upon reading the file).

    - http://static.eluctari.com/download/game/poker/HandRanks.dat
    - Size: 129951336
    - CRC32: 7808da57
    - MD5: 5de2fa6f53f4340d7d91ad605a6400fb
    - SHA1: f8467e36f470c9beea98c47d661c9b2c4a13e577
    - SHA256: ad00f3976ad278f2cfd8c47b008cf4dbdefac642d70755a9f20707f8bbeb3c7e

  • J says:

    Hey all,

    I wonder if anyone has tried to build this on Visual C++ Express 2008? I’m keen to try out the 2+2 code. I’ve build the project (no errors) and I’ve created the HANDRANKS.DAT file by double clicking on XPokerEval.TwoPlusTwo.exe in the bin folder of the project. Then I’ve double clicked the XPokerEval.TwoPlusTwo.Test.exe to see if the tests run correctly, but the program hangs. The last lines of text to be printed are these:

    Category: 1
    Salt: 49

    Anyone got any tips on what’s gone wrong? Also, when I try degubbing within the IDE I am failing to get it going to date. It asks me for the exe and when I give it XPokerEval.TwoPlusTwo.Test it tells me:

    “Unable to start program XPokerEval.TwoPlusTwo.Test. The system cannot find the file specified.”

    Any tips?

  • Anonymous says:

    Hello!
    Im very interesting TwoPlusTwo evaluator. But I dont have Visual Studio. I need HANDRANKS.DAT for working on Delphi. Please somebody comlile .exe file from XPokerEval.TwoPlusTwo folder and post somewhere this .exe file for downloading.

  • Anonymous says:

    Any sample evaluator that takes advantage of the parallelism offered by GPU programming?

  • Anonymous says:

    my vote of 5

  • ghd outlet australia says:

    I love my ghd outlet Glattetang! It heats up very quickly and works well. My glamour hair is annoyingly thick and poofy, but ghdstraighteneroutletaustralia works wonders! I have never been so fully satisfied with just ghd straightener outlet supplier! What a pleasure shopping at this ghd outlet australia! Thank you very much for this wonderful shopping experience. I will be shopping ghd outlet very very often.

  • ghd outlet australia says:

    I love my ghd outlet Glattetang! It heats up very quickly and works well. My glamour hair is annoyingly thick and poofy, but ghdstraighteneroutletaustralia works wonders! I have never been so fully satisfied with just ghd straightener outlet supplier! What a pleasure shopping at this ghd outlet australia! Thank you very much for this wonderful shopping experience. I will be shopping ghd outlet very very often.

  • totolemuto says:

    I am writing mine. Sure to beat everyone !

  • Denis says:

    I’ve ported 2+2 evaluator to OpenCL, It evaluates 700 000 000 **random** hands per second using Ati Radeon HD5970

  • hayvlad says:

    Very good reference for poker evaluators! I have a [question][1] on 2+2. I’ve posted it on stackexchange. Please help!

    [1]: http://stackoverflow.com/questions/9935212/on-two-plus-two-poker-hand-evaluator-how-do-you-get-the-best-5-cards-combinatio

  • ラルフローレン says:

    クリントン米国務長官は4日、ミャンマーの国会補選実施などの民主化努力を評価し、同国に対する金融サービス投資禁止や政府高官の渡米禁止など一部制裁の緩和措置を取る用意があると発表した。また、駐ミャンマー米大使を近く指名すると明らかにした。
     民主化運動指導者アウンサンスーチー氏と同氏率いる国民民主連盟(NLD)が補選で圧勝したことを受け、制裁緩和に着手する姿勢を示すことで、一層の民主化を促すのが狙い。ただ、緩和の対象を限定し、全政治犯釈放や北朝鮮との軍事協力停止を含めた改革を推進するよう圧力を維持する方針も示した。

  • Jeremy Kun says:

    I recently used Senzee’s method for computing optimal stackings in Texas Hold ‘Em. Check it out! http://jeremykun.wordpress.com/2012/04/09/optimal-stacking-hold-em/

  • codestrikken says:

    Just wondering, did anyone create a 2+2 datafile version for a pre-sorted hand. I would imaging if the hand is sorted (ex highest to lowest) then the 2+2 .dat file would be much smaller since you would only have 1 possibility for a hand such as: As-Ad-2d-4c-Js (which would be passed as A-A-J-4-2) rather than all the various permutations of it (A-2-A-4-J , A-A-4-J-2…etc)

    • Fish says:

      Not sure, if I correctly understood the construction of the handrank.dat file than the pointer is the same for the A-2-A-4-J and A-A-4-J-2 and the also for the every permuted subset i.e. A 2 and 2 A, A 2 A and A A 2, and so on. Also the file contains only 32M integers and the number of different 7-card hands are 132M, so the pointer-(addresses) must be reused.

  • Ethan Waldo says:

    I’ve created a Ruby port of the CactusKev.PerfectHash. Although it increases execution speed from 82ms to over 4s, it may be easier for some non-C/C++ savvy programmers to absorb.

  • Ethan Waldo says:

    And here is the [actual link][1].

    [1]: https://github.com/Dishwasha/cactus_key

  • Leave a Reply