Skip to content
[OPEN_POKER]

7日間でゼロからOpen Pokerリーダーボードへ:デイリープラン

JJoão Carvalho||17 min read

ポーカーボットを「存在しない」から「Open Pokerリーダーボードにランクイン」まで7日間で到達できる。7週間ではない。プラットフォームは開発者が1日目に登録、接続、プレイし、その後1日1つの戦略アップグレードを追加して競争力を持てるように設計されている。ここでは推奨プランを、差をつけるアップグレードとそうでないものの両方を含めて紹介する。

なぜ7日間プランが機能するのか?

毎日、動いているボットの上に単一の独立した改善を追加するからだ。最初のハンドの前にエンドツーエンドのシステムを構築するのではない。1日目にコーリングステーションを稼働させ、段階的に改善する。7日目までに、すべてのアップグレードが実際の対戦相手に対して検証されている。嘘をつくセルフプレイシミュレーターに対してではない。

これはほとんどのボットプロジェクトが失敗する方法の正反対だ。ほとんどの開発者は2週間かけて「完全な」アーキテクチャを設計し、14日目にハンドエバリュエーターがプロトコルのカード形式と一致しないこと、アクション送信がターントークンを忘れていること、ポットオッズ計算が間違ったフィールドを使っていることに気づく。7日間プランはボットがすでに稼働するまでそのすべてを延期する。

もう1つの理由:Open Pokerのリーダーボードはピークスキルよりもボリュームに報酬を与える。7日間動く凡庸なボットは、1日だけ動く優秀なボットを上回る。7日目までに4-5日分のハンド履歴が蓄積され、戦略がまだ粗くてもフィールドの底から浮上するのに十分な分散削減になる。

Day 1:コーリングステーションを稼働させる

1日目の唯一の目標は、合法的なハンドをプレイする接続されたボットだ。戦略を心配しない。利益を心配しない。心配するのは:ボットが接続する、ボットがテーブルに参加する、ボットがyour_turnに有効なアクションで応答する、ボットが30分間クラッシュしない、これだけ。

Pythonでポーカーボットを作るガイドを出発点として使う。完全なボットは50行未満。コピーして、APIキーを貼り付けて、実行。30秒以内に「Connected as あなたのボット名」が表示され、ロビー参加後1-2分で最初のハンドが来る。

コーリングステーション戦略(チェックできるならチェック、できなければコール、それもできなければフォールド)はチップを失うが、完全なイベントループを生成する。すべてのメッセージタイプが処理される。すべてのアクションが有効なターントークンで送信される。1日目の終わりまでにボットは50-100ハンドプレイし、改善のためのベースライン勝率がある。

終日目標:ボットが少なくとも20ハンドプレイ、クラッシュなし、タイムアウトなし。

Day 2:プリフロップハンド選択を追加

コーリングステーションの上に行える最大の単一改善。Hold'emのスターティングハンドの大半は負け手。プリフロップで下位60%をフォールドすると、フロップ以降で失うチップが劇的に減る。

def should_play_preflop(cards):
    """Top ~40% of starting hands."""
    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
    if high >= 10: return True
    if high >= 8 and suited: return True
    if high == 12 and low >= 7: return True
    return False

hole_cardsが届いたらホールカードを保存。your_turnで、プリフロップかつshould_play_preflop()がFalseならフォールド。それ以外はコーリングステーションロジックにフォールバック。

この単一の変更で、コーリングステーションが通常-2.5 bb/100から約-0.8 bb/100に改善。まだチップを失っているが、頻繁なリバイを引き起こさないレートだ。終日目標:200+ハンドプレイ、勝率12%以上。

Day 3:プリフロップレイズを追加

Day 2のボットは悪いハンドをフォールドするが、良いハンドでレイズしない。相手がプレミアムハンドに対して安いフロップを見られる。修正:トップ15%のハンドでコールだけでなくレイズ。

def should_raise_preflop(cards):
    ranks = "23456789TJQKA"
    r1, r2 = ranks.index(cards[0][0]), ranks.index(cards[1][0])
    pair = r1 == r2
    high, low = max(r1, r2), min(r1, r2)
    suited = cards[0][1] == cards[1][1]
    # Premium pairs
    if pair and r1 >= 8: return True  # 99+
    # Big aces
    if high == 12 and low >= 9: return True  # AT+
    # Suited broadway
    if suited and high >= 10 and low >= 9: return True
    return False

これがTrueを返し、raisevalid_actionsにある場合、コールだけでなくミニレイズを送信。amountフィールドはraise-toの合計で、増分ではない。raiseエントリーのmin値をamountとして使用。

終日目標:400+ハンドプレイ、勝率16%以上、bb/100がbreak-even付近。

Day 4:ポストフロップコールにポットオッズを追加

ポストフロップはボットが最もチップを失う場所だ。計算せずにランダムなベットにコールするのが、コーリングステーションをコーリングステーションたらしめている。ポットオッズで修正。

計算:コール額をコール後のポットで割る。推定勝率がその比率を超えるならコール。そうでなければフォールド。ボット向けポーカー数学チュートリアルが公式を詳しくカバー。

def call_is_profitable(your_turn_msg, win_pct=0.30):
    actions = {a["action"]: a for a in your_turn_msg["valid_actions"]}
    if "call" not in actions:
        return False
    pot = your_turn_msg["pot"]
    call_amt = actions["call"]["amount"]
    if call_amt == 0:
        return True
    pot_odds = call_amt / (pot + call_amt)
    return win_pct > pot_odds

Day 4では一律30%の勝率推定を使う。ほとんどの特定のスポットでは間違っているが、平均すると「常にコール」よりはるかに良いデフォルトだ。何も持っていないリバーではフォールドし、現実的なエクイティのあるハンドでは続行する。

終日目標:600+ハンドプレイ、bb/100がbreak-evenまたはわずかにプラス。

Day 5:相手のVPIPを追跡

Day 5は毎ハンド同じプレイをやめて、テーブルに適応し始める時だ。各プレイヤーがプリフロップでどのくらいの頻度でチップを入れるかを記録するシンプルな相手トラッカーを構築。オポーネントモデリングガイドに完全な実装があるが、最小限の実行可能バージョン:

from collections import defaultdict
 
vpip_data = defaultdict(lambda: {"hands": 0, "voluntary": 0})
acted_this_hand = set()
 
def on_hand_start():
    acted_this_hand.clear()
 
def on_player_action(msg):
    name = msg.get("name", f"seat{msg['seat']}")
    if msg["action"] in ("call", "raise", "all_in"):
        if name not in acted_this_hand:
            vpip_data[name]["hands"] += 1
            vpip_data[name]["voluntary"] += 1
            acted_this_hand.add(name)
 
def get_vpip(name, default=0.25):
    data = vpip_data[name]
    if data["hands"] < 20:
        return default
    return data["voluntary"] / data["hands"]

VPIPを使ってレイズレンジを調整。レイトポジションでVPIP 15%未満の相手には、スティールレンジを広げる。VPIP 50%以上の相手には、バリューベットのみ(ブラフしない:彼らはコールしすぎる)。

終日目標:800+ハンド、ボットが少なくとも5人の異なる相手を識別。

Day 6:ベットサイジングのバリエーションを追加

固定ベットサイジングが次のリーク。初心者ボットの多くは常にミニレイズか常にポットベットをする。どちらも間違い。状況に応じて3つのサイズを混ぜる:

スポットサイズ根拠
レイトポジションからのプリフロップオープン2.5x BBスタンダードオープン、スティール頻度とバリューのバランス
ドライボードでのContinuation bet33% ポット安価で、弱いハンドをフォールドさせる
ウェットボードでのバリューベット75% ポットドローに課金し、バリューのためにポットを構築

スポットに基づいてレイズ額を計算し、valid_actionsmin/maxにクランプして送信。サイズを混ぜることで著しく搾取されにくくなる。パターンを追跡する相手は、ベットサイズだけからハンドの強さを推測できなくなる。ベッティング戦略チュートリアルに完全な内訳がある。

終日目標:1,000+ハンド、bb/100が測定可能なプラス、リーダーボードトップ50%。

Day 7:稼働、監視、イテレーション

Day 7は新機能についてではない。ボットを12時間以上連続で稼働させ、リーダーボードを観察し、壊れたものを修正することだ。デバッグガイドが最も一般的な障害モード(認証問題、タイムアウト、レースコンディション)をカバー。

確認すべき3つのこと:

1. クラッシュログ。 メインループをtry/exceptで囲み、すべての例外をタイムスタンプ付きでログ。4時間稼働させてからログを読む。最も頻繁なエラーを最初に修正。

2. 遅い判断。 タイムアウトガイドからレイテンシーロギングを追加。1秒以上のものは調査する価値がある。

3. 時間帯別勝率。 ハンド履歴を時間帯ごとにグループ化し、勝率が変動するか確認。一部のボットはピーク時間帯(相手が多い=弱いプレイヤーが多い)に勝つがオフピークで負ける。これを使ってボットをいつ稼働させるか決定。

終日目標:ボットが7日間で1,500+ハンドプレイ、現在のシーズンリーダーボードのトップ25%にランクイン、12時間以上無人で稼働。

7日間の進行は実際にどう見える?

このプランに従った複数の開発者で観察した概算の勝率推移:

Day追加した戦略中央値 bb/100終日までのハンド数
1コーリングステーション-2.550
2プリフロップフォールドレンジ-0.8200
3プリフロップレイズレンジ-0.3400
4ポストフロップポットオッズ+0.4600
5VPIP基のオポーネントリード+1.1800
6ベットサイジングバリエーション+1.71,000
7安定性とアップタイム+1.71,500+

最大のジャンプはDay 2とDay 5:プリフロップハンド選択とオポーネントモデリング。コード1行あたりのコスパが最も良い改善だ。Day 4とDay 6はより小さなゲインだが、うまく複利する。Day 7はエッジを追加しないが、すでに持っているエッジをハンドを蓄積してリーダーボードのポジションに変換する。

これは厳密な研究ではない。6-maxの分散は高く、個人の結果はシーズンのプレイヤープールによって変動するが、方向的にはパターンは一貫している:シンプルなアップグレードの積み重ねが、1週間未満のパートタイム作業で競争力のあるボットになる。

最初の1週間にやるべきでないこと

避けるべき3つの罠。

ハンドエバリュエーターをゼロから書かない。 「本当の」ポーカー作業のように感じるから魅力的だ。しかしシンプルな勝率ヒューリスティックを超える測定可能なエッジを追加しない数日間のプロジェクトだ。どうしてもハンドの強さが必要なら、自分で書く代わりにPokerKitTreys(実戦で鍛えられたエバリュエーター付きのPythonライブラリ)を使う。サーバー側でも同じ理由でPokerKitを使っている。

CFRやGTOソルビングの実装を試みない。 Counterfactual Regret MinimizationはPluribusとLibratusの背後にある技術。数千万のトレーニングハンドを要する数ヶ月の研究プロジェクトでもある。最初のボットでは、同じ時間を基本的なヒューリスティックに費やした方が良い結果が得られる。トップ20%を超えたいならCFRは後から追加。

エラーハンドリングなしでDay 1にデプロイしない。 イベントループをtry/exceptで囲み、すべての例外をログし、続行する。単一の不良メッセージでクラッシュするボットは、夜通し蓄積するはずだったテーブルタイムをすべて失う。修正はtry/exceptブロック1つだ。

Day 7の後は?

動く競争力のあるボットができたら、次のアップグレードはインクリメンタルかつ高インパクト:

  • ポジションベースのレンジ:アンダーザガンではタイト、カットオフとボタンではルース
  • マルチストリートプランニング:フロップでベットする前にターンで何をするか考える
  • ブラフ選択:ブラフが信憑性のあるボードを選ぶ(ハイカード、ドローが少ない)
  • バンクロール管理:セッション結果に基づいてバイインを変動させる

これらのそれぞれが、うまく実装されれば0.5-1.5 bb/100をさらに追加する。リーダーボードトップ10フィニッシュへの道は単一のブレークスルーではない。すべての一般的なスポットで測定可能なエッジを持つまで10の小さな改善を積み重ねることだ。完全なプラットフォームドキュメントが次のラウンドに必要なすべてのプロトコル詳細をカバーする。

FAQ

競争力のあるポーカーボットを作るのに実際どのくらいかかる? 最初の競争力のあるボット(リーダーボードトップ25%)には、7日間のパートタイム作業が現実的。トップ10%には2-4週間のイテレーションとチューニングを見込むべき。トップ1%にはCFRスタイルのアルゴリズム、大規模なオポーネントプロファイリング、数ヶ月のリファインメントが必要。

Python以外の言語でこのプランに従える? はい。プロトコルは純粋なWebSocket + JSONなので、WebSocketサポートのある言語なら何でも動く。JavaScript、Go、Rust、Java、C++、websocatを使ったBashまでOpen Pokerでボットを構築するのに使われてきた。Pythonの例は直接翻訳できる。

リーダーボードを上がるには何ハンド必要? リーダーボードに表示されるには最低10ハンド必要。大きく上がるには、シーズンを通じて1,000-2,000ハンドを計画する。ハンド数が多いほど分散が減り、エッジが現れる。

このプランを実行するのにProアカウントが必要? いいえ。このプランのすべては無料アカウントで機能する。Proプラン($5/シーズン、バンドル割引あり)はアナリティクス、カスタム戦略、短いリバイクールダウンを追加するが、戦略の改善自体は無料。完全なリストはPro機能比較を参照。

プランを完了する前にボットがバストしたら? auto-rebuyが対応する。接続後に{"type": "set_auto_rebuy", "enabled": true}を1回送信すれば、ボットがバストした時にサーバーが自動的にリバイする(無料プランでは5分のクールダウン付き)。イベントループは動き続け、クールダウン満了と同時に次のハンドが始まる。


7日間の集中した作業で、ポーカーボットをゼロからOpen Pokerリーダーボードのコンテンダーに導ける。プラットフォームはこのイテレーション速度のために設計されている。ボットを登録し、デイリープランに従えば、来週にはランクインしたエントリーができる。

続きを読む