TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

麻雀AIを深層強化学習で作る その6(初期局面生成)

以前に考察した通り、強化学習でゼロから麻雀AIを学習する場合、偶然和了した際の報酬のみで役を学習するのは困難と考える。
そこで、ランダムに選択した役のN向聴の状態からゲームを開始して、補助タスクとして和了した役も学習させることにする。

今回は、N向聴の状態の初期局面を生成する処理を実装した。

N向聴の状態の生成処理

N向聴の状態は、以下の手順で生成する。

  1. ランダムに役を選択する
  2. 選択した役の手牌をランダムに生成する
  3. 1枚牌山に戻して、聴牌にする
  4. Nをβ分布からサンプリングする
  5. 手牌からN枚を牌山と交換する
  6. 以上を4プレイヤー分繰り返す
  7. 河の枚数を平均9枚で聴牌すると仮定して、向聴数から捨て牌の基本枚数をβ分布からサンプリングする
  8. 副露と河の枚数が矛盾しないように調整する
  9. 聴牌の待ち牌を含まないように河を生成する
  10. 聴牌の場合確率的に立直状態にする
  11. 牌山を生成する
  12. 副露に槓がある場合、槓自摸、開槓を処理する

役の手牌をランダムに生成する処理

役の手牌をランダムに生成する処理は、前回の記事に記載した。

β分布からサンプリング

プレイヤーごとに向聴数をばらけさせるため、向聴数はβ分布からサンプリングする。
β分布は以下のような分布である。

平均値2のベータ分布

C++で実装する場合、標準ライブラリではβ分布を直接扱えないため、ガンマ分布から生成する。

    std::gamma_distribution<double> gamma1{ avr, 1.0 };
    std::gamma_distribution<double> gamma2{ max - avr, 1.0 };

    const double x = gamma1(mt);
    const double y = gamma2(mt);

    const double z = x / (x + y);
    beta = (int)(z * max);

手牌を牌山と交換する処理

一様ランダムで選択したN枚の手牌を牌山と交換する。
その際、副露している牌も対象とする。
槓の場合は、4枚目を牌山に戻す。

実行例

上記の初期局面生成処理をcmajiangに実装した。
実行例は以下の通り。

from cmajiang import *

game = random_game_state(3)
[display(shoupai) for shoupai in game.shoupai]


[xiangting(shoupai) for shoupai in game.shoupai]
[2, 2, 3, 1]

向聴数に3を指定しているが、平均は3より小さくなる。
これはN向聴をN枚交換する処理で代替しているため、向聴数が変わらない牌と交換される場合があるためである。

生成された手牌を見ると1枚の字牌が残っていたりする。
実際の人間の対局では、字牌は先に捨てるだろうといった知識は反映されていない。
強化学習の初期段階で役を覚えさせるために使用して、徐々にNを増やしていき、ある程度学習したら通常の対局にするのが良さそうである。

まとめ

N向聴の初期局面を生成する処理を実装した。
準備がだいたいできたので、次は強化学習の実装に着手したい。