koba::blog

小林聡: プログラマです

麻雀の打牌選択アルゴリズム(9)

麻雀の打牌選択アルゴリズム(4) - koba::blog から 麻雀の副露判断アルゴリズム(5) - koba::blog まで打牌選択と副露判断に打点を考慮するよう改良してきたが、今回で最終回。一色手を狙わせてみる。

前回のアルゴリズムで1000半荘対局させると一色手(混一色清一色)の出現率が 1.5% なのであるが、天鳳鳳凰卓の一色手出現率は 4.8% であり、かなり差がある。一色手を狙う場合は序盤に孤立字牌を残す打ち方をするべきなので、麻雀の打牌選択アルゴリズム(3) - koba::blog で導入した孤立牌の評価値に手を加えてみる。

アルゴリズム

孤立牌の評価値は、搭子になり得る牌の枚数を基本とし、ドラを2倍の価値に評価している。まだ1枚も牌が見えていない初期状態(配牌前)であれば以下の値となる*1

m0
42
m1
12
m2
16
m3
21
m4
21
m5
21
m6
21
m7
21
m8
16
m9
12
p0
42
p1
12
p2
16
p3
21
p4
21
p5
21
p6
21
p7
21
p8
16
p9
12
s0
42
s1
12
s2
16
s3
21
s4
21
s5
21
s6
21
s7
21
s8
16
s9
12
z1
16
z2
4
z3
4
z4
4
z5
8
z6
8
z7
8

これを踏まえ、

  • 一色手を狙う場合、染め色の孤立牌の評価値は2倍とする(染め色の一九牌の評価値が、非染め色の中張牌の評価値以上となる)
  • 一色手を狙う場合、字牌の孤立牌の評価値は4倍とする(オタ風の評価値が非染め色の一九牌の評価値以上となる)

とし、一色手を狙う条件は

  • ある色の枚数と字牌の枚数の和が10枚以上

とした。

また同じ要領で四喜和と大三元も狙わせてみた。

  • 風牌が9枚以上ある場合は、風牌の評価値を8倍とする
  • 三元牌が6枚以上ある場合は、三元牌の評価値を8倍とする

ただし孤立牌の評価値は、3向聴以前でしかも待ち枚数が同数の牌同士の比較にしか使われないため、この変更が有効となる局面はかなり限定的ではある。

プログラム実装

メソッド select_dapai() を以下に修正した。

Majiang.Player.prototype.select_dapai = function() {

    /* ………… */

    /* 手牌中の各色・字牌および風牌、三元牌の枚数 */
    var n_suit, n_sifeng, n_sanyuan;
    
    /* 枚数を数え評価値を変更する内部関数を追加 */
    function paijia(p) {

        if (! n_suit) {         // まだ枚数を数えていない場合

            /* 打牌可能な手牌中の各色および字牌の枚数を数える */
            n_suit = { m: 0, p: 0, s: 0, z: 0 };
            for (var s of ['m','p','s','z']) {
                var bingpai = self._shoupai._bingpai[s];
                for (var n = 1; n < bingpai.length; n++) {
                    n_suit[s] += bingpai[n];
                }
            }

            /* 打牌可能な手牌中の風牌・三元牌の枚数を数える */
            var bingpai = self._shoupai._bingpai.z;
            n_sifeng  = bingpai[1] + bingpai[2] + bingpai[3] + bingpai[4];
            n_sanyuan = bingpai[5] + bingpai[6] + bingpai[7];

            /* 副露牌中の各色・字牌および風牌、三元牌の枚数を数える */
            for (var m of self._shoupai._fulou) {
                n_suit[m[0]] += 3;
                if (m.match(/^z[1234]/)) n_sifeng  += 3;
                if (m.match(/^z[567]/))  n_sanyuan += 3;
            }
        }
        return self._suanpai.paijia(p)      // 元々の孤立牌の評価値
                * ( p.match(/^z[1234]/) && n_sifeng  >= 9   ? 8 // 四喜和狙い
                  : p.match(/^[567]/)   && n_sanyuan >= 6   ? 8 // 大三元狙い
                  : (p[0] == 'z'
                     && Math.max(n_suit.m, n_suit.p, n_suit.s)
                                  + n_suit.z >= 10)         ? 4 // 一色手狙い
                  : (n_suit[p[0]] + n_suit.z >= 10)         ? 2 // 一色手狙い 
                  :                                           1 );
    }
    
    /* ………… */

    for (var p of this.get_dapai()) {

        /* ………… */

        var x = 1 - paijia(p)/100       // 変更した評価値を使う
              + this.eval_shoupai(new_shoupai, paishu);

        /* ………… */
    }

    /* ………… */
 
    for (var p of backtrack) {

        /* ………… */

        var x = 1 - paijia(p)/100       // 変更した評価値を使う
              + this.eval_backtrack(new_shoupai, paishu, tmp_max, p);

        /* ………… */
    }
 
    /* ………… */
}

対戦結果

今回修正した思考ルーチン(一色手)と ver.0.8.8 の思考ルーチンの対戦結果は以下の通り。

ver.0.8.8副露判断 一色手 ver.0.8.8副露判断 一色手
対戦数 1,000 1,000 1,000 総局数 10,674 10,606 10,608
1位率 .244 .276 .293 和了 .199 .205 .215
2位率 .244 .259 .261 放銃率 .118 .118 .121
3位率 .249 .224 .212 立直率 .248 .229 .234
4位率 .263 .241 .234 副露率 .269 .322 .331
平均順位 2.53 2.43 2.39 平均打点 5,290 5,601 5,762

和了ver.0.8.8副露判断 一色手 和了ver.0.8.8副露判断 一色手
ドラ 49.13% 56.12% 56.15% 断幺九 22.32% 21.25% 21.02%
赤ドラ 45.07% 47.56% 46.82% 一盃口 2.74% 3.91% 2.85%
裏ドラ 23.22% 19.46% 23.26% 三色同順 1.56% 3.96% 4.64%
立直 52.52% 50.37% 50.07% 一気通貫 0.24% 1.66% 1.53%
一発 10.43% 9.94% 9.64% 七対子 3.96% 3.50% 3.90%
門前清自摸 26.52% 26.49% 24.31% 対々和 0.14% 0.51% 0.70%
翻牌 36.67% 37.81% 39.95% 混一色 0.80% 1.38% 2.58%
平和 13.50% 14.26% 15.20% 清一色 0.00% 0.18% 0.18%

一色手が 1.5% → 2.7% と上昇したが、残念ながら四喜和、大三元での和了はなかった。成績も上昇しているようなので、本アルゴリズムを ver.0.9 に採用することにする。

*1:東場の東家の場合