麻雀の役を判定するプログラム - koba::blog の続き。
前回同様和了形からパターンマッチングで役を判定していくのだが、問題が一つあって、それは九蓮宝燈の扱いである。九蓮宝燈という役は門前・清一色の使用牌が特定のパターンの場合になるので、和了形判定の際に「九蓮宝燈形」として処理してしまった方が簡単である。
なので先に「九蓮宝燈形」の判定処理から。
和了形を求めるプログラム - koba::blog のプログラムに九蓮宝燈形の判定処理を追加する。九蓮宝燈形は手牌全体で1面子扱いにした。これで全体が1面子だったら九蓮宝燈として処理できる*1。
function hule_jiulian(shoupai, rongpai) { var hulepai = rongpai || shoupai._zimo; var s = hulepai[0]; if (s == 'z') return []; var hule_mianzi = s; var pai = shoupai._shouli[s]; for (var n = 1; n <= 9; n++) { if ((n == 1 || n == 9) && pai[n-1] < 3) return []; if (pai[n-1] == 0) return []; var nn = (n == hulepai[1]) ? pai[n-1] -1 : pai[n-1]; for (var i = 0; i < nn; i++) { hule_mianzi += n; } } hule_mianzi += hulepai.substr(1) + '_'; return [[hule_mianzi]]; } function hule(shoupai, rongpai) { if (rongpai) { shoupai._zimo = rongpai.substr(0,2); shoupai._shouli[rongpai[0]][rongpai[1]-1]++; } return [].concat(hule_yiban(new_shoupai, rongpai)) .concat(hule_qiduizi(new_shoupai, rongpai)) .concat(hule_guoshi(new_shoupai, rongpai)) .concat(hule_jiulian(new_shoupai, rongpai)); }
これで準備はOKなので、あとはパターンマッチで処理。
function hupai(mianzi, zhuangfeng, zifeng, pre_hupai) { var menqian = mianzi.filter(function(m){ return m.match(/[\-\+\=](?!_)/)}).length == 0; // 門前のとき true var zhuangfengpai = new RegExp('^z' + ((zhuangfeng + 1) % 4) + '.*$'); // 場風 var zifengpai = new RegExp('^z' + ((zifeng + 1) % 4) + '.*$'); // 自風 var fengpai = /^z[1234].*$/; // 風牌 var sanyuanpai = /^z[567].*$/; // 三元牌 var yaojiu = /^.*[z19].*$/; // 幺九牌 var zipai = /^z.*$/; // 字牌 var shunzi = /^[mpsz](?!(\d)\1).*$/; // 順子 var kezi = /^[mpsz](\d)\1\1.*$/; // 刻子 var ankezi = /^[mpsz](\d)\1\1(?:\1|_)?$/; // 暗刻子 var gangzi = /^[mpsz](\d)\1\1.*\1.*$/; // 槓子 var danqi = /^[mpsz](\d)\1[\-\+\=]?_$/; // 単騎待ち var kanzhang = /^[mps]\d\d[\-\+\=]?_\d$/; // 嵌張待ち var bianzhang = /^[mps](123[\-\+\=]?_|7[\-\+\=]?_89)$/; // 辺張待ち /**** ここに前回の一般役の判定処理が入る ****/ function guoshiwushuang() { if (mianzi.length != 13) return []; if (mianzi.filter(function(m){return m.match(danqi)}).length == 0) return [{ name: '国士無双', fanshu: '*' }]; else return [{ name: '国士無双十三面', fanshu: '**' }]; } function sianke() { if (mianzi.length != 5) return []; if (mianzi.filter(function(m){return m.match(ankezi)}).length != 4) return []; if (mianzi.filter(function(m){return m.match(danqi)}).length == 0) return [{ name: '四暗刻', fanshu: '*' }]; else return [{ name: '四暗刻単騎', fanshu: '**' }]; } function dasanyuan() { if (mianzi.length != 5) return []; if (mianzi.filter(function(m){return m.match(sanyuanpai)}).length != 3) return []; if (mianzi[0].match(sanyuanpai)) return []; return [{ name: '大三元', fanshu: '*' }]; } function sixihu() { if (mianzi.length != 5) return []; if (mianzi.filter(function(m){return m.match(fengpai)}).length != 4) return []; if (mianzi[0].match(fengpai)) return [{ name: '小四喜', fanshu: '*' }]; else return [{ name: '大四喜', fanshu: '**' }]; } function ziyise() { if (mianzi.filter(function(m){return ! m.match(zipai)}).length > 0) return []; if (mianzi.length != 7) return [{ name: '字一色', fanshu: '*' }]; else return [{ name: '字一色七対子', fanshu: '**' }]; } function lvyise() { if (mianzi.filter(function(m){return m.match(/^[mp]/)}).length > 0) return []; if (mianzi.filter(function(m){return m.match(/^z[^6]/)}).length > 0) return []; if (mianzi.filter(function(m){return m.match(/^s.*[1579]/)}).length > 0) return []; return [{ name: '緑一色', fanshu: '*' }]; } function qinglaotou() { if (mianzi.length != 5) return []; if (mianzi.filter(function(m){return ! m.match(yaojiu)}).length > 0) return []; if (mianzi.filter(function(m){return m.match(shunzi)}).length > 0) return []; if (mianzi.filter(function(m){return m.match(zipai)}).length > 0) return []; return [{ name: '清老頭', fanshu: '*' }]; } function sigangzi() { if (mianzi.length != 5) return []; if (mianzi.filter(function(m){return m.match(gangzi)}).length != 4) return []; return [{ name: '四槓子', fanshu: '*' }]; } function jiulianbaodeng() { if (mianzi.length != 1) return []; if (! mianzi[0].match(/^[mps]1112345678999/)) return [{ name: '九蓮宝燈', fanshu: '*' }]; else return [{ name: '純正九蓮宝燈', fanshu: '**' }]; } var damanguan = [] .concat(guoshiwushuang()) .concat(sianke()) .concat(dasanyuan()) .concat(sixihu()) .concat(ziyise()) .concat(lvyise()) .concat(qinglaotou()) .concat(sigangzi()) .concat(jiulianbaodeng()); if (damanguan.length > 0) return damanguan else return [] .concat(menqianqing()) .concat(fanpai()) .concat(pinghu()) .concat(duanyaojiu()) .concat(yibeikou()) .concat(sansetongshun()) .concat(yiqitongguan()) .concat(hunquandaiyaojiu()) .concat(qiduizi()) .concat(duiduihu()) .concat(sananke()) .concat(sangangzi()) .concat(sansetongke()) .concat(hunlaotou()) .concat(xiaosanyuan()) .concat(hunyise()) .concat(chunquandaiyaojiu()) .concat(erbeikou()) .concat(qingyise()) }
役満は一般役とは複合しないので、役満がある場合はそこで処理を打ち切っている。七対子形の字一色はダブル役満にしてみました。
次は符計算の予定。