麻雀の和了点の計算 〜 状況役と懸賞役の一覧を作る - koba::blog の続き。
状況役、懸賞役の一覧を作成したら、次は和了形の一覧を求める。以前 和了形を求めるプログラム - koba::blog で説明したように、和了形は複数に解釈できる場合があるので、そのすべてを求める必要がある。
和了形を求めるメイン関数は hule_mianzi()
。
function hule_mianzi(shoupai, rongpai) { var hulepai = rongpai || shoupai._zimo + '_'; hulepai = hulepai.replace(/0/, '5'); return [].concat(hule_mianzi_yiban(shoupai, hulepai)) // 4面子1雀頭形 .concat(hule_mianzi_qiduizi(shoupai, hulepai)) // 七対子形 .concat(hule_mianzi_guoshi(shoupai, hulepai)) // 国士無双形 .concat(hule_mianzi_jiulian(shoupai, hulepai)); // 九蓮宝燈形 }
入力の shoupai
、rongpai
は 麻雀の和了点を計算するプログラム - koba::blog で説明した入力がそのまま渡される。
まず先頭で、ツモ和了だった場合に和了牌(hulepai
)に'_'
を付加している。'_'
はツモ和了を示すマーク。ロン和了の場合、'-'
、'+'
、'='
がすでについているのは前述した通り。
一般形である4面子1雀頭と七対子、国士無双、九蓮宝燈は和了形の求め方がそれぞれ異なるので、そのすべてを試行する必要がある。二盃口の場合、一般形と七対子形で解が得られる訳だ。
返り値は和了形の一覧。
で ツモ和了とすると返り値は、
[ [ 's88_!', 'm234', 'm234', 'p567', 'p567' ], // 二盃口 [ 'm22', 'm33', 'm44', 'p55', 'p66', 'p77', 's88_!' ] ] // 七対子
となる。和了形に赤牌の情報は要らない*1ので、 は に変換している。また '_'
はツモを、'!'
は和了牌を表している*2。和了形になっていない場合は空配列を返す。
さてそれぞれの和了形判定処理だが、4面子1雀頭形は処理が複雑なので後回しにして、簡単なその他の特殊形から。
七対子形
function hule_mianzi_qiduizi(shoupai, hulepai) { var mianzi = []; for (var s in shoupai._bingpai) { var bingpai = shoupai._bingpai[s]; for (var n = 1; n < bingpai.length; n++) { if (bingpai[n] == 0) continue; if (bingpai[n] == 2) { var p = (s+n == hulepai.substr(0,2)) ? s+n+n + hulepai[2] + '!' : s+n+n; mianzi.push(p); } else return []; // 対子でないものがあった場合、和了形でない。 } } return (mianzi.length == 7) ? [mianzi] : []; }
改めて説明するまでもないほど単純。
国士無双形
function hule_mianzi_guoshi(shoupai, hulepai) { var mianzi = []; if (shoupai._fulou.length > 0) return mianzi; var you_duizi = false; for (var s in shoupai._bingpai) { var bingpai = shoupai._bingpai[s]; var nn = (s == 'z') ? [1,2,3,4,5,6,7] : [1,9]; for (var n of nn) { if (bingpai[n] == 2) { var p = (s+n == hulepai.substr(0,2)) ? s+n+n + hulepai[2] + '!' : s+n+n; mianzi.unshift(p); you_duizi = true; } else if (bingpai[n] == 1) { var p = (s+n == hulepai.substr(0,2)) ? s+n + hulepai[2] + '!' : s+n; mianzi.push(p); } else return []; // 足りない幺九牌があった場合、和了形でない。 } } return you_duizi ? [mianzi] : []; }
幺九牌が13種そろっていること、雀頭があることをチェックしている。返り値は雀頭1つと残りの幺九牌12個の13面子として扱う。
の場合、返り値は、
[ ['z55','m1','m9','p1','p9','s1','s9','z1','z2','z3','z4_!','z6','z7'] ]
となる。
九蓮宝燈形
九蓮宝燈は4面子1雀頭形の和了なのであるが、役を判定する場合、個々の面子は意味を持たず、手牌全体を見なければならない。なのでこれを九蓮宝燈形として1面子で表すことにした。
function hule_mianzi_jiulian(shoupai, hulepai) { var s = hulepai[0]; if (! s.match(/^[mps]$/)) return []; var mianzi = s; var bingpai = shoupai._bingpai[s]; for (var n = 1; n <= 9; n++) { if ((n == 1 || n == 9) && bingpai[n] < 3) return []; // 1と9が3枚そろっていない場合、和了形でない if (bingpai[n] == 0) return []; // 足りない数牌がある場合、和了形でない var nn = (n == hulepai[1]) ? bingpai[n] - 1 : bingpai[n]; for (var i = 0; i < nn; i++) { mianzi += n; } } mianzi += hulepai.substr(1) + '!'; return [[mianzi]]; }
和了牌は最後に付加する。
の場合、返り値は、
[ [ 'm11123456789991=!' ] ]
となる。