← Showcases / case 01

Case study

Counterplay · 过招

A native 6-max, 100BB training table where five GTO opponents play every hand through the same Pokerai API available to developers. Counterplay records every Hero decision, grades it against returned frequencies, groups recurring deviations into leak reports, and keeps the underlying hands ready for review or optional grounded coaching.

270lines — the whole API client
0cached strategy
10API endpoints in use

Updated · Maintained by Pokerai API

Counterplay / iOSreal product interface
Counterplay iOS live training table
Counterplay iOS analysis report

01 / architecture

A poker engine owns the hand. Pokerai API owns the strategy.

Counterplay separates game mechanics, session state, and strategic truth. PokerKit advances the six-handed table; the trainer service preserves the ordered action line, effective stacks, ranges, and review context; Pokerai API supplies presolved lookups or real-time solver output. The same response can drive opponent play, Hero grading, range visualization, and later coaching without embedding private charts in the app.

01 / experience

iOS app

SwiftUI interface with PokerKit game mechanics and decision capture.

02 / state

Trainer backend

FastAPI holds the current hand, action line, ranges, and review context.

03 / strategy

pokerai.bet

Presolved preflop and flop strategy plus queued turn and river solving.

02 / product loop

Train at the table. Find the pattern. Reopen the hand.

The product does more than expose one solver response. It turns individual decisions into an analysis report, then keeps the underlying hands available for review and follow-up study.

Analysis report

See repeated deviations

Group similar decisions, show sample counts and average deviation, then rank repeated preflop and postflop patterns by severity.

Counterplay analysis report showing recurring GTO deviations
Hand history

Return to the exact spot

Search by hand, position, or cards; inspect the result and net BB; then reopen the exact decision sequence behind a report.

Counterplay hand history with recent training hands
Discuss and lookup

Study a spot without playing a full hand

Choose quick presolved lookup or an exact live solve, define the pot and positions, and carry the result into a focused discussion.

Counterplay lookup screen for quick and exact GTO study
Trainer preferences

Control language, identity, and coaching model

Keep the training surface focused while letting the player choose locale, table identity, and the optional model used for explanations.

Counterplay settings with language, profile, and coaching model controls

03 / endpoint ledger

Every street maps to a documented API surface.

Traced from the real client (method → endpoint → where it's used):

Preflop/v1/gto/preflop

Villain preflop actions + grading Hero's preflop decision

Preflop/v1/gto/preflop/range

The 13×13 range grid shown in review

Flop/v1/gto/flop/tree

Postflop decision tree for the flop spot

Flop/v1/gto/flop/node

Strategy at the current flop node (+ grading)

Ranges/v1/gto/flop/projected-range

Update ranges along the flop action line

Ranges/v1/gto/range

Assemble solver_results for an action line

Turn/v1/gto/turn/projected-range

Flop → turn range handoff

Solver/v1/gto/solver

Submit a turn/river spot for real-time solving

Solver/v1/gto/solver/tree

Decision tree of the solved turn/river spot

Solver/v1/gto/solver/node

Strategy at a turn/river node (+ grading)

04 / implementation proof

The whole strategy layer fits in one file.

The client does not embed charts or solver output. It shapes the current spot, sends an authenticated request, and returns normalized strategy frequencies to the trainer.

01One shared authentication header.
02Typed methods named after public endpoints.
03One timeout and retry path for every street.
04No private strategy cache in the application.
app/gto/client.pythin client excerpt
class GtoClient:
    def __init__(self, settings):
        self._headers = {"Authorization": f"Bearer {settings.gto_api_key}"}

    async def preflop(self, hole_cards, hero_pos, preflop_actions):
        body = {"hole_cards": hole_cards,
                "positions": {"hero": hero_pos},
                "preflop_actions": preflop_actions}
        return _strategy_from(await self._post("/v1/gto/preflop", body))

    async def flop_node(self, node_token, hole_cards=None):
        body = {"node": node_token}
        if hole_cards: body["hole_cards"] = hole_cards
        return _strategy_from(await self._post("/v1/gto/flop/node", body))
    # … + solver_schedule / solver_tree / solver_node, all one-liners over _post()

05 / decision loop

From a dealt hand to grounded feedback.

01 / Preflop

Read the line

Send cards, position, and ordered actions; use returned frequencies for play and grading.

02 / Flop

Walk the tree

Load the presolved tree, resolve the active node, and project ranges after each action.

03 / Turn + river

Queue a solve

Hand ranges to the real-time solver, poll the job, then read the resulting strategy node.

04 / Review

Grade and explain

Measure the chosen action by its own GTO frequency and pass structured context to the optional coach.

Scope and provenance

Counterplay is a Pokerai API product integration maintained by Pokerai API. It is not presented as a third-party customer deployment. This technical record documents the iOS trainer workflow and the public API endpoints used by that workflow. The current product targets six-handed 100BB cash-game training, randomizes Hero's seat, uses five strategy-driven opponents, and reduces multiway postflop spots to a heads-up approximation. Coaching is optional. Counterplay does not enable real-time assistance at real-money tables. This page makes no claim about customer adoption, downloads, benchmark performance, commercial partnerships, or external endorsements.

Build on the same API

Turn strategy into a product.

Start with a free key, make one preflop call, then follow the same public endpoints used here.

Get your API key · Free