Skip to main content

Fast Texas Hold'em equity calculator with precomputed preflop lookup table

Project description

py-poker-equity

Fast Texas Hold'em equity calculator for Python. Computes win/loss/tie percentages for heads-up matchups at any stage of a hand.

  • Preflop: Instant lookup from a precomputed table (all 169x169 canonical matchups)
  • Flop/Turn/River: Exact enumeration of remaining board cards (no Monte Carlo, no approximation)
  • Zero dependencies: Pure Python, no C/Rust compilation, nothing to break

Installation

pip install py-poker-equity

Or install from source:

git clone https://github.com/mitchtabian/py-poker-equity.git
cd py-poker-equity
pip install -e .

Quick Start

from py_poker_equity import get_equity

# Preflop: AK suited vs pocket queens
result = get_equity(["Ah", "Kh"], ["Qd", "Qs"])
# {'a_win': 46.15, 'b_win': 53.15, 'tie': 0.70}

# Flop: AK suited vs pocket queens on a friendly board
result = get_equity(["Ah", "Kh"], ["Qd", "Qs"], board=["7h", "4h", "2c"])
# {'a_win': 54.55, 'b_win': 43.94, 'tie': 1.52}

# Turn
result = get_equity(["Ah", "Kh"], ["Qd", "Qs"], board=["7h", "4h", "2c", "Jd"])
# {'a_win': 43.18, 'b_win': 56.82, 'tie': 0.0}

# River (deterministic — one player wins or it's a tie)
result = get_equity(["Ah", "Kh"], ["Qd", "Qs"], board=["7h", "4h", "2c", "Jd", "Ac"])
# {'a_win': 100.0, 'b_win': 0.0, 'tie': 0.0}

API Reference

Card Notation

Cards are strings with rank + suit:

  • Ranks: 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A
  • Suits: h (hearts), d (diamonds), c (clubs), s (spades)

Examples: "Ah" (ace of hearts), "10s" (ten of spades), "2c" (two of clubs)


get_equity(hand_a, hand_b, board=None)

Calculate win/loss/tie percentages for a heads-up matchup.

Parameters:

  • hand_a — list of 2 card strings (player A's hole cards)
  • hand_b — list of 2 card strings (player B's hole cards)
  • board — list of 0, 3, 4, or 5 card strings (community cards). Default None = preflop.

Returns: dict with keys a_win, b_win, tie (percentages 0-100, rounded to 2 decimal places)

Raises: ValueError if duplicate cards are detected or board has invalid number of cards.

from py_poker_equity import get_equity

# Preflop (uses lookup table — instant)
get_equity(["Ah", "Kh"], ["Qd", "Qs"])

# Flop (enumerates ~946 remaining board combos)
get_equity(["Ah", "Kh"], ["Qd", "Qs"], board=["7h", "4h", "2c"])

# Turn (enumerates ~44 remaining cards)
get_equity(["Ah", "Kh"], ["Qd", "Qs"], board=["7h", "4h", "2c", "Jd"])

# River (just evaluates — one comparison)
get_equity(["Ah", "Kh"], ["Qd", "Qs"], board=["7h", "4h", "2c", "Jd", "Ac"])

get_preflop_equity(hand_a, hand_b)

Look up preflop equity directly from the precomputed table. This is called automatically by get_equity when no board is provided, but you can call it directly if you want.

Parameters:

  • hand_a — list of 2 card strings
  • hand_b — list of 2 card strings

Returns: dict with keys a_win, b_win, tie

from py_poker_equity import get_preflop_equity

get_preflop_equity(["Ah", "Kh"], ["Qd", "Qs"])
# {'a_win': 46.15, 'b_win': 53.15, 'tie': 0.70}

evaluate_hand(hole_cards, board)

Evaluate the best 5-card poker hand from hole cards + board.

Parameters:

  • hole_cards — list of 2 card strings
  • board — list of 3-5 card strings

Returns: HandResult namedtuple with:

  • hand_rank — int (1 = Royal Flush, 10 = High Card)
  • hand_name — string (e.g. "Full House", "Pair")
  • best_five — list of the 5 Card namedtuples that make the best hand
  • tiebreaker — tuple used for comparing hands of the same rank
from py_poker_equity import evaluate_hand

result = evaluate_hand(["Ah", "Kh"], ["Qh", "Jh", "10h", "2c", "3d"])
print(result.hand_name)   # "Royal Flush"
print(result.hand_rank)   # 1

result = evaluate_hand(["7s", "7d"], ["7c", "Ks", "Kh", "2c", "3d"])
print(result.hand_name)   # "Full House"
print(result.hand_rank)   # 4

Hand rank reference:

Rank Hand
1 Royal Flush
2 Straight Flush
3 Four of a Kind
4 Full House
5 Flush
6 Straight
7 Three of a Kind
8 Two Pair
9 Pair
10 High Card

compute_winner(hand_a, hand_b)

Determine the winner between two HandResult objects.

Parameters:

  • hand_aHandResult from evaluate_hand
  • hand_bHandResult from evaluate_hand

Returns: The winning HandResult object, or None if it's a tie.

from py_poker_equity import evaluate_hand, compute_winner

board = ["9c", "7s", "5h", "2c", "3d"]
aces = evaluate_hand(["Ah", "Ad"], board)
kings = evaluate_hand(["Kh", "Kd"], board)

winner = compute_winner(aces, kings)

if winner is aces:
    print("Aces win!")
elif winner is kings:
    print("Kings win!")
else:
    print("It's a tie!")
# Output: Aces win!

Preflop Table

The preflop lookup table ships with the package (752 KB). It contains exact equity for all 14,365 canonical hand matchups, sourced from the oscar6echo/Poker2 dataset which was computed via exhaustive enumeration of all possible boards.

If the table gets deleted or you want to regenerate it:

python -m py_poker_equity

This converts the reference CSV (included in the test fixtures) into the JSON lookup format. Takes about 2 seconds.

How It Works

Preflop: There are 169 canonical starting hand types in Texas Hold'em (13 pairs + 78 suited + 78 offsuit). The preflop table stores the exact equity for every possible matchup between these hand types. At runtime, hole cards are mapped to their canonical type and the result is looked up — no computation needed.

Post-flop: With 3+ community cards dealt, the remaining unknown cards are few enough to enumerate exhaustively:

  • Flop (2 cards to come): ~946 possible board completions
  • Turn (1 card to come): ~44 possible river cards
  • River (board complete): Just evaluate and compare

For each possible board completion, both hands are evaluated and the winner is tallied. This gives exact results — no simulation, no approximation.

Performance

Stage Method Speed
Preflop Lookup table Instant
Flop Enumerate ~946 boards ~50ms
Turn Enumerate ~44 cards ~2ms
River Single evaluation <1ms

Limitations

This library is heads-up only (2 players). It does not support multi-way equity calculations (3+ players).

Why? The preflop lookup table is the constraint. For 2 players there are ~14,000 canonical matchups — a few hundred KB. For 3 players it's 169^3 = ~4.8 million matchups (~115 MB). For 4 players it's ~815 million. It blows up exponentially and becomes impractical to ship as a pip package.

Post-flop multi-way is still feasible using the evaluate_hand and compute_winner functions directly. The enumeration logic is the same — you just evaluate 3+ hands per board runout instead of 2. On the flop that's ~946 board completions, which is fast regardless of player count. If you need multi-way equity post-flop, you can write a small wrapper around evaluate_hand:

from itertools import combinations
from py_poker_equity.evaluator import evaluate_hand, compute_winner, parse_card

def get_multiway_equity(hands, board):
    """
    hands: list of hole card lists, e.g. [["Ah", "Kh"], ["Qd", "Qs"], ["Jc", "10c"]]
    board: list of 3-5 board cards
    """
    all_known = [c for h in hands for c in h] + board
    known_set = set((parse_card(c).rank, parse_card(c).suit) for c in all_known)

    RANKS = ['2','3','4','5','6','7','8','9','10','J','Q','K','A']
    SUITS = ['h','d','c','s']
    remaining = [f"{r}{s}" for r in RANKS for s in SUITS
                 if (parse_card(f"{r}{s}").rank, parse_card(f"{r}{s}").suit) not in known_set]

    cards_needed = 5 - len(board)
    wins = [0] * len(hands)
    ties = 0

    for extra in combinations(remaining, cards_needed):
        full_board = board + list(extra)
        results = [evaluate_hand(h, full_board) for h in hands]
        # Find the best hand
        best = results[0]
        best_indices = [0]
        for i in range(1, len(results)):
            w = compute_winner(best, results[i])
            if w is results[i]:
                best = results[i]
                best_indices = [i]
            elif w is None:
                best_indices.append(i)
        if len(best_indices) == 1:
            wins[best_indices[0]] += 1
        else:
            ties += 1

    total = sum(wins) + ties
    return {
        'wins': [round(w / total * 100, 2) for w in wins],
        'tie': round(ties / total * 100, 2),
    }

The only thing this library can't do for multi-way is preflop — because there's no lookup table for 3+ players. If you know the stage of the all-in (flop, turn, or river), multi-way works fine.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

py_poker_equity-0.1.2.tar.gz (189.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

py_poker_equity-0.1.2-py3-none-any.whl (157.9 kB view details)

Uploaded Python 3

File details

Details for the file py_poker_equity-0.1.2.tar.gz.

File metadata

  • Download URL: py_poker_equity-0.1.2.tar.gz
  • Upload date:
  • Size: 189.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for py_poker_equity-0.1.2.tar.gz
Algorithm Hash digest
SHA256 eb51d4315d723dbc318a68312fe71fc63e41d34332789c7ba2b3cef5c4d8c18e
MD5 843385e027f5172cb9e84f2a663971ff
BLAKE2b-256 c2dc40ab95e0d96a1cbda9a14fe1bae3f3f5256f040444d6322f2fea87cd8040

See more details on using hashes here.

File details

Details for the file py_poker_equity-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: py_poker_equity-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 157.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for py_poker_equity-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c828469f66652e275a1e97719fac995c0994ed7969d66911968de8e087f64227
MD5 b019077d44ecc9167522d7ee3a2c38b0
BLAKE2b-256 598afae1a9ad1cf27556af432626cd0e8da0938461c39a645475698c527b1d43

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page