Skip to content
[OPEN_POKER]

Por Que Seu Poker Bot Dá Timeout: Causas e Correções Assíncronas

JJoão Carvalho||5 min read

Um timeout do poker bot faz auto-fold da mão que você estiver segurando. Pocket aces, nut flush, não importa: o servidor descarta a ação e seu stack anda para trás. A janela de 120 segundos parece generosa até você assistir um bot real estourar ela na mão #47 de uma sessão. Aqui está o que causa e como corrigir cada variante.

O que acontece quando seu poker bot dá timeout?

Open Poker impõe uma janela rígida de 120 segundos desde o momento que seu bot recebe your_turn até o momento que envia uma mensagem action válida. Se perder o prazo, o servidor força um fold (ou check, se check for a ação mais barata válida). Seu bot permanece na mesa e recebe a próxima mão normalmente, mas a mão que deu timeout se foi.

A penalidade vai além das fichas perdidas. O servidor registra cada timeout contra seu bot, e timeouts repetidos eventualmente acionam uma desconexão por "abuso consistente de timer." Comportamento completo do timer está documentado na referência de timeouts de ação.

Timeouts tendem a acontecer nas suas mãos mais fortes. Bots pensam mais em decisões que importam. Uma simulação Monte Carlo complexa roda mais rápido em 72 offsuit (insta-fold) do que em AKs enfrentando 3-bet. As mãos que você mais quer jogar são as que seu bot tem maior chance de dar timeout.

Por que 120 segundos não é realmente generoso?

1. Chamadas de rede síncronas dentro do loop de decisão. Causa #1. Um bot chama um avaliador de mãos que bate numa API externa, ou consulta banco de dados para stats de oponentes, ou usa requests.get() em vez de um cliente async. Cada chamada síncrona bloqueia o bot inteiro.

2. Reconexões durante uma decisão. Se sua rede oscila enquanto o bot está decidindo, o WebSocket desconecta, seu envio de ação falha silenciosamente, e você espera o timeout.

3. Pausas do garbage collector em bots de longa execução. Menos comum mas real. Um bot rodando há 6 horas acumulou dezenas de milhares de estados de jogo na memória.

Como encontrar quais decisões estão lentas?

Adicione logging de latência ao seu handler de ação:

import time
 
async def handle_your_turn(msg, ws):
    start = time.monotonic()
    try:
        action = await decide(msg)
        await ws.send(json.dumps({
            "type": "action",
            "action": action["type"],
            "amount": action.get("amount", 0),
            "client_action_id": f"a-{msg['turn_token'][:8]}",
            "turn_token": msg["turn_token"],
        }))
    finally:
        elapsed_ms = (time.monotonic() - start) * 1000
        if elapsed_ms > 1000:
            print(f"[SLOW] decision took {elapsed_ms:.0f}ms on hand {msg.get('hand_number')}")
        if elapsed_ms > 5000:
            print(f"[VERY SLOW] {elapsed_ms:.0f}ms, investigate")

Como corrigir chamadas de rede síncronas?

Converta tudo para async. Use httpx em vez de requests, AsyncAnthropic em vez de Anthropic:

import httpx
 
http = httpx.AsyncClient(timeout=3.0)
 
async def decide(msg):
    response = await http.get("https://api.example.com/equity")
    equity = response.json()["equity"]
    return ("call" if equity > 0.4 else "fold")

Como envolver decisões com timeout?

Use asyncio.wait_for() para limitar defensivamente o tempo de qualquer decisão:

import asyncio
 
async def decide_with_fallback(msg):
    try:
        return await asyncio.wait_for(decide(msg), timeout=10.0)
    except asyncio.TimeoutError:
        print(f"[TIMEOUT] decision exceeded 10s, falling back")
        return fallback_decision(msg)
 
def fallback_decision(msg):
    actions = {a["action"]: a for a in msg["valid_actions"]}
    if "check" in actions:
        return ("check", 0)
    if "call" in actions:
        call_amt = actions["call"]["amount"]
        if call_amt < msg.get("pot", 0) * 0.2:
            return ("call", call_amt)
    return ("fold", 0)

Como lidar com reconexões sem perder a janela de ação?

O padrão correto: detectar perda de conexão fora do loop de mensagens e reiniciar completamente:

while True:
    try:
        async with websockets.connect(WS_URL, additional_headers=headers) as ws:
            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)
                await handle_message(msg, ws)
    except websockets.ConnectionClosed:
        print("Connection lost, reconnecting in 2s...")
        await asyncio.sleep(2)

E as pausas do garbage collector?

Limite a retenção do histórico de mãos com deque:

from collections import deque
recent_hands = deque(maxlen=500)

Execute gc.collect() entre mãos, não durante decisões.

FAQ

Qual é o timeout de ação no Open Poker? 120 segundos do your_turn até sua resposta action. Se não responder a tempo, o servidor faz auto-fold (ou auto-check se possível).

Por que meu bot só dá timeout em decisões difíceis? Porque decisões difíceis ativam mais caminhos de código. Chamadas de rede síncronas, calculadoras de equity e lookups de perfil de oponentes rodam mais em spots complexos.

Posso estender o timeout de ação? Não. O timer de 120 segundos é fixo para todos os bots. Se está atingindo, o problema é no seu código.

Timeout custa fichas? Não diretamente: o servidor faz auto-fold, que custa o que já estava no pot. Mas timeouts repetidos desconectam você da mesa.

Qual é uma latência alvo razoável? Menos de 200ms é excelente. Menos de 1 segundo é normal. Acima de 5 segundos significa que tem uma chamada síncrona em algum lugar.


Timeouts são o bug invisível que destrói win rates de bots. A correção é majoritariamente defensiva: logue a latência de cada decisão, envolva sua lógica principal em asyncio.wait_for(), use clientes async em todo lugar, e tenha um fallback seguro. Construa seu primeiro bot com esses padrões desde o início e você nunca vai debugar um timeout em produção.

Continue Lendo