Poker Botがタイムアウトする理由:原因と非同期の修正方法
Poker botのタイムアウトは保持しているハンドを自動フォールドする。ポケットエース、ナッツフラッシュ、関係ない。サーバーがアクションを没収し、スタックが後退する。120秒のウィンドウはセッションのハンド#47で実際のボットがオーバーするのを見るまで寛大に聞こえる。原因と各バリアントの修正方法を解説する。
タイムアウトすると何が起こるか?
Open Pokerはyour_turnから有効なactionメッセージ送信まで120秒の厳格なウィンドウを強制する。期限を過ぎるとフォールド(またはチェック)が強制される。ボットはテーブルに残るが、ハンドは失われる。繰り返しのタイムアウトは切断につながる。タイムアウトリファレンスを参照。
タイムアウトは最も強いハンドに偏る。ボットは重要な判断により長く考える傾向がある。
なぜ120秒は実際には寛大でないのか?
1. 判断ループ内の同期ネットワーク呼び出し。 原因#1。同期呼び出しはボット全体をブロックする。
2. 判断中の再接続。
3. 長時間稼働ボットのガベージコレクションポーズ。
遅い判断の見つけ方
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')}")同期ネットワーク呼び出しの修正方法
すべてをasyncに変換。requestsの代わりにhttpxを使用:
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")タイムアウト付きで判断をラップする方法
async def decide_with_fallback(msg):
try:
return await asyncio.wait_for(decide(msg), timeout=10.0)
except asyncio.TimeoutError:
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)再接続の処理方法
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)FAQ
Open Pokerのアクションタイムアウトは? 120秒。時間切れで自動フォールド。
なぜ難しい判断でのみタイムアウトするのか? 複雑な判断はより多くのコードパスを実行するため。
タイムアウトを延長できるか? できない。120秒は全ボット固定。
合理的な目標レイテンシーは? 200ms未満が優秀。5秒以上は調査が必要。
タイムアウトはボット勝率を破壊する見えないバグだ。修正は防御的:すべての判断レイテンシーをログし、メインロジックをasyncio.wait_for()でラップし、あらゆる場所でasyncクライアントを使い、安全なフォールバックを用意する。最初のボットを構築する際にこれらのパターンを最初から組み込もう。