麻雀ルールのカスタマイズ(4) ~ 和了役と点計算

電脳麻将 ver.2.0 で開発中の新機能「パラメータによるルールのカスタマイズ」。麻雀ルールのカスタマイズ(3) ~ 赤牌とドラ - koba::blog に続いて今回の話題は 和了役点計算。これらに関わるパラメータは以下の通り。

  • クイタンあり
  • 一発あり
  • ダブル役満あり
  • 役満の複合あり
  • 数え役満あり
  • 役満パオあり
  • 切り上げ満貫あり

パラメータの意味

クイタンあり
true のとき、副露しての断幺九を認める。デフォルト値は true。
一発あり
true のとき、リーチ後一巡以内の和了を一発とし、1翻付加する。デフォルト値は true。
ダブル役満あり
true のとき、国士無双十三面、四暗刻単騎、大四喜、純正九蓮宝燈 の得点を役満の2倍とする。デフォルト値は true。
役満の複合あり
false のとき、ダブル役満あり がtrue であっても、和了役に複数の役満があっても単一の役満と同じ得点とする*1。デフォルト値は true。
数え役満あり
false のとき、13翻以上を役満とせず、11翻以上は全て三倍満となる。デフォルト値は true。
役満パオあり
本項目が true で 役満の複合あり も true の場合、パオに関連する役満だけ責任払いが発生する。本項目が true で 役満の複合あり が false の場合、全体に責任払いが発生する。
切り上げ満貫あり
true のとき、30符4翻などを満貫に切り上げる。デフォルト値は false。

和了役判定

クイタン

断幺九の役判定関数 duanyaojiu()クイタンあり が false の場合はメンゼンを役成立条件とする。

    function duanyaojiu() {
        if (hudi.n_yaojiu > 0)  return [];
        if (rule['クイタンあり'] || hudi.menqian)   // クイタンありか門前の場合
                                return [{ name: '断幺九', fanshu: 1 }];
        return [];
    }

一発

状況役はクラス Majiang.Game で判定する。メソッド dapai() でリーチ宣言の際に 一発あり が true なら「一発フラグ」(_yifa)を立てる。

    dapai(dapai) {

        /* …… */

        if (dapai.substr(-1) == '*') {
            this._lizhi[model.lunban] = this._diyizimo ? 2 : 1;
            this._yifa[model.lunban]  = this._rule['一発あり'];
        }
        /* …… */
    }

ダブル役満

ダブル役満あり が false の場合、get_hupai() での役判定の後の処理で役ごとの「役満数」(fanshu)を一律1に変更する。これにより役名は「四暗刻単騎」のようにダブル役満特有の名称のまま点数は通常の役満と同じになる。

function get_hupai(mianzi, hudi, pre_hupai, post_hupai, rule) {

    /* …… */

    for (let hupai of damanguan) {
        if (! rule['ダブル役満あり']) hupai.fanshu = '*';
        /* …… */
    }
    /* …… */
}

点計算

役満パオ

役満パオあり が false の場合、get_hupai() の役判定の後の処理で「パオ対象者」(baojia)を一律削除する。

function get_hupai(mianzi, hudi, pre_hupai, post_hupai, rule) {

    /* …… */

    for (let hupai of damanguan) {
        /* …… */
        if (! rule['役満パオあり']) delete hupai.baojia;
    }
    /* …… */
}

役満の複合

役満の複合あり が false の場合、get_defen() の基本点計算の際に全体の「役満数」を一律1に変更する。これにより役満パオの責任範囲も自動的に役満1つ分となる。

function get_defen(fu, hupai, rongpai, param) {

    /* …… */

    if (hupai[0].fanshu[0] == '*') {    // 役満の場合
        fu = undefined;
        /* 基本点を算出する */
        damanguan = ! param.rule['役満の複合あり'] ? 1  // 役満数を1にする
                  : hupai.map(h => h.fanshu.length).reduce((x, y) => x + y);
        base      = 8000 * damanguan;   // 基本点 = 8000 × 役満数

        let h = hupai.find(h => h.baojia);
        if (h) {                        // 役満パオがある場合
            /* 責任払いの基本点を算出する */
            baojia2 = (menfeng + { '+': 1, '=': 2, '-': 3}[h.baojia]) % 4;
            base2   = 8000 * Math.min(h.fanshu.length, damanguan);
        }
    }
    /* …… */
}

数え役満と切り上げ満貫

get_defen() で基本点(満貫の基本点は2000点)を決定する際に、数え役満あり が true の場合は、13翻以上の基本点を8000点とする。また、切り上げ満貫あり が true の場合は基本点が1920点なら2000点に切り上げる。

function get_defen(fu, hupai, rongpai, param) {

    /* …… */

    if (hupai[0].fanshu[0] == '*') {    // 役満の場合
        /* …… */
    }
    else {                              // 役満以外の場合
        /* 符(fu)と翻数(fanshu)から基本点を算出する */
        fanshu = hupai.map(h => h.fanshu).reduce((x, y) => x + y);
        base   = (fanshu >= 13 && param.rule['数え役満あり'])
                                ? 8000      // 役満
               : (fanshu >= 11) ? 6000      // 三倍満
               : (fanshu >=  8) ? 4000      // 倍満
               : (fanshu >=  6) ? 3000      // 跳満
               : param.rule['切り上げ満貫あり'] && fu << (2 + fanshu) == 1920
                    ? 2000                  // 満貫
                    : Math.min(fu << (2 + fanshu), 2000);
    }
    /* …… */
}

*1:つまり最高得点は単一の役満(= 4倍満)となる