(最終更新:2026年5月2日)
約1年前、暇で仕方がなかった時期がありました。せっかくなら何か面白いものを作りたいと思い、趣味の延長としてポーカーAIの開発を始めました。当初は強化学習もC++も初心者でしたが、チャッピー(当時は4o)と対話しつつ、試行錯誤しながら少しずつ形にしていく過程が楽しかったです。
しかし、ありがたいことに仕事やプライベートで変化があり、開発の熱意と時間が少しずつ減ってきました。結果として、このプロジェクトは半年ほど手つかずのままです。このまま完全に記憶の彼方へ消えてしまうのは少しもったいないので、せっかくなら他人が動かせる状態に整えて公開し、やりたいことリストの断捨離の一環として一区切りつけたいと思います。
個人の趣味で作ったので説明は雑ですが、一応、下記の順番通りに進めれば誰でも動かせるはずです。GTOの裏側の仕組みやLinuxに興味のある方は触ってみてください。僕が書いた強化学習のロジックに関する質問はお受けしますが、初期設定まわりの初歩的な質問はチャッピーにでも聞いてください。
必要なもの
開発環境はLinux(Ubuntu Server)で、Windowsから遠隔操作しています。Windowsでも、WSL(Windows Subsystem for Linux)を使えば直接実行できると思います。ただ、ポーカーの強化学習はメモリを大量に使うため、メモリオーバーヘッドの少ないUbuntu Server上で起動し、WindowsからSSHで接続する方がよいと思います。
- サーバーとなるPC:
自分はミニPCを使用しています。Ubuntu ServerというOSをインストールするので、Windows等が入っているのであれば別途SSDを買った方がいいです(なのでSSDを差し替えられないノートパソコンはUbuntu Serverのインストール非推奨です)。メモリは32GB以上は欲しいです。メモリが32GB未満のPCではテストしていないので、動作は保証できません。SSH接続できるようになるまでモニターが必要です。 - 普段使いのノートパソコン:
OSとスペックは何でもいいです。この記事はWindowsでの手順になっていますが、Macでも動作確認できてます。
プログラム構成
強化学習を触り始めたころは、Google DeepMindが提供しているOpenSpielというライブラリを活用して、基礎を学んでいました。OpenSpielは、ポーカーを含むさまざまなゲームに対応しており、強化学習のロジックも多数用意されています。そのため、学習用の教材や実験環境としては非常に便利です。
一方で、今回のようにポーカーAIの開発に用途を絞る場合には、やや扱いにくい面もあります。OpenSpielは汎用的に設計されている分、不要な依存関係が多く、細かくコードを改修したり、処理速度を追求したりするには限界がありました。
特にポーカーの強化学習では、データ量と読み書き速度がボトルネックになりやすいため、本プロジェクトではメインとなるコードを自分で書き、必要最低限の構成で動作するように新たに構築しています。これにより、OpenSpielを使う場合と比べて、処理速度を大幅に向上させることができました。
また、一口に強化学習と言っても、さまざまな学習ロジックがあります。ポーカーに適した手法として CFR(Counterfactual Regret Minimization) がよく知られていますが、CFRの中にもStandard CFR、Linear CFR、Discounted CFR、Deep CFR、External Sampling、Outcome Samplingなど、多くのバリエーションがあります。
実際の開発では、これらの手法をそのまま使えるわけではなく、学習段階に応じて切り替えたり、複数の考え方を組み合わせたりする必要があります。名前の付いていない細かな工夫まで含めると、かなり個人の色が出やすい分野だと思います。GTO Wizard、PioSolver、MonkerSolverのようなメジャーなポーカーツールはもちろん、Pluribusなど参考になりそうな論文についても、コードが公開されているわけではありません。そのため、OpenSpielのような実装例を参考にしつつも、最終的には自分で試行錯誤しながら設計していく必要がありました。
- src/
- 強化学習の主要な学習ロジックは自分で実装
- External Sampling MCCFR に QRE と DCFR の考え方を組み合わせています
- external/acpc_server/
- NLHのディーラー役(ゲーム進行、アクション処理、状態管理)はオープンソースのAnnual Computer Poker Competitionを利用
- ここからコードを取得しました
- external/OMPEval/
- ショーダウン時のハンド評価として使用
- Git submodule として追加
- external/oneTBB/
- RAM 上でステート情報を管理する際に、並列アクセス可能なデータ構造として使用
- Git submodule として追加
- RocksDB
- SSDで学習後のステート情報を保持するために使用
- Ubuntu の apt 経由でインストール
- FLTK
- GUIの作成に使用
- Ubuntu の apt 経由でインストール
どこまで開発したか
ポーカーAIの仕組みには、大きく分けて「事前学習」と「リアルタイム学習」の2つがあります。
事前学習では、あらかじめ数十時間にわたるゲームシミュレーションを行い、探索したステート、一般的に「ノード」とも呼ばれる局面における最適なアクション情報を蓄積していきます。ただし、以前の記事で説明している通り、フロップ以降の局面や、到達頻度の低いレアなステートについては、事前学習では十分にカバーできない場合が多くあります。そのため、実戦レベルで利用するには、局面に応じてリアルタイムに探索を行う仕組みが重要になります。
本記事では、事前学習のコードと、その学習結果を確認するためのGUIを公開しています。リアルタイム学習については、内部データを観察しながら仮説を検証できる段階までは開発しましたが、他の人が直感的に扱えるGUIとして整理できていないため、今回は公開対象には含めていません。
そのため、公開したコードでできることは、アクションやベットサイズのパラメータを変更し、プリフロップの戦略を観察するくらいです。プログラミングにある程度自信のある人は、相手のアクション分布に「フラッシュドローを好む人」や「アグレッションの高い人」のような傾向を反映させ、CFRの戦略がどのように変化するか見てみると面白いです。
実用的なAIを目指したい人は、「リアルタイム学習」にも挑戦してみてください。自分は、ベイズ推定を使って相手のアクションからレンジを絞り込み、探索するノードを限定していく仕組みにしました。本来であれば、フロップ以降はハンドとボードの抽象化を解除したほうが、ボードの細かなニュアンスを捉えやすくなりますが、自分はそこまで開発できませんでした。
開発難易度はかなり高いです。ポーカーAIの教師データが不足しているせいか、「どのようなロジックを組めばいいか」について生成AIは嘘をつくことがあります。また、動くパーツが多すぎることに加え、コード変更の検証には学習データを蓄積してから観察する必要があります。
初期設定
サーバーの初期設定
- サーバーとなるPCにUbuntu Serverをインストールする。
- Ubuntu Serverにログインできるようになったらアップデートを適応する。
sudo apt update && sudo apt upgrade -y - 任意のIPアドレスに固定する。
- SSHをインストールする。
sudo apt install -y openssh-server - サーバーを再起動する。
sudo reboot - ノートパソコンからサーバーにSSH接続できることを確認する。WindowsであればPowerShell、Macであればターミナルを使います。今後はSSH接続のみで作業できるので、サーバーのモニターはもう不要です。
ssh ユーザー名@固定したIPアドレス - サーバーにRocksDB、FLTK、X11をインストールする。
sudo apt install -y librocksdb-dev rocksdb-tools lld libfltk1.3-dev fluid xauth x11-apps - サーバーにビルド関連をインストールする。
sudo apt install -y cmake build-essential - サーバーのスワップ処理を無効化する。無効化しないとSSDを仮想メモリとして使ってしまい、処理速度が極端に落ちます。
- サーバーのリソース制限を引き上げる。RocksDBのデータベースは複数のファイルとして保存されるため、長時間学習を実行していると、同時に開けるファイル数の上限に達することがあります。
- サーバーへのSSH接続を一旦切る。
exit
GUIの初期設定
- ノートパソコンにVcXsrvをインストールする。(Macの場合はxquartz)
- インストールされたxlaunch.exeを起動する。設定は基本標準のままでいいですが、初回はDisable access controlにチェックを入れて起動する。タスクバーに起動されたことを確認する。
- PowerShellで環境変数を設定する。
$env:DISPLAY="localhost:0.0" - -Yオプションをつけて、もう一度サーバーにSSH接続する。
ssh -Y ユーザー名@固定したIPアドレス - SSH接続で下記コマンドを実行して、ノートパソコン側に時計の画面が出てきたらGUIの初期設定は完了してます。
xclock &
ソースコード取得&ビルド
- リポジトリをクローンする。
git clone git@github.com:guregu321/poker-agent.git cd acpcでディレクトリを移動し、git submodule update --init --recursiveでサブブモジュールをダウンロードする。- makeでビルド
パラメータ設定
プレイヤー人数、スタックサイズ、コールドコールの有無、ベットサイズ、QREの有無など、各種ファイルで調整できます。設定を変更したら、make clean && makeで再度ビルドしてください。
- src/holdem.nolimit.6p.game
- プレイヤー人数やスタックサイズなど、ゲームのルールを変更できます。
- ハンドやボードの枚数を変え、NLH以外のルールで学習することも可能です。
- src/acpc_settings.hpp
- ベットサイズやオールインの閾値など、取り得るアクションの抽象化設定を変更できます。
- src/training_settings.hpp
- QREの有無やRAMの上限など、学習にまつわる設定を変更できます。
- src/es_mccfr.cpp
- パラメータではありませんが、相手のアクションに特定の傾向やバイアスを持たせたい場合は、ここのサンプリング方法を変更します。
強化学習の実行方法
学習はtraining_loop.shで走らせます。
- 学習用のセッションを開始する。(ノートパソコンの電源を切っても裏で学習が回るようにするため)
tmux new -s training - 学習を開始する。最初の数字が総学習回数です。次の数字が、累積リグレットと累積戦略を減衰させるインターバルです。
chmod +x training_loop.sh./training_loop.sh 1000000000 10000000 db 1 off - 学習用のセッションを出る場合はCtrl+Bを押して、Dを押す。
- CPUやメモリの使用状況はhtopで確認できます。
- 学習用のセッションに戻る場合は
tmux attach -t training - 学習を中断する場合はCtrl+C
- 学習を再開する場合は再開箇所を指定してください。
./training_loop.sh 100000000 10000000 db 5 off
4スレッドしかないRyzen 4300UとDDR4 RAMのポンコツPCでも、初期段階のメモリ上で完結する学習であれば、1秒あたり3万2千回のゲームシミュレーションを実行できています。ちなみに、このゲームシミュレーションでは、自分側はすべてのノードで全アクション(Fold、Call、Raise)を取り、対戦相手側は各ノードで最善のアクションを1つだけ取るようにしています。そのため、実際のゲームよりも探索ノード数は多くなっています。

サンプル画像では、1セット100万回のシミュレーション後に、累積リグレットと累積戦略へ50%の減衰をかけたうえで、2回目のセットに入っています。2回目のセットでは、SSDの学習データを参照しながら、メモリに新しいデータを蓄積していきます。遅いタイプの2.5インチSSDであっても、1秒あたり1万7千回のゲームシミュレーションができてます。M.2 SSDであればさらに高速ですが、その分、発熱は大きくなります。減衰はセットを重ねるごとに、徐々に弱まるようにしています。

学習済みデータの確認
学習済みデータはdata/に溜まっていきます。GUIで可視化する場合は、./bin/range_viewerを実行します。累積リグレットと累積戦略を減衰させるタイミングで、DBが新しく作成されます。
画面右上からDBを選択できるようにしているため、学習の過程で戦略がどのように変化したかを確認できます。下の画像では、左が100万回学習後、右が1000万回学習後のUTGのオープンレンジです。プリフロップのレンジを十分に収束させるには、何十億回もの学習が必要になります。


技術的な補足説明
基本はコードを見ていただきたいですが、実装したロジックの説明や技術的に工夫した点も気が向いたら書こうと思います。特に、GTOを理解するためには、ポーカーの「抽象化」は外せない論点です。