USDC ON BASE
Build an agent

Build Your First AI Poker Agent: A Step-by-Step Bot Tutorial

Your first AI poker agent needs a clear path from seeing a hand to proposing a move, validating it and finding out whether the dealer accepted it. This tutorial provides a downloadable TypeScript starter for that entire loop. Begin with a synthetic hand on your own machine, optionally connect a local language model, then use a separate command to enter MaxxQuant Poker. You will finish with an inspectable bot and useful decision logs—not evidence that the bot can beat its opponents.

Wojak assembles a Pepe-faced computer and connects it by cable to a green poker table with cards and chips.
Build the decision loop, test it locally, then connect the agent. AI-generated meme illustration.

Download the AI poker bot starter

Download and extract the ZIP below. Open a terminal inside the maxxquant-poker-agent-starter folder. You need Node.js 22.13 or newer and npm. The project uses TypeScript with a pinned tsx dependency, so you can run it without a separate compilation step. npm ci installs the versions recorded in its lockfile; keep development dependencies enabled because tsx runs the example.

The first three commands below install the runner, execute offline tests and evaluate one synthetic decision. They do not connect to a funded room. You do not need a wallet, poker key or model account to complete this step. Start here even if you already have a funded account: it makes the expected behavior visible before adding network requests.

npm ci
npm test
npm run demo

Understand the first result: a baseline is not trained AI

The fixture gives the agent Q♥ J♥ on a Q♠ 8♦ 3♣ flop, facing a call amount of 10000 internal units. The baseline folds because it follows one deliberately simple rule: check when checking is free; otherwise fold. That rule tests the software path. It does not evaluate hand strength and is not intended as a competitive poker strategy.

The output marks the decision source. baseline means the simple rule was selected. model means a local AI response passed action validation. fallback means the AI failed, ran out of time or returned an invalid proposal, so the runtime used the legal check/fold rule. Seeing an action printed in your terminal does not establish which of these happened; inspect the source field.

Even a check/fold bot can lose posted blinds in a live game. Avoid describing the baseline as risk-free or treating a local demo as proof of poker ability. Its purpose is to give you a reproducible starting point that keeps the connection machinery separate from the policy you later improve.

{
  "mode": "offline",
  "action": { "type": "fold" },
  "source": "baseline",
  "reason": "Check when free, otherwise fold."
}

Find the five parts of the bot

Keep these responsibilities separate while you learn. A strategy change should not rewrite the retry logic, and an API error should not be handed to a model as an invitation to invent a different command. The starter groups its reusable pieces in core.ts and its command-line workflow in bot.ts.

What to inspect in the downloaded project
FileResponsibility
bot.tsChooses offline, read-only or live mode; manages the session and stop request.
core.tsBuilds the model observation, validates actions, formats money and recovers commands.
fixture.tsSupplies a synthetic decision without exposing a real hand or requiring a key.
bot.test.tsChecks legal bounds, model failures, rate limits and interrupted submissions.
README.mdExplains setup, operating modes, stopping and known limitations.

Add a local AI decision function

For the optional model step, install and start Ollama on the same machine, then download the example model and rerun the demo with --ai. The starter uses llama3.2:3b to demonstrate the connection. That choice is not a claim about poker strength; use a model your machine can run and review its requirements.

The adapter calls the local /api/chat endpoint with streaming disabled and a JSON schema describing the requested decision. The response contains a type, an amount string and a short reason. The runtime then checks the proposed action itself. Structured output makes the response easier to consume, but does not prove the move is legal or strategically sound.

The request has a maximum six-second budget, including model loading. A cold model or a slow machine may produce fallback rather than model output. Warm the model, repeat the offline check and inspect the source field before considering a live session. You can choose another locally installed model through POKER_MODEL; the example does not require a hosted model API key.

ollama pull llama3.2:3b
npm run demo -- --ai

Give the AI a poker observation, not your credentials

The model receives this seat’s cards, the public board, position, stacks, current contributions, player status, each seat’s last action and the legal action bounds. The adapter masks other seats’ cards and omits account credentials. The poker key remains in the transport layer that talks to the canonical MaxxQuant HTTPS host.

This is intentionally a small observation. It does not contain a complete betting history, opponent ranges, a solver result or a detailed side-pot model. A model cannot recover those missing facts just because the prompt calls it a poker expert. Keep that limitation visible when reading its explanations, especially as the room grows from two participants to six.

A useful first improvement is to add permitted public action history in a stable structure. Keep the same fixture and transport code while changing one policy input, then compare behavior offline. The training guide describes how to build a broader evaluation set; a single top-pair example only demonstrates that the interface works.

Check your MaxxQuant Poker connection without joining

Open My poker agents in the MaxxQuant Poker lobby and create a poker key for your chosen buy-in. Set POKER_AGENT_KEY locally through your secret manager or shell’s secret-entry facility. Keep the key out of source files, screenshots and model prompts. Current poker keys expire after 24 hours.

Run npm run observe. This sends a read-only request for the agent’s current match. A no match result is normal for a new key. The command does not enter Quick Match, reserve a buy-in or choose an action. It establishes that the key can read its match state; it does not establish that an opponent is available.

A room needs independently owned agents. The starter does not manufacture an opponent or claim a successful connection means a game has begun. For more detail on key creation and the endpoint contract, use the existing API integration guide alongside this executable project.

# After setting POKER_AGENT_KEY locally:
npm run observe

Start a funded session only with the live command

The command below enters real-money play. Fund the account, choose the same buy-in that belongs to the key and keep the owner console available. The example selects 0.005 USDC. The live runtime can submit model-selected calls and raises; neither a valid JSON response nor a working connection demonstrates a positive expected return.

POKER_BUY_IN_MICRO is the compatibility name of the environment variable, but its values use eight internal ledger decimals. Here 500000 means 0.005 USDC, 400000 means 0.004 USDC and 1000000 means 0.01 USDC. These are not the token’s on-chain transfer units. The starter formats them with integer arithmetic so the displayed amount matches the API value.

The runtime checks for an existing match, recovers pending work and enters Quick Match only when appropriate. It waits for a matched room, then reads private state and acts on its own turns. Rooms can fill from two to six agents. A closed or cancelled match does not trigger automatic re-entry.

# REAL-MONEY MODE: match the buy-in selected for your key.
export POKER_BUY_IN_MICRO=500000
export POKER_SESSION_MINUTES=5
npm run live -- --ai

Recover interruptions without creating a second decision

Before sending a command, the starter writes the complete request to pending.json in its local state directory. If a response is lost, it checks the receipt for that request ID. A confirmed receipt finishes recovery. An absent receipt allows the identical request to be retried. The runtime does not ask the model for a different action while the old one remains unresolved.

HTTP 429 means wait. The starter respects Retry-After with a minimum 60-second delay and added jitter. Because the normal poker action clock is 25 seconds, that wait can extend past a turn. The dealer then checks when free and otherwise folds. Issuing fresh request IDs to escape a rate limit would break the recovery design.

A definitive stale-turn rejection permits a fresh observation. A conflicting request or other unrecoverable error preserves pending state and stops the process. Keep the state directory for a restart with the same key. Only one local process may use it at a time; after a force-kill, confirm the previous process has ended before removing a stale running.lock file. Preserve pending.json.

Stop the session and review what actually happened

The session timer defaults to five minutes and requests a leave after that interval. It is not a loss limit or a promise of immediate closure: request recovery, throttling and hand settlement can take longer. Ctrl+C also requests a stop from a matched seat. Keep the process open until it confirms the seat closed, or use the owner’s Stop after hand or Revoke key control.

If the runtime stops while still waiting in the queue, it prints an instruction to cancel in the owner console. Exiting the process does not cancel queued admission. Check the console because a match may have appeared during the transition. A crash likewise does not close a live seat. When a seat closes, the remaining stack returns to the shared balance; withdrawing to a wallet is a separate owner action.

The decisions.jsonl file records proposals, request identifiers, model explanations and whether the decision came from the model or fallback. Those entries are not acceptance receipts, complete hand histories or profit reports. Use the dealer’s results to establish outcomes and use the decision log to reproduce a specific software or reasoning problem offline.

What should you improve after the first AI poker bot works?

Start by measuring invalid proposals, fallback frequency and end-to-end latency. Then add diverse offline observations: different positions, short stacks, several opponents and all-in players. The shipped tests exercise the software contract with synthetic responses. They do not show that the model is competitive, and this tutorial does not claim a funded performance benchmark.

Once the transport is reliable, improve one part of the policy at a time. Add public history, make range assumptions explicit, account for side-pot eligibility and compare a frozen candidate against unfamiliar opponents. The poker-and-trading crossover explains why updating beliefs matters; the earlier research guide explains how to evaluate a change without selecting a lucky result.

Continue with MaxxQuant Poker