Daniel Beer

pan2

15 Dec 2017

pan2 is a poker odds calculator. For any game scenario, it produces a table showing win/loss probabilities for each player, plus the distribution of hand ranks that each player can expect. It can calculate odds for a variety of game rules including 5-card draw, Texas hold 'em, Omaha hold 'em, and lowball variants. Calculation for most games is extremely fast (tens of milliseconds on a modern PC). For more expensive evaluations, multithreaded processing is available.

Compile it with:

gcc -O1 -Wall -o pan2 pan2.c -pthread

Run with --help to get a list of options:

$./pan2 --help Usage: ./pan2 [options] [<hand1> <hand2> ...] General options: --help Show this text --version Show program version -i <n> Set number of iterations per player (default 10000) -t <n> Set number of parallel threads (default 1) Options for specifying game rules: -n <n> Number of private cards per player (default 5) -s <n> Number of public cards in the game (default 0) -N <n> Number of private cards usable (default all) -S <n> Number of public cards usable (default all) -j <n> Set number of wildcards/jokers in pack (default 0) -w <hand> Specify wildcards -l Lowball (worst hand wins) Options for specifying game scenarios: -p <hand> Public cards already dealt -d <cards> Dead cards <handN> Private cards already dealt to a given player Examples of game rules: 5-card draw: -n 5 Texas hold 'em: -n 2 -s 5 Omaha hold 'em: -n 4 -s 5 -N 2 -S 3 Hands should be specified as comma-separated lists of cards. Each card is specified as a face (2..9, 10, J, Q, K, A) and a suit character (S, C, D, H), or a single * character to indicate a joker. Use an empty string for a player whose cards are all unknown or undealt. Consider the following scenario, with calculated odds, from the ESPN World Series of Poker: The odds for this scenario can be specified as follows: $ time ./pan2 -n 2 -s 5 -p js,6s,5h,7h ad,8d ah,2d
Private cards: 2 (2 usable)
Public cards:  5 (5 usable)
Wild cards:
Ranking:       normal

Public dealt:  5H 6S 7H JS
Player 2:      2D AH

-----------------+-------+------
Player           |     1 |     2
-----------------+-------+------
High card        |  42.9 |  61.3
Pair             |  38.8 |  38.7
Two pair         |   0.0 |   0.0
Threes           |   0.0 |   0.0
Straight         |  18.3 |   0.0
Flush            |   0.0 |   0.0
Full house       |   0.0 |   0.0
Fours            |   0.0 |   0.0
Straight flush   |   0.0 |   0.0
-----------------+-------+------
Win              |  93.3 |   6.7
Tie              |   0.0 |   0.0
Value            |  93.3 |   6.7
-----------------+-------+------

real    0m0.074s
user    0m0.072s
sys     0m0.000s

Implementation details

pan2 calculates probabilities by simulating random deals of all unknown cards and then counting outcomes. It uses a Mersenne twister as a pseudo-random number generator, seeded with values derived from the scenario parameters in order to produce repeatable results.

When multithreaded evaluation is requested, a simple fork/join procedure is used. Each thread does the same with slightly different seed values, and the overall results are combined at the end.

Cards are represented internally by integers in the range 0..63. The first 52 numbers represent standard cards and the last 12 are jokers. This means that internally, each joker is a distinct card, although for most purposes they are treated as indistinguishable. Sets of cards are represented by 64-bit bitmasks.

A simple evaluation function called score_hand takes as input a set of usable cards and the set of cards which should be considered wild, in addition to jokers. It identifies the highest-value 5-card poker hand that can be made from the usable cards and returns a score. This evaluation is done in constant time.

Scores are two-part integers: upper bits indicate the major rank of a hand (three of a kind, full house, etc.), while the lower bits indicate the relative value of the hand within that rank. Scores can therefore be compared directly to determine a winning hand, or the upper bits can be used as an index when compiling a hand-rank histogram.

Finally, more complex scoring functions need to be used to implement game rules such as lowball and limits on usable cards (as in Omaha hold 'em). For a normally-ranked game with limits on usable cards, the scoring function enumerates all subsets of the available cards of a size equal to the usable card limit and performs simple evaluation, keeping track of the highest score found. Note that in cases where the limit is equal to the hand size (i.e. no effective limit), only one subset is enumerated.

For lowball poker, the scoring function enumerates all subsets of exactly 5 cards which don't violate the card-use limits. It evaluates each subset and keeps track of the lowest-scoring hand. This is often more expensive than the scoring function described above, even when the hand sizes and card-use limits are the same. The reason for having to do this is that the simple evaluation function determines the score of the highest-value subset in the cards it's given. Note also that wildcards in lowball poker still have their highest-possible-value interpretation in any hand they're used in.

Licence

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.