Home

打牌選択アルゴリズム(10) 〜 二次有効牌

麻雀の打牌選択アルゴリズム(9) 以来、6年ぶりに打牌選択アルゴリズムを改善。

[ 東場 東家 ]
__s4____
m6m7m7p2p2p3p3p4p6p7p7s3s4 s0

ウザク式牌効率講座「3分何切る」(2023/04/10) からの出題。 電脳麻将: 何切る解答機 の選択は p6、ウザク式牌効率講座の回答も p6


二次有効牌

p6m6 は評価値が同点なので、麻雀の打牌選択アルゴリズム(3) で定義した 牌の評価値*1にしたがい p6 を選択しているのだが、ウザク式効率講座の解説では p0 を引いたときの 手変わり を根拠としている。 具体的には、

となり、打 m7 で評価値が 3075.69 → 5106.60 に上昇する。 このような「シャンテン数は進まないが評価値が向上する牌」のことを 二次有効牌 と呼ぶことにする。

m6 の場合、二次有効牌は

枚数 牌姿 評価値
p2 2 p7m7m7p2p2p2p3p3p4p6p7s3s4s0 4301.39
p3 2 p7m7m7p2p2p3p3p3p4p6p7s3s4s0 3943.75

の2種4枚となり、打 p6 については

枚数 牌姿 評価値
p2 2 m7m6m7p2p2p2p3p3p4p7p7s3s4s0 4992.71
p3 2 m7m6m7p2p2p3p3p3p4p7p7s3s4s0 3943.75
p5 3 m7m6m7p2p2p3p3p4p5p7p7s3s4s0 3639.24
p0 1 m7m6m7p2p2p3p3p4p0p7p7s3s4s0 5106.60

の3種*28枚となる。 これを見れば打 p6 が有利と分かる。

プログラム変更

何切る検討(3) ~ 手変わりの考慮 でも手変わりに触れているが、その際には評価値計算の際に再帰的に二次有効牌を考慮すると計算量が爆発的に増えると考え、打牌選択のアルゴリズムには組み入れなかった。 今回は発想を転換して評価値が同点の場合のみ限定的に二次有効牌による評価値の向上を計算することとする。

まず、二次有効牌により向上した評価値の総和を求めるメソッド eval_replace() を追加する。

    eval_replace(shoupai, paishu, back, min) {

        let n_xiangting = Majiang.Util.xiangting(shoupai);

        function gulipai(bingpai, n) {
            if (3 <= n && bingpai[n-2] > 0) return false;
            if (2 <= n && bingpai[n-1] > 0) return false;
            if (n != 0 && bingpai[n]   > 0) return false;
            if (n <= 8 && bingpai[n+1] > 0) return false;
            if (n <= 9 && bingpai[n+2] > 0) return false;
            return true;
        }

        function replace() {
            let pai = [];
            for (let s of ['m','p','s']) {
                let bingpai = shoupai._bingpai[s];
                for (let n = 1; n < bingpai.length; n++) {
                    if (s+n == back) continue;
                    if (gulipai(bingpai, n)) continue;
                    bingpai[n]++;
                    if (Majiang.Util.xiangting(shoupai) == n_xiangting)
                                                                pai.push(s+n);
                    bingpai[n]--;
                }
            }
            return pai;
        }

        let rv = 0;
        for (let p of add_hongpai(replace())) {
            if (paishu[p] == 0) continue;
            paishu[p]--;
            let ev = this.eval_shoupai(shoupai.clone().zimo(p), paishu, back);
            paishu[p]++;
            if (ev - min > 0.0000001) rv += ev * paishu[p];
        }
        return rv / width[n_xiangting];
    }
  1. ^電脳麻将本」の【南一局 1本場】リーチのAI 参照
  2. ^ p5p0 は同種