PH Evaluator: poker hand strength by perfect hash lookup
Poker-Hand-Evaluator: An efficient poker hand evaluation algorithm and its implementation, supporting 7-card poker and Omaha poker evaluation
At a glance
- What is it?
- PH Evaluator scores poker hands by encoding them into a perfect hash and reading the answer from a pre-computed table, covering five to seven card hands and Omaha variants in C, C++ and Python. It suits game servers and simulators that need a raw strength ranking fast, and the Omaha tables are large enough that the memory cost is the first thing to check.
- Who is it for?
- Adopt PH Evaluator if you need to rank many hands quickly, for a game server, a Monte Carlo equity estimator or a training tool, and if the memory cost of the table you need fits your process. Do not adopt it for a five-card only game, where a direct classifier over the 7462 ranks is smaller and simpler, and do not adopt the PLO6 table without checking that 353 megabytes is affordable.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem: scoring a poker hand without trying every combination
Efficiently evaluating a poker hand is described in the README as an interesting and challenging problem. The general form of the question is whether two different hands can be compared, or more generally whether one hand can be assigned a score indicating its strength.
Cactus Kev gave an answer for five-card evaluation, and with a smart encoding he ranks each hand to 7462 distinct values. The scheme became known well beyond the pages it first appeared on, and it is still the reference point for this project.
The limitation of that answer is where PH Evaluator starts. Kev's solution is specific to a five-card hand, and the popular real-world case is seven cards, because that is Texas Hold'em. Evaluating a seven-card hand with Kev's algorithm by brute force means iterating all 7 choose 5 combinations and running the five-card evaluation 21 times to find the best answer, which the README calls apparently too time-inefficient.
Omaha is worse, because it requires picking exactly two cards from four player's cards and exactly three from five community cards. Brute force there takes 60 iterations of the five-card algorithm, since 5 choose 3 multiplied by 4 choose 2.
PH Evaluator is designed for evaluating hands with more than five cards, and the design choice is the one that makes it interesting. Rather than traversing all the combinations, it uses a perfect hash algorithm to get the hand strength from a pre-computed hash table, which the README says costs very few CPU cycles and considerably small memory, about 100kb for the seven-card evaluation. With a slight modification, the same algorithm applies to Omaha hands.
The project is licensed under Apache-2.0, the default branch is `develop`, and the last push to the tree was on 2026-10-13. There are no GitHub releases, so the code in the repository is the artefact.
What a perfect hash buys you: a table lookup instead of a search
The mechanism is worth understanding at the level the documentation supports, because it explains both the speed and the memory numbers.
A brute force evaluator has to consider candidate subsets. A seven-card hand has 21 five-card subsets, and each subset has to be classified before the best one is kept. A perfect hash evaluator inverts that. The hand is encoded into a key, the key is looked up in a table that was computed ahead of time, and the table entry already contains the answer. There is no search over subsets at evaluation time, because the search happened once, offline, when the table was generated.
That is why the memory figure and the speed figure are the pair of numbers that matter. The seven-card table is about 100kb, and the cost of an evaluation becomes a hash computation plus a table access. The Omaha case is the interesting one, because the table for PLO6 is far larger than the one for seven cards.
The memory table in the README, measured by `pmap`, shows the shape of that cost. The five, six and seven-card libraries come in at 60k, 84k and 144k, and the combined library `pheval` at 648k. The Omaha libraries are the outlier: `phevalplo4` at 30516k, `phevalplo5` at 112296k, and `phevalplo6` at 353484k. So the more community cards an Omaha variant uses, the larger the pre-computed table becomes, and the six-card Omaha case is nearly six thousand times the size of the five-card table.
That is a real design trade rather than a free win, and it is the reason the seven-card table fits comfortably in a cache while the PLO6 table does not. It is also why the generated tables are data rather than code, and why anyone building on this has to ship the table with the library.
The benchmark numbers, and what they do and do not show
The README reports a Google Benchmark run, and the numbers are worth reading carefully because the two tables measure different things.
The benchmark binary is run as shown.
$ ./benchmark_phevaluator
Running ./benchmark_phevaluatorThe first table of results is exhaustive. Evaluating all five-card hands takes 33412347 ns for 2598960 hands, which the README puts at 77 million hands per second. All six-card hands take 286893486 ns for 20358520 hands, 71 million per second. All seven-card hands take 2229689783 ns for 133784560 hands, 60 million per second. The per-hand cost rises only modestly as you add a card, and the throughput stays in the same tens-of-millions range.
The second set of figures is on random samples of 100 hands, and it is the one that looks slower: 1376 ns for a random five-card hand, 1778 ns for a random seven-card hand, and 3054, 3300 and 3441 ns for the PLO4, PLO5 and PLO6 cases. The README gives the reason, that the performance on random samples are slightly worse due to the overhead of accessing the pre-generated random samples in the memory. In other words, the sample set does not fit in cache the way the exhaustive sweep does, so the measurement includes memory latency that the exhaustive run amortises.
For an engineer choosing between this and a brute force implementation, the exhaustive figures are the ones to compare against, and they are the ones that describe steady state on a hot table.
One caveat about the whole table: the run was recorded on a 12 core 2600 MHz CPU with a 12288 KiB L3 cache, and the numbers are a snapshot from a dated run. They describe that machine, not yours.
The C and C++ library and the Python package
Two implementations ship in the repository, and they cover different ground.
The `cpp` subdirectory holds the C and C++ implementation of the algorithm, offering evaluation from five-card hands to seven-card hands, as well as Omaha poker hands including PLO4, PLO5 and PLO6. The presence of a C implementation with a C++ friendly surface is what makes it usable from C++ projects and from anything with a FFI, and the benchmark output in the README comes from a C++ benchmark binary.
The `python` subdirectory has the Python implementation, which is the one published to PyPI under the name `phevaluator`. The README notes that it currently supports five-card, six-card and seven-card poker hands evaluation, as well as Omaha poker hands evaluation, and the install is a single pip command.
pip3 install phevaluatorThe README credits the community for the Python work: it thanks to the community for contributing to the Python implementations, naming azriel1rf, ohwi and bensi94. The project also publishes a documentation site at henryrlee.github.io/PokerHandEvaluator, and the algorithm itself is described in a separate document at `Documentation/Algorithm.md`.
For a.NET, Java or Go team, the answer is not in this repository directly. The README lists a set of ports maintained elsewhere: a JavaScript port called PHE by Thorsten Lorenz, another JavaScript port called 41Poker by 41semicolon, a Dart port called poker by Kohei, a Haskell implementation in the ghais/poker repository, a Go port called gophe by mattlangl, a TypeScript port called poker-handle by pocketberserker, a C# port called PokerHandEvaluator.cs by travisstaloch, a Rust port called poker_engine by Alexander Leones, and a CUDA implementation inside the Poker-Calculator repository.
That list is a useful piece of information in itself. It tells you the algorithm has been ported into most of the languages a team might reach for, and it tells you which implementations are outside the maintainer's control.
Where the design limits you, and the alternative of writing your own
There are three limitations, and they follow from the design rather than from a missing feature.
The first is the memory cost of the Omaha tables, described above. A process that embeds `phevalplo6` carries roughly 353 megabytes of table data. For a game server that is fine, because one copy serves many hands. For a mobile client or a small container, it is not, and the seven-card table at 144k is the one most people actually want.
The second is the fixed scope of the evaluation rules. The library answers which hand is stronger. It does not model betting, pot odds, player ranges, or equity, and it does not simulate. For a pre-flop equity estimator you need something on top of it, and the README's awesome use cases section shows exactly that pattern: a Reddit user coded a Hold'em pre-flop equity estimator in C++ using the PHEvaluator library, running random playouts, with the source in a linked sim.cc gist. The evaluator is the primitive, and the estimator is the application built on it.
The third is that the tables are generated data. A rule change in the poker variant you care about, and the Omaha variants are where the complexity lives, means regenerating or extending the table rather than patching a comparison function. The README does not document that generation pipeline in the excerpt available here, so the practical advice is to look before you commit to a variant.
The alternative approach is to write a direct five-card evaluator and accept the combination cost. For a game that only ever deals five cards, a straightforward classifier over the 7462 possible ranks is fast enough and has no table to ship, and it is a few hundred lines rather than a build step plus a megabyte of data. The trade is direct. The direct evaluator is simpler and smaller, and the perfect hash evaluator is faster and handles the seven-card and Omaha cases without the 21 or 60 iteration penalty.
There is a third option worth naming, which is a general poker odds library that includes hand evaluation alongside equity calculation and hand range modelling. The difference is scope. This project is one primitive done very well, and the broader library is many features done adequately, and which one you want depends entirely on whether you are building a game server that needs raw speed or an analysis tool that needs more than ranking.
Editorial conclusion
Adopt PH Evaluator if you need to rank many hands quickly, for a game server, a Monte Carlo equity estimator or a training tool, and if the memory cost of the table you need fits your process. Do not adopt it for a five-card only game, where a direct classifier over the 7462 ranks is smaller and simpler, and do not adopt the PLO6 table without checking that 353 megabytes is affordable. Before you build on it, pick the language from the ports listed in the README or use the C library directly, then benchmark on your own hardware, because the published numbers describe a dated run on a 12 core 2600 MHz machine rather than your workload.
Frequently asked questions
What is PH Evaluator and how does it evaluate a 7-card poker hand?
It is a poker hand evaluator based on a perfect hash algorithm, designed for hands of more than five cards. Instead of traversing all the combinations, it gets the hand strength from a pre-computed hash table, which the README says costs very few CPU cycles and about 100kb of memory for the 7-card evaluation.
Can I install the Python version of the poker hand evaluator?
Yes. The Python implementation is published on PyPI as `phevaluator` and installs with `pip3 install phevaluator`. It supports 5-card, 6-card and 7-card hands as well as Omaha hands, and the README credits community contributors for the Python work.
How much memory does the Omaha evaluation take?
Measured by `pmap`, the README lists `phevalplo4` at 30516k, `phevalplo5` at 112296k and `phevalplo6` at 353484k, against 60k for the five-card library, 84k for six-card, 144k for seven-card and 648k for the combined `pheval` library.
Which languages has the PH Evaluator algorithm been ported to?
The README lists ports in JavaScript as PHE and 41Poker, in Dart as poker, in Haskell in the ghais/poker repository, in Go as gophe, in TypeScript as poker-handle, in C# as PokerHandEvaluator.cs, in Rust as poker_engine, plus a CUDA implementation inside the Poker-Calculator repository.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/henryrlee-pokerhandevaluator)Was this useful?