tomohxxの日記

麻雀プログラミング

天鳳牌譜解析(6) 期待最終順位

はじめに

今回は天鳳の牌譜を使用して期待最終順位を計算します。期待最終順位とは、一局の開始時点の得点などを基に予測されるゲーム終了時点の順位のことです。似たような概念が複数の麻雀AIで採用されています。

麻雀AI 開発者 関連する論文
爆打 水上氏 https://ipsj.ixsq.nii.ac.jp/ej/index.php?active_action=repository_view_main_item_detail&page_id=13&block_id=8&item_id=145804&item_no=1
Akochan 栗田氏 https://ipsj.ixsq.nii.ac.jp/ej/index.php?active_action=repository_view_main_item_detail&page_id=13&block_id=8&item_id=183834&item_no=1
Suphx Microsoft Research Asia (MSRA) [2003.13590] Suphx: Mastering Mahjong with Deep Reinforcement Learning

期待最終順位(またはその類似概念)は大局的な戦略を決定する材料に用いられます。

方法

上の表で挙げたもののうち栗田氏の方法が最もシンプルなので、この方法を模倣することにします。この方法では各プレイヤの得点のみを特徴量として期待最終順位を予測します。予測する対象は東一局での東家、南家、西家、北家がそれぞれ何位になるかを表す順列です。プレイヤが4人なので4! = 24通りのどの順位分布になるかの確率を計算します。詳しい原理については氏のブログでの解説がとてもわかりやすいのでぜひご覧になってください。

麻雀順位予想計算機概要

実装は手軽さを重視してPythonのscikit-learnを使用することにします。このため目的関数とフィッティングのためのアルゴリズムが氏と異なることに注意してください。また、教師データは2018年の天鳳鳳凰卓東風戦とします。これは自作麻雀AIに組み込んだ際の評価を東風戦で行うことを想定しているためです。各種パラメータなどはソースコードGitHubで公開するので、そちらでご確認ください。

GitHub - tomohxx/expected-final-ranking

結果と考察

上記のプログラムの実行結果をここで示します。まずは訓練の様子です。

$ python3 train_score.py
0 Accuracy (train) : 0.07244196044711952
0 Accuracy (test)  : 0.07184502539025767
1 Accuracy (train) : 0.14373525053509686
1 Accuracy (test)  : 0.14451274170828532
2 Accuracy (train) : 0.23610527200874465
2 Accuracy (test)  : 0.2376405963902694
3 Accuracy (train) : 0.3883307830483667
3 Accuracy (test)  : 0.3838869916175101

各行の一番左の数字が何局目かを表していて0から順に東一局から東四局を表しています。そしてその右側に訓練データまたはテストデータに対する正答率を表示しています。これらの数字から以下のことが読み取れます。

  • ランダムに順位を予測した場合の正答率は1/24 = 約4%であることから、すべての局で少なからず学習が行われていること。
  • 教師データとテストデータで正答率に大きな差がないことから、過学習は起こっていないこと。
  • 局が進むごとに正答率が上昇していることは経験的に納得できること。

次に学習済モデルを用いて期待最終順位を求めた結果を示します。

$ python3
Python 3.6.9 (default, Apr 18 2020, 01:56:04)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> import pickle
>>> from train_score import Ranking
>>>
>>> f = np.array([25000, 25000, 33000, 17000])
>>> p = pickle.load(open('score_predictor.bin', 'rb'))
>>> r = Ranking(p)
>>>
>>> r.predict(1, f)
array([[0.19807261, 0.29964195, 0.3143146 , 0.18797084],
       [0.23031479, 0.27038114, 0.30361783, 0.19568625],
       [0.47933657, 0.31991855, 0.15472499, 0.04601989],
       [0.09227603, 0.11005836, 0.22734259, 0.57032302]])

東一局で西家が北家から満貫を出和了ったと仮定して、東二局開始時点の期待最終順位を求めました。一番下のarrayの中の4 × 4行列が各プレイヤが1位から4位をとる確率を表しています。これらの確率が経験に沿うものなのか確認してみましょう。東一局での東家をAさん、南家をBさん、西家をCさん、北家をDさんとします。満貫を和了ったCさんの期待最終順位は

[0.47933657, 0.31991855, 0.15472499, 0.04601989]

です。1位の確率が約48%、2位の確率が約32%、3位の確率が約15%、4位の確率が約5%となっています。各値の大小関係は問題なさそうですね。次は満貫を振り込んだDさんの期待最終順位です。

[0.09227603, 0.11005836, 0.22734259, 0.57032302]

1位の確率が約9%、2位の確率が約11%、3位の確率が約23%、4位の確率が約57%となっています。こちらも各値の大小関係は問題なさそうです。次はAさんの期待最終順位です。

[0.19807261, 0.29964195, 0.3143146 , 0.18797084]

1位の確率が約20%、2位の確率が約30%、3位の確率が約31%、4位の確率が約19%となっています。起家で現在2位なので2位の確率が最も大きくなると思ったのですが、3位の確率が最も大きいようです。現在の親のBさんにまくられる可能性を考慮した上での確率かと思います。最後にBさんの期待最終順位です。

[0.23031479, 0.27038114, 0.30361783, 0.19568625]

1位の確率が約23%、2位の確率が約27%、3位の確率が約30%、4位の確率が約20%となっています。現在の親なので1位の確率がAさんよりも大きくなっているように思います。

おわりに

期待最終順位を計算するプログラムを作成しました。今後時間をかけてこの有用性を確かめていく予定です。