概要
以前の投稿でテーブルリバランシングの方法について簡易的に考察したが、やはり実装を始めるとうまく処理できない気がしてきた。本稿では、以下の要素を無視してとりあえず人数合わせができるアルゴリズムを考えよう。次の投稿でこれらの要素も考慮するようにアルゴリズムを考えよう。
- ポジション(BTN, BBなど)の回数を可能な限り同じぐらいの回数になるようにする(現在のポジションに近い位置に移動する)。
- スタックの不均衡を少なめにする。
- 直近で移動したプレイヤーや移動回数の多いプレイヤーについて、移動の優先順位が低くなるようにする。
開始時点
テーブル構成を\(\small n_1,\cdots,n_T, n = \sum_{t=1}^T n_i\)と表す。最少人数制約を\(\small \underline{n}\)、最大人数制約を\(\small \bar{n}\)と表す。開始時点ではプレイヤー間に差はないため、テーブル数を可能な限り少なくして、各テーブル間の人数差が少なくなるように配置すればよいだろう。最大人数制約を満たす最小のテーブル数は\(\small T^{\ast}=[n/\bar{n}]+1\)であるから(\(\small [x]\)はガウスの記号)、あとは人数差が最小になるように各テーブルに順番に1人ずつ配置していけばよい。例えば、\(\small n=100\)で\(\small \bar{n}=9\)ならば、最小のテーブル数は12であり、9人のテーブルが4個、8人のテーブルが8個という構成になる。
各テーブルのゲーム終了時の処理
テーブルのリバランシングはゲーム中は行えないため、ゲーム終了時に移動するプレイヤーやテーブルを解散するかを判定して、移動先のテーブルのゲーム終了時に移動処理を実行する。レイトレジスト(途中参加)や再エントリーもあるため、それらのプレイヤーの配置も同時に行う必要がある。ChatGPTによると実務で良く用いられる方式はGreedy方式と言われる手順であるらしい。
大まかにいうと、以下の三つの手順で構成される。
- 人数が少ないテーブルを解散(テーブルブレイク)
- 人数が多いテーブルから人数が少ないテーブルへプレイヤーを移動(Greedy方式)
- テーブルの増設(プレイヤー数が増えた場合)
対象のテーブルが最少人数制約を\(\small \underline{n}\)を下回った場合テーブルを解散してプレイヤーを待機者リスト(レイトレジストや再エントリーでテーブルが決まっていないプレイヤーのリスト)に移す。テーブルブレイクによって移動するプレイヤーは、新規エントリーのプレイヤー同様にランダムに座席を決めることが一般的であるようである。
Greedy方式の名称になっているアルゴリズムが2.の処理であり、各テーブルの人数で、最大値のテーブルと最小値のテーブルで人数差が二人以上ある場合に、人数が多いテーブルから人数が少ないテーブルへプレイヤーを移動する。この際、座席移動が少ないプレイヤーや移動先の座席とポジションが近いプレイヤーを移動対象にする(移動後もほぼ同じポジションでプレイできることになる)。これにより、全体的にほぼ同じ人数のプレイヤー数でゲームが進行することになる。
注意点として、最少人数制約を\(\small \underline{n}\)と最大人数制約\(\small \bar{n}\)は、大体\(\small \bar{n}\approx \underline{n}/2\)ぐらいに設定すると良いだろう。\(\small \underline{n}\)を小さくしすぎると人数が少ないテーブルが多く存在することになってしまうためである。9maxならば\(\small \underline{n}=5\)(4人以下でテーブルブレイク)、6maxならば、\(\small \underline{n}=4\)(3人以下でテーブルブレイク)ぐらいにすればよいだろう。2つのテーブルを結合できる状態が維持されないようにするためである(9maxならば、4人のテーブルが2つあるより、8人のテーブルに結合した方が良い。)。
当然ながら、残りのプレイヤー数が最少人数制約を下回った場合はテーブルブレイクしないように分岐処理する必要がある。各テーブルは1ゲーム終了時に、テーブルブレイクとGreedy方式により移動判定を行えば、自動的にテーブル間のプレイヤー数のバランスが保たれることになるだろう。
待機者リストの処理
テーブルブレイクやレイトレジストでプレイヤーが増えた場合、新たにテーブルを増設することになる。この際待機者リストで最少人数制約\(\small \underline{n}\)を上回る場合は、とりあえず待機者リストのプレイヤーでテーブルを作成すればよいだろう。ゲームの進行に合わせてGreedy方式で人数の調整が行われることになる。複数のテーブルを作成する場合は開始時点のアルゴリズムで同様に処理することができる。
面倒くさそうなのは、待機者リストのプレイヤー数が最少人数制約\(\small \underline{n}\)を下回る場合である。これは待機させるしかないのであるが、既存のテーブルのゲームが終了するごとに空いている座席にアサインしていけばよいと思うかもしれない。しかし、以下のような問題が生じるケースがある。例として、既存で3つのテーブルが存在しておりそれぞれ、9人、8人、8人が座っているとする(1テーブル当たりの最大人数を9人と仮定する)。このとき、待機者リストに3人のプレイヤーがいる場合、空いている座席に一人ずつアサインしていくと、1人のプレイヤーが余ってしまい、しばらくの間このプレイヤーはゲームに参加できないことになってしまう。
これを避けるためには、必要テーブル数を把握しておかなければならず、上記の例では25人+3人=28人であるから、\(\small [28/9]+1=4\)個のテーブルが必要になる。新たにテーブルを増設してプレイヤーを移動させなければならないということになる。したがって、この状況では待機者リストのプレイヤーでテーブルを増設した後、Greedy方式で人数の多いテーブルからプレイヤーを移動させ、プレイヤー数が最少人数制約\(\small \underline{n}\)を上回った時点でゲームを開始するという手順で処理しなければならない。移動させる人数は”プレイヤー数/テーブル数”までにとどめる必要がある(例では、\(\small 28/4=7\)であり、9人のテーブルが終わった際には2人のプレイヤーを移動する)。必要テーブル数が存在するかどうかを毎回チェックして、処理を分岐する必要がある点について注意が必要だろう。
まとめ
ひとまず、本稿の処理を実装すれば、MTTのテーブルリバランシングに必要な機能は足りると考えられるし、おそらくエムホールデムやポーカーチェイスで用いられているアルゴリズムも似たようなアルゴリズムであると推測される。ただし、ChatGPTによるともう少し洗練されたアプローチもあるようであり、それは数学的な問題(このブログが数学のブログであることを筆者は忘れつつあるけど・・・)になっているということで、これは次回に紹介しようと思う。
