| 私はパターンプレイを助けるための9ボールプレイヤーを作っています。次のボールを作る方法はいくつもあり、場合によっては複数の明らかなポケットに入れられることもあります。どれを選ぶべきかは、そのショットを入れる確率だけでなく、次のショットに有利な位置にボールを残せるか、そしてその後のポジション取りもしやすいかに依存します。そこで、以下のコンポーネントを作りました:
真値(ground truth):pooltool プールの物理はよくモデル化されていますが高価です。私は pooltool の Python ライブラリを使っています。これは精度の高いボール‐クッション‐ポケット‐フェルト間の相互作用を備えた、堅実なオープンソースのビリヤードシミュレータです。典型的なショット評価で出てくる 1〜3 個のオブジェクトボール配置に対して、1 ショットのエンドツーエンドのシミュレーションは 1CPUスレッドで約 5〜15ms かかります。フルラック(オブジェクトボールが9個)では、追跡するペアごとの衝突が増えるため、約 20〜50ms に跳ね上がります。 速そうに聞こえますが、計算するとそうでもありません。各レイアウトで 6 ポケットすべてに向けた候補ショットを作りたいのですが、各ポケットには探索すべき 5 次元のパラメータ空間があります:速度、狙い角、キュースティックの高さ(elevation)、サイドスピン、フォロー/ドロー。 素朴なグリッドスイープでも、例えば各次元を 10 ステップで粗く区切るだけで、100K(組み合わせ数)×10ms = 1 ポケットあたり約 17 分、1 回の意思決定ごとにかかります。CMA-ES のような反復型オプティマイザなら 1 ポケットあたり約 500〜1000 回のシミュレーションにまで下がりますが、それでも 1 ポケットあたり 5〜10 秒、1 レイアウトあたり 30〜60 秒です。価値ネットワーク(value network)を何百万もの意思決定で学習するとなると、計算資源の投入は数か月規模になります。 候補の高速評価 ショット選択は、「すべての可能なショットをシミュレーションしなくても入るのか」を知る必要があります。ただし、現時点ではテーブルの最終位置がどうなるかは必要ありません。 そこで私は、ショットを「オブジェクトボールに何をさせる必要があるか」と「それを実現するためにキューボールをどう打つか」に分けて捉えることで問題に取り組みました。まずショット作成における最初のコンポーネントが 次に これは改善でしたが、離散化のせいで穴(抜け)が残ります。そこで、連続空間への汎化のための この仕組みの良いところは、ショットインデックスを使ってショットの十分良い初期パラメータセットを得たうえで、さまざまなパラメータに小さな摂動を加え、GPU上で throw model によってそれらをバッチ評価できる点です。私のセットアップでの速度向上は、物理エンジンを通してそれらすべてのショットをシミュレートする場合と比べて約 10000 倍で、自己対戦用の十分なデータを生成するうえで大きな差になります。1000 個の候補ショットのバッチは、評価に 1 ms かかります。平均 10 ms の 1000 回のシミュレーションと比べてください。 次に、速度、スピン、ドロー周りのバケツ(bucketing)によって、意図したポケットの acceptance window 内に入ると予測されるすべてのショットをクラスタリングします。そして各クラスタから代表となるショットを選び、物理エンジンでノイズ付きシミュレーション(ショットに実行ノイズを加える)を用いて評価します。確実に実行できない「100万分の1」のようなショットを見つけたくありません。最後に、 候補を見つけた後は依然として物理シミュレーションを行うので、エンドツーエンドの速度向上はおよそ 50〜100 倍でした。 ショット選択の可視化 より具体化するために、キューボールがテーブル中央、8 ボールが左上方向、9 ボールが下レールにある 8〜9 ボール配置を用意しました。色は 9 ボールの位置に基づく p(win) を表しています(9 ボールはショット中に動かさないものとしてあります)。この投稿では、選択された 10 ショットを 20 回ずつシミュレートしました。結果として、10/20 のうち 6 ショットは 20/20 全て成功、3 ショットは 19/20、残り 1 ショットは 15/20 成功でした。キューボールの軌道の色は、これら 20 回のショットでの成功率を反映しています。各 10 ショットについて 20 回のうち 1 つだけノイズありシミュレーションをプロットしました。残りはかなり近いはずです。 9 ボールの周りの黒い領域は、9 ボールから 1 ボール未満の距離内にあり、キューボールが 9 ボールの空間に侵入してしまうため無効な位置を表しています。 この投稿では直接ショットのみを話しましたが、p(win) のヒートマッププロットに織り込まれている型(テンプレート)のバンクショット、キックショット、キャロム、コンビネーションショットもあります。もちろん、9 ボールのみのケースではキャロムやコンビネーションはここには適用されません。 次は何をする? 私はカリキュラム学習(curriculum learning)に取り組んでいます。9 ボールだけを使った p(win) モデルは素直です。9 ボールをポケットに入れれば勝ち(ただしスクラッチしない場合)。スクラッチしたら負けです。なぜなら、まともな相手なら手球付きで 9 ボールを入れてくるからです。外した場合の報酬は、結果として得られた状態からの (1-p(win)) です。私はフルのショット選択オプションを使って約 100k ショットをシミュレーションし、p(win) モデルに対して 4 倍の対称性を使用しました。モデルが更新されるたびに 100% 成功しないショットについてはショット選択を作り直します。モデルの更新によって、異なるショット選択/セーフティ位置につながる可能性があるためです。 単一ボールのシナリオが「解けた」ら、2 ボールのシナリオに進みます。オンボールを作ると、モデルから価値を参照する「解けた状態」になります。ミスした場合は、モデルのイテレーション間で再評価されます。私はカリキュラムを進め、 ボールのシナリオをマスターし、そこから最大 9 ボールまでの n ボール配置をすべてマスターしていきます。 うまくいかなかったことはいろいろ試しました。たとえば、(ミラーリングに基づく)ゴーストポケットの角度を特徴量として与えたとき(物理に基づいたML)、バンクモデルがかなり改善しました。興味があれば、そのどれについてでも詳細を共有できます。 [リンク] [コメント] |
9ボールのAIプレイヤーを作る:直線カットショットの候補生成
Reddit r/MachineLearning / 2026/5/5
💬 オピニオンDeveloper Stack & InfrastructureTools & Practical UsageModels & Research
要点
- 著者は9ボールのAIプレイヤーを、パターンプレイ支援のために構築しており、テーブルレイアウトから勝率 p(win) を推定するトランスフォーマーベースのモデルを用いている。
- システムは直線カットだけでなく、バンク、キック、キャロム、コンビネーションショット、さらにセーフティも含めて候補ショットを生成し、各候補の結果状態に p(win) モデルを適用して最適なショットを選定する。
- プール物理のシミュレーションにはコストがかかるため(オープンソースの PoolTool を使用)、候補評価の効率化が重要な課題として述べられている。
- 最適化の要点として、(オブジェクトボール位置、ポケット、速度) ごとに事前計算した「acceptance window(受け入れ範囲)」を使い、成功させるために成立するOB(オブジェクトボール)の発射角の範囲を絞り込む。
- ナイーブなグリッド探索では計算が膨大になること、また CMA-ES などの反復最適化でも学習規模では十分に速くならないため、候補を高速にふるい分ける工夫が必要だと説明している。




