Construa seu primeiro bot de pôquer em Python
Aula 1 de 13: Conectar
Complete a primeira mão acompanhando a execução do bot. Python 3.11 ou superior, um terminal e uma chave de API do bot para jogar online. Os testes locais não exigem conta.
Comece com um cliente Python pequeno que se conecta ao Open Poker e responde às ações legais enviadas pelo servidor. Completar sua primeira mão é o objetivo desta lição; a estratégia vem depois.
Início do curso: baixe o checkpoint da lição acima para acompanhar a evolução do bot do curso. O cliente curto abaixo explica o loop de conexão. Mantenha o guia completo de construção aberto como referência.
O que você realmente precisa para começar?
Use Python 3.11 ou superior no curso. O cliente curto abaixo precisa de uma biblioteca:
python -m pip install "websockets>=14,<16"
É só isso. Você não precisa instalar SDK, framework ou mecanismo de jogo. Mantivemos o protocolo deliberadamente simples: seu bot se conecta por WebSocket, recebe o estado do jogo como mensagens JSON e envia as ações de volta como JSON. Se você consegue interpretar um dicionário, consegue construir um bot.
Você também precisará de uma chave da API de bots do Open Poker: entre, selecione seu bot e escolha Self Host. Armazene a chave em OPEN_POKER_API_KEY como variável de ambiente. Consulte o guia de registro. Mantenha as credenciais fora do código-fonte e de capturas de tela compartilhadas.
Esta lição usa mensagens WebSocket diretamente para que você possa ver o protocolo. Durante o aprendizado, registre os tipos de mensagem e os códigos de erro; evite publicar payloads brutos que contenham credenciais ou cartas privadas.
Como é o bot completo?
import asyncio
import json
import os
import uuid
import websockets
API_KEY = os.environ["OPEN_POKER_API_KEY"]
WS_URL = "wss://openpoker.ai/ws"
async def play():
headers = {"Authorization": f"Bearer {API_KEY}"}
async with websockets.connect(WS_URL, additional_headers=headers) as ws:
msg = json.loads(await ws.recv())
print(f"Connected as {msg['name']}")
await ws.send(json.dumps({"type": "set_auto_rebuy", "enabled": True}))
await ws.send(json.dumps({"type": "join_lobby", "buy_in": 2000}))
async for raw in ws:
msg = json.loads(raw)
t = msg.get("type")
if t == "your_turn":
actions = {a["action"]: a for a in msg["valid_actions"]}
if "check" in actions:
act = "check"
elif "call" in actions:
act = "call"
else:
act = "fold"
await ws.send(json.dumps({
"type": "action",
"hand_id": msg["hand_id"],
"action": act,
"client_action_id": str(uuid.uuid4()),
"turn_token": msg["turn_token"],
}))
elif t == "table_closed":
await ws.send(json.dumps({"type": "join_lobby", "buy_in": 2000}))
elif t == "season_ended":
await ws.send(json.dumps({"type": "join_lobby", "buy_in": 2000}))
elif t == "hand_result":
winners = msg.get("winners", [])
if winners:
print(f"Hand won by {winners[0]['name']} (+{winners[0].get('amount', 0)})")
asyncio.run(play())Salve-o como bot.py, defina OPEN_POKER_API_KEY como variável de ambiente e execute python bot.py. Uma conexão bem-sucedida imprime o nome do seu bot. Sentar-se e concluir uma mão depende da disponibilidade de outros bots. Este exemplo curto para quando ocorre uma desconexão; consulte as lições de confiabilidade antes de deixar um bot sem supervisão.
O que este bot realmente faz?
Ele é uma calling station, e também foi nosso primeiro bot.
Quando chega sua vez: dê check se puder (dinheiro grátis). Se não puder dar check, pague. Se não puder pagar, desista. Isso perde fichas lentamente porque você paga todas as apostas sem considerar a força da mão. Ainda assim, ele joga pôquer legalmente, permanece na mesa e oferece um loop completo de eventos sobre o qual você pode construir.
Os quatro conceitos que vale entender:
set_auto_rebuy diz ao servidor para recomprar automaticamente 1.500 fichas quando você quebrar. Sem isso, seu bot para de jogar depois de perder seu stack. Com isso, o servidor cuida das recompras (sujeitas a um intervalo) e seu bot continua indefinidamente.
join_lobby coloca você na fila de matchmaking. O campo buy_in define quantas fichas levar para a mesa. O intervalo válido é de 1.000 a 5.000; usamos 2.000 por padrão, que equivalem a 100 big blinds na estrutura de blinds 10/20. Quando há jogadores suficientes na fila, o matchmaking cria uma mesa 6-max.
turn_token é um token antirrepetição. Cada mensagem your_turn inclui um token novo. Você precisa devolvê-lo na ação. Se enviar um token antigo de um turno anterior, a ação será rejeitada. Use sempre o token do your_turn mais recente. Nunca o armazene em cache.
hand_id identifica a mão atual. Devolva-o junto com o token da mesma mensagem your_turn.
client_action_id identifica uma tentativa de ação. O servidor o devolve em action_ack; este exemplo usa um UUID. O código de recuperação precisa preservar a identidade da tentativa ao resolver uma confirmação incerta.
Quais mensagens WebSocket seu bot trata?
Seu bot recebe um fluxo contínuo de mensagens JSON. A maioria é informativa; você só precisa responder a your_turn. Mas entender as outras é o que permite construir um bot mais inteligente. Este é o conjunto completo que você encontrará:
| Mensagem | O que significa | Você responde? |
|---|---|---|
connected | Autenticação bem-sucedida; você está online | Não |
lobby_joined | Você está na fila de matchmaking | Não |
table_joined | Você está sentado em uma mesa | Não |
hand_start | Uma nova mão começa; aqui estão seu assento e o dealer | Não |
hole_cards | Suas duas cartas privadas (por exemplo, ["Ah", "Kd"]) | Não |
your_turn | Suas ações válidas, o pote e a mesa | Sim: envie uma ação |
player_action | Alguém (talvez você) agiu | Não |
community_cards | Flop, turn ou river distribuído | Não |
hand_result | A mão terminou; aqui está quem venceu | Não |
busted | Você ficou sem fichas | Não (a recompra automática cuida disso) |
table_closed | A mesa foi encerrada | Volte ao lobby |
season_ended | Transição de temporada | Volte ao lobby |
A referência completa de mensagens está em docs.openpoker.ai/api-reference/message-types. Cada campo de cada mensagem é documentado com exemplos JSON. Vale salvar nos favoritos; você consultará essa página o tempo todo.
Como deixá-lo mais inteligente: três ganhos rápidos
A calling station é uma linha de base de conectividade. Quando a conexão funcionar, experimente estas três estratégias e meça os efeitos em vez de presumir uma taxa de vitória específica.
1. Adicione um filtro pré-flop simples
A maioria das mãos iniciais do pôquer perde. Um filtro pré-flop simples que desiste dos 60% inferiores antes do flop coloca você à frente de qualquer calling station da plataforma. A seleção de mãos iniciais é a maior melhoria isolada que você pode fazer.
def should_play(cards):
"""Illustrative starting range, not a calibrated percentile."""
ranks = "23456789TJQKA"
r1 = ranks.index(cards[0][0])
r2 = ranks.index(cards[1][0])
high, low = max(r1, r2), min(r1, r2)
pair = r1 == r2
suited = cards[0][1] == cards[1][1]
if pair: return True # All pairs
if low >= 8: return True # Both cards ten or higher
if suited and high - low == 1 and low >= 7: return True # 98s+
if high == 12 and low >= 5: return True # A7+
return FalseArmazene suas cartas fechadas ao receber hole_cards e depois verifique should_play() no manipulador de your_turn. Com uma mão excluída, veja se essa ação é gratuita e legal; caso contrário, desista somente quando fold estiver em valid_actions.
2. Aumente com suas mãos fortes
A calling station nunca aumenta. Isso permite que os adversários vejam flops baratos contra você em todas as mãos. A correção: aumente com os 15% mais fortes das mãos pré-flop.
if "raise" in actions and should_raise(my_cards):
await ws.send(json.dumps({
"type": "action",
"hand_id": msg["hand_id"],
"action": "raise",
"amount": actions["raise"]["min"], # minimum raise
"client_action_id": next_id(),
"turn_token": msg["turn_token"],
}))A entrada raise em valid_actions informa exatamente os valores min e max. O campo amount é um valor raise-to (tamanho total da aposta), e não um incremento. Se o big blind for 20 e você quiser aumentar para 60, envie "amount": 60.
3. Use pot odds no pós-flop
Depois do flop, você tem informação real. As pot odds dizem se pagar é matematicamente correto: se o preço que você paga for menor que sua probabilidade de vencer, pague. Caso contrário, desista. Para a matemática completa, a entrada do glossário sobre pot odds traz exemplos resolvidos e armadilhas que confundem bots iniciantes.
def pot_odds_say_call(pot, call_amount, estimated_win_pct=0.3):
if call_amount == 0:
return True
odds = call_amount / (pot + call_amount)
return estimated_win_pct > oddsAté uma estimativa aproximada da sua probabilidade de vitória (30% por padrão, mais alta com top pair e mais baixa sem nada), combinada com pot odds, supera a calling station pura por uma margem ampla. A mensagem your_turn inclui o tamanho atual do pote, então você tem tudo de que precisa.
O que aprendemos executando este bot
Executei a calling station por mais de 1.200 mãos para obter uma linha de base real. Ela perdeu 2,4 big blinds a cada 100 mãos: não é catastrófico, mas é uma drenagem constante. O maior vazamento não era pagar apostas demais. Era pagar apostas no river sem nada. A calling station não entende “errei tudo e esta aposta é grande em relação ao pote”; ela simplesmente paga, sempre, e vai sangrando fichas.
A segunda coisa que me surpreendeu: os intervalos das recompras automáticas importam mais do que você imagina. Depois de quebrar, há um intervalo de 5 minutos no plano gratuito (2 minutos no Pro) antes da próxima recompra. Um bot que quebra com frequência passa muito tempo ausente. Acertar o gerenciamento do stack (não quebrar em primeiro lugar) traz retornos cumulativos além de simplesmente conservar fichas.
Adicionar should_play() da seção acima reduziu a taxa de perda para cerca de 0,8 bb/100 nos nossos testes, uma melhoria de 3 vezes com uma única função. O bot ainda perde, mas agora perde como um jogador medíocre, em vez de como um jogador quebrado. Esse é o ponto de partida para um trabalho de estratégia de verdade.
Não estamos afirmando que estas são amostras rigorosas. A variância em 6-max é alta, e 1.200 mãos representam uma janela pequena. Mas, direcionalmente, o padrão é consistente: a seleção pré-flop é a primeira alavanca; a agressividade pós-flop é a segunda.
Como reproduzir a linha de base de 1.200 mãos?
Trate a execução de 1.200 mãos da calling station como uma linha de base de engenharia, não como um indicador de lucratividade. O resultado registrado foi de -2,4 bb/100. O resultado posterior com filtro pré-flop ficou em cerca de -0,8 bb/100, mas essa execução não preservou metadados suficientes para uma comparação direta limpa. Publicamos essa limitação porque um número sem seu método é marketing, não evidência.
Para uma comparação reproduzível, fixe a revisão do bot e registre estes campos em cada execução:
| Campo | Por que pertence ao benchmark |
|---|---|
| Commit do Git e hash da configuração | Comprova qual política produziu as ações |
| Horário UTC de início e fim | Expõe diferenças de campo e disponibilidade |
| IDs das mãos concluídas | Torna a amostra auditável e evita contagem dupla |
| Big blinds ganhados ou perdidos por 100 mãos | Normaliza resultados entre níveis de blinds |
Contagem de action_rejected | Detecta erros de protocolo disfarçados de perdas de estratégia |
| Timeouts de turno e reconexões | Separa qualidade das decisões de falhas de execução |
| Número de adversários e distribuição dos assentos | Mostra se uma mesa dominou o resultado |
Execute a linha de base e o candidato com a mesma quantidade mínima de mãos, mantenha as duas listas brutas de IDs de mão e informe intervalos de confiança antes de afirmar que houve uma melhoria real. Com 1.200 mãos, o resultado é útil para encontrar vazamentos óbvios, como pagar apostas no river sem avaliar a situação. Não é suficiente para classificar estratégias de pôquer.

Captura de tela do produto de primeira parte feita em 10 de março de 2026. Esta é a interface de auditoria de resultados, não a execução de 1.200 mãos da calling station. IDs de mão e resultados por mão são a trilha de evidências que um benchmark deve conservar.
O que esperar da tabela de classificação
A calling station básica é um teste de conectividade, não uma estratégia competitiva. Adicionar as três melhorias remove vazamentos óbvios, mas nenhuma posição específica na tabela de classificação resulta delas. O campo muda a cada temporada e amostras curtas são ruidosas. Para continuar melhorando, adicione avaliação de mãos, modelagem de adversários, gerenciamento de stack e consciência de posição; depois meça cada alteração contra uma linha de base fixada.
Seu bot precisa de pelo menos 10 mãos para aparecer na tabela de classificação. O tempo necessário depende da disponibilidade das mesas e da velocidade de jogo.
A documentação completa da plataforma está em docs.openpoker.ai. O guia de ações e estratégia detalha a semântica de raise, tokens de turno e comportamento de timeout. A documentação da biblioteca websockets vale a leitura se você quiser lidar com conexões assíncronas além do básico mostrado aqui.
Perguntas frequentes
Meu bot se conecta, mas nunca consegue um assento. O matchmaking precisa de 2 ou mais jogadores na fila. Se ninguém mais estiver jogando, seu bot espera. Consulte a tabela de classificação para ver se há outros ativos; não registre agentes de produção independentes extras só para preencher um segundo assento.
Recebo erros action_rejected.
Verifique o código de rejeição e confirme que hand_id e turn_token vêm da mesma mensagem your_turn atual. Não reutilize a autoridade de um turno antigo.
Meu bot se desconectou e perdeu o assento. Você tem 120 segundos para se reconectar. Se voltar a tempo, seu assento é preservado. Depois de 120 segundos, seu stack retorna ao saldo e você precisará entrar novamente no lobby.
Posso executar este bot 24 horas por dia, 7 dias por semana? O exemplo curto serve para uma primeira sessão supervisionada. Jogar sem supervisão exige reconexão, recuperação de estado, tratamento de prazos e acompanhamento de confirmações. Continue até a etapa de confiabilidade antes de tentar.
Quanto devo colocar na mesa? O intervalo válido é de 1.000 a 5.000 fichas. Usamos 2.000 nos exemplos (100 big blinds em blinds 10/20), um valor inicial padrão para stacks profundos. Entrar com menos (1.000) reduz a variância, mas também limita quanto você pode ganhar em uma única mão. Entrar com mais (5.000) é aceitável quando seu bot já tiver uma estratégia básica de desistir e aumentar; não faça isso com uma calling station pura.
Depois que seu bot completar uma mão, continue para a próxima lição abaixo. Se o lobby estiver esperando, mantenha a sessão aberta para os adversários e use o checkpoint offline para verificar seu cliente enquanto isso.
Confira o resultado da aula 1
- O que muda
- Conecte um cliente e envie uma ação permitida.
- Saída esperada
- completed_hands: 1
- Confira se funcionou
- Espere uma mão real terminar. Um teste com dados simulados não conta como uma partida online.
Extraia o arquivo da aula, abra a pasta em um terminal e execute:
python -m pip install -r requirements.txt
python bot.py --lesson 1 --self-test
python bot.py --lesson 1 --hands 3 --report run.jsonO teste usa dados simulados, sem conexão, e exibe checkpoint: passed. Para jogar online, você precisa de OPEN_POKER_API_KEY no ambiente de execução e de adversários disponíveis. Consulte o README incluído para configurar o bot e entender os limites de recuperação.
Todas as aulas do curso
- 1. Crie um bot de pôquer em Python
- 2. Arquitetura básica de um bot de pôquer
- 3. Como depurar erros de WebSocket no bot
- 4. Por que o bot fica sem tempo para agir
- 5. Matemática do pôquer para bots
- 6. Ranges por posição para bots de pôquer
- 7. Estratégia de apostas para bots de pôquer
- 8. Tutorial de PokerKit
- 9. Calculadora de equidade por Monte Carlo
- 10. Modelagem de adversários
- 11. Arquitetura avançada de um bot de pôquer
- 12. Como funciona a pontuação da classificação
- 13. Arquitetura profissional de um bot de pôquer