Skip to content
[OPEN_POKER]

PokerKit Poker Bot Tutorial: Add Real Hand Evaluation

JJoão Carvalho||8 min read

PokerKit is the fastest upgrade after your first Open Poker bot connects. The 47-line starter bot proves your WebSocket loop works, but PokerKit gives that loop a real hand evaluator, board parser, and simulation path. Use it for decisions. Keep Open Poker's WebSocket protocol for competition.

Part of: The Complete Guide to Building an AI Poker Bot in 2026 - the full pillar covering frameworks, decision logic, equity, testing, and where to compete.

Key Takeaways

  • PokerKit is a pure Python poker library with hand evaluation, simulations, and Texas Hold'em support.
  • Use PokerKit inside your decision function, not as a replacement for the Open Poker WebSocket client.
  • Start by converting Open Poker card strings into PokerKit's compact format, then compare StandardHighHand objects.

What should PokerKit do inside your bot?

PokerKit should answer one question: "How strong is my hand right now?" The Open Poker server sends game state over WebSocket, while PokerKit evaluates the cards locally. PokerKit's docs report standard high-hand evaluation at 1,016,740.7 hands per second on a single Intel i7-1255U core in Python 3.11.5 (PokerKit docs).

That speed is enough for normal Open Poker decisions because the platform gives your bot 120 seconds before an action times out (Open Poker actions docs). You don't need a neural network for the first upgrade. You need to stop treating ace-high and a flush as the same "call if cheap" branch.

The clean split is simple:

LayerToolJob
ConnectionOpen Poker WebSocketAuth, lobby, your_turn, actions
Card mathPokerKitEvaluate hands and compare outcomes
PolicyYour codeDecide fold, check, call, raise
FeedbackHand history and leaderboardCheck whether the policy works

If you're still writing your first event loop, start with Build a Poker Bot in Python. Come back here once that bot can sit at a table without timing out.

How do you install PokerKit for a bot?

Install PokerKit with pip install pokerkit. The current PokerKit README says the library requires Python 3.11 or above and is pure Python, so it fits the same deployment model as a normal Open Poker bot (PokerKit GitHub).

python -m venv .venv
. .venv/bin/activate
pip install websockets pokerkit

Keep websockets for the Open Poker connection. PokerKit doesn't log in, join the lobby, or submit actions. It only helps you reason about cards after the server sends a your_turn message.

The Open Poker protocol uses cards like Ah, Kd, and 7c. PokerKit accepts the same compact rank-suit strings. That means you can turn an Open Poker list into a PokerKit string with one join:

def compact(cards):
    return "".join(cards)
 
assert compact(["Ah", "Kd"]) == "AhKd"

PokerKit's docs note that string parsing is computationally expensive if you do it repeatedly in hot loops. For a first bot, strings are fine. When you start running thousands of Monte Carlo trials per decision, parse or cache more aggressively.

How do you evaluate a Texas Hold'em hand?

Use StandardHighHand.from_game(hole, board) for ordinary No-Limit Texas Hold'em. Open Poker is 6-max NLHE with 10/20 blinds and 1,000 to 5,000 chip buy-ins (season docs), so standard high-hand evaluation is the right default.

from pokerkit import StandardHighHand
 
 
def made_hand(hole_cards, board_cards):
    """Return the best standard high hand from hole cards plus board."""
    hole = "".join(hole_cards)
    board = "".join(board_cards)
    return StandardHighHand.from_game(hole, board)
 
 
hero = made_hand(["As", "Ah"], ["Ad", "7c", "2d", "9h", "Ks"])
villain = made_hand(["Kc", "Kh"], ["Ad", "7c", "2d", "9h", "Ks"])
 
if hero > villain:
    print("hero wins")

That comparison is the important part. PokerKit hand objects support normal comparison operators, which lets your bot rank outcomes without hand-written "pair beats high card" logic.

Don't overfit this too early. On the flop, most decisions aren't about the final made hand because two cards are still coming. Use made-hand strength for obvious decisions, then add equity estimation for draws and marginal calls. That's where PokerKit gets useful.

How do you wire PokerKit into your_turn?

Run PokerKit inside the your_turn branch, after you've cached your latest hole cards. The WebSocket your_turn message includes valid_actions, pot, community_cards, min_raise, max_raise, and a fresh turn_token (WebSocket docs).

from pokerkit import StandardHighHand
 
 
def current_hand_label(hole_cards, board_cards):
    if len(board_cards) < 3:
        return "preflop"
    hand = StandardHighHand.from_game("".join(hole_cards), "".join(board_cards))
    return str(hand)
 
 
def choose_action(msg, hole_cards):
    actions = {a["action"]: a for a in msg["valid_actions"]}
    board = msg.get("community_cards", [])
 
    # Preflop still needs ranges, not a made-hand evaluator.
    if len(board) == 0:
        if "raise" in actions and is_premium_preflop(hole_cards):
            return "raise", actions["raise"]["min"]
        if "check" in actions:
            return "check", None
        return "fold", None
 
    hand = StandardHighHand.from_game("".join(hole_cards), "".join(board))
    label = hand.entry.label.name
 
    if label in {"STRAIGHT_FLUSH", "FOUR_OF_A_KIND", "FULL_HOUSE", "FLUSH"}:
        if "raise" in actions:
            return "raise", actions["raise"]["min"]
        if "call" in actions:
            return "call", None
 
    if "check" in actions:
        return "check", None
    return "fold", None

That is intentionally conservative. It folds too much, but it won't donate stacks with bottom pair. Once this runs, combine it with poker bot betting strategy so strong hands raise to a useful size instead of always clicking the minimum.

When should you add equity instead of labels?

Add equity when a label hides important draws. A naked flush draw and a made pair can both be profitable in different spots, but a label-only bot misreads one of them. PokerKit is useful because you can simulate unknown turn and river cards instead of guessing.

The simplest path is:

  1. Build a 52-card deck.
  2. Remove hero cards and visible board cards.
  3. Sample opponent hole cards plus missing board cards.
  4. Compare final PokerKit hands.
  5. Repeat 1,000 to 10,000 times.

That gives you an estimated win probability. Then compare it to pot odds:

def pot_odds(pot, call_amount):
    return call_amount / (pot + call_amount)
 
 
def should_call(pot, call_amount, equity):
    return equity > pot_odds(pot, call_amount)

Open Poker already sends the pot and call amount in valid_actions, so you don't need to reconstruct the betting round. For the complete equity implementation, read Monte Carlo Equity Calculator in Python.

What should you not use PokerKit for?

Don't use PokerKit as a substitute for live testing. PokerKit can tell you whether a hand beats another hand, but it can't tell you whether your opponents overfold river bets, call too wide in the big blind, or punt after a failed bluff. Open Poker exists because self-play alone misses those patterns.

Also don't simulate the whole Open Poker table locally unless you have a specific reason. The server is authoritative. Your bot should react to your_turn, send legal actions with the current turn_token, and use client_action_id for deduplication. Rebuilding table state from scratch adds bugs before it adds edge.

A good first production shape is:

WebSocket message
  -> parse visible state
  -> PokerKit evaluates cards
  -> policy chooses action
  -> send action with turn_token
  -> log result for later review

That's enough structure to climb past most beginner bots. Then you can add position ranges, opponent modeling, and stack management one piece at a time.

What should you log after adding PokerKit?

Log the hand label, the street, the action, and the final result. Open Poker exposes public hand flow over WebSocket and season stats through the dashboard/API, but your own decision log is the only place that explains why the bot acted. Without that log, a better evaluator can still feel like a black box.

Start with one JSON line per decision:

def log_decision(msg, hole_cards, action, amount, hand_label):
    row = {
        "hand_id": msg.get("hand_id"),
        "street": "river" if len(msg.get("community_cards", [])) == 5 else "postflop",
        "hole": hole_cards,
        "board": msg.get("community_cards", []),
        "pot": msg["pot"],
        "hand": hand_label,
        "action": action,
        "amount": amount,
    }
    print(json.dumps(row))

After 200 to 500 hands, group the logs by hand and action. If your bot is folding too many one-pair hands to tiny bets, you will see it. If it is raising flushes but only calling full houses, you will see that too.

This is also how you avoid changing five things at once. Add PokerKit, run a sample, compare results to the old bot, then change one policy branch. The evaluator should make your bot easier to debug, not harder.

FAQ

Is PokerKit better than writing my own hand evaluator?

Yes for almost every Open Poker bot. PokerKit ships tested Hold'em hand evaluation, supports comparison operators, and reports 99% code coverage in its README. Your time is better spent on strategy, logging, and postflop policy.

Can PokerKit connect to Open Poker directly?

No. Open Poker uses WebSocket plus JSON for live play. PokerKit is a local Python library for card evaluation and simulation. Use both: WebSocket for the arena, PokerKit for decisions.

Should I use PokerKit or OpenSpiel?

Use PokerKit if you're building a live bot. Use OpenSpiel if you're researching algorithms across many games. OpenSpiel is excellent research infrastructure, but PokerKit is simpler when the task is "evaluate these Hold'em cards right now."

Does PokerKit make a bot profitable by itself?

No. A hand evaluator stops obvious mistakes, but profit comes from range selection, sizing, opponent modeling, and bankroll decisions. Add PokerKit first, then read position ranges for poker bots.

继续阅读