- 麻雀の和了点を計算するプログラム - koba::blog
- 麻雀の和了点の計算 〜 状況役と懸賞役の一覧を作る - koba::blog
- 和了形を求めるプログラム(特殊形) - koba::blog
- 和了形を求めるプログラム(一般形) - koba::blog
- 麻雀の符を求めるプログラム - koba::blog
- 麻雀の役を判定するプログラム(再) - koba::blog
で説明してきた和了点を計算するプログラムも今日で最終回。いよいよ最終目的の点数計算です。
まずは麻雀のルールをおさらい。符と翻数を求めた後は以下の手順で得点を求める。
基本点の算出
- 符を4倍する (場ゾロ)
- 翻数分だけ2倍する
で得られた点が基本点になるというのが基本ルールだが、このままでは点数が大きくなりすぎる(いわゆる青天井)ので以下の制約がある。
負担額の決定
負担額の100点未満は切り上げる。供託の扱い
ダブロンの場合は供託収入は頭ハネとする。
役満パオの扱い
ロン和了の場合放銃者と折半、ツモ和了の場合1人払いというのが基本だが、役満の複合があった場合(大三元字一色など)はパオ対象者は成立させた役満にのみ責任を持つのが一般的なルールと思う*1。
電脳麻将 でもパオ対象者は成立させた役満にのみ責任を持つこととした*2ので、以下のルールとなる。
上記の手順に従った和了点計算は、関数 Majiang.Util.hule()
の後半部分で行っている。
Majiang.Util.hule = function(shoupai, rongpai, param) { var max = { hupai: null, fu: 0, fanshu: 0, damanguan: 0, defen: 0, fenpei: [ 0, 0, 0, 0 ] }; var pre_hupai = get_pre_hupai(param.hupai); var post_hupai = get_post_hupai( shoupai.toString(), param.baopai, param.fubaopai); for (var mianzi of hule_mianzi(shoupai, rongpai)) { var hudi = get_hudi(mianzi, param.zhuangfeng, param.menfeng); var hupai = get_hupai(mianzi, hudi, pre_hupai); if (hupai.length == 0) continue; /**** ここからが点数計算 ****/ var fu = hudi.fu; var fanshu = 0, defen = 0, damanguan = 0; var baojia2, defen2 = 0; if (hupai[0].fanshu[0] == '*') { // 役満の場合 for (var h of hupai) { // 複合する役満すべてについて以下を行う damanguan += h.fanshu.match(/\*/g).length; // 役満複合数を加算 if (h.baojia) { // パオの場合 /* -, +, = の記号からパオ対象者を特定する */ baojia2 = h.baojia == '+' ? (param.menfeng + 1) % 4 : h.baojia == '=' ? (param.menfeng + 2) % 4 : h.baojia == '-' ? (param.menfeng + 3) % 4 : -1; /* パオ対象の基本点を求める */ defen2 = 8000 * h.fanshu.match(/\*/g).length; } } /* パオを含む全体の基本点を求める */ defen = 8000 * damanguan; } else { // 役満以外の場合 hupai = hupai.concat(post_hupai); // 懸賞役を加える for (var h of hupai) { fanshu += h.fanshu } // 翻数を決定する /* 基本点を求める */ if (fanshu >= 13) defen = 8000; // 役満 else if (fanshu >= 11) defen = 6000; // 三倍満 else if (fanshu >= 8) defen = 4000; // 倍満 else if (fanshu >= 6) defen = 3000; // 跳満 else { defen = fu * 2 * 2; // 符を4倍する (場ゾロ) for (var i = 0; i < fanshu; i++) { defen *= 2 } // 翻数分だけ2倍する if (defen >= 2000) defen = 2000; // 2000点を上限とする(満貫) } } var fenpei = [ 0, 0, 0, 0 ]; // 収入と負担額を初期化する if (defen2 > 0) { // パオがあった場合、先に精算する if (rongpai) defen2 = defen2 / 2; // ロン和了の場合は放銃者と折半 defen = defen - defen2; // 基本点からパオ分を減算 defen2 = defen2 * (param.menfeng == 0 ? 6 : 4); // パオ分の負担額を求める fenpei[param.menfeng] = defen2; // 和了者の収入を加算 fenpei[baojia2] = -defen2; // パオ対象の負担額を減算 } var changbang = param.jicun.changbang; // 積み棒 var lizhibang = param.jicun.lizhibang; // 立直棒 if (rongpai || defen == 0) { // ロン和了もしくはパオ1人払いの場合 /* -, +, = の記号から放銃者を特定する */ var baojia = defen == 0 ? baojia2 // パオ1人払いは放銃者扱い : rongpai[2] == '+' ? (param.menfeng + 1) % 4 : rongpai[2] == '=' ? (param.menfeng + 2) % 4 : rongpai[2] == '-' ? (param.menfeng + 3) % 4 : -1; /* 負担額を求める(100点未満切り上げ) */ defen = Math.ceil(defen * (param.menfeng == 0 ? 6 : 4) / 100) * 100; fenpei[param.menfeng] += defen + changbang * 300 + lizhibang * 1000; // 和了者の収入を加算(供託含む) fenpei[baojia] += -defen - changbang * 300; // 放銃者の負担額を減算(供託含む) } else { // ツモ和了の場合 var zhuangjia = Math.ceil(defen * 2 / 100) * 100; // 親の負担額 var sanjia = Math.ceil(defen / 100) * 100; // 子の負担額 if (param.menfeng == 0) { // 親の和了の場合 defen = zhuangjia * 3; // 和了打点は 親の負担額 x 3 for (var l = 0; l < 4; l++) { if (l == param.menfeng) fenpei[l] += defen + changbang * 300 + lizhibang * 1000; // 和了者の収入を加算(供託含む) else fenpei[l] += -zhuangjia - changbang * 100; // 負担者の負担額を減算(供託含む) } } else { // 子の和了の場合 defen = zhuangjia + sanjia * 2; // 和了打点は 親の負担額 + 子の負担額 x 2 for (var l = 0; l < 4; l++) { if (l == param.menfeng) fenpei[l] += defen + changbang * 300 + lizhibang * 1000; // 和了者の収入を加算(供託含む) else if (l == 0) fenpei[l] += -zhuangjia - changbang * 100; // 親の負担額を減算(供託含む) else fenpei[l] += -sanjia - changbang * 100; // 子の負担額を減算(供託含む) } } } /* 得られた和了点の最大値を解とする */ if (defen + defen2 > max.defen) { max = { hupai: hupai, // 和了役一覧 fu: fu, // 符 fanshu: fanshu, // 翻数 damanguan: damanguan, // 役満複合数 defen: defen + defen2, // 和了打点 fenpei: fenpei // 局収支 }; } } return max; }
上記ソースを使ったデモプログラムも作りました。