電脳麻将 ver.2.0 で開発中の新機能「パラメータによるルールのカスタマイズ」。麻雀ルールのカスタマイズ(2) ~ 流局処理と連荘判断 - koba::blog からだいぶ間が空いたが今回の話題は 赤牌 と ドラ。これらに関わるパラメータは以下の通り。
今までルールの決定は局進行を司るクラス Majiang.Game
が行なっていたが、赤牌とドラに関しては牌山を表現するクラス Majiang.Shan
で行なう。
constructor()
でルールで指定された赤牌の数に応じて牌山を生成している。
constructor(rule) {
this._rule = rule;
let hongpai = rule['赤牌'];
/* 全ての牌を順に並べる */
let pai = [];
for (let s of ['m','p','s','z']) {
for (let n = 1; n <= (s == 'z' ? 7 : 9); n++) {
for (let i = 0; i < 4; i++) {
if (n == 5 && i < hongpai[s]) pai.push(s+0); // 赤牌
else pai.push(s+n); // 赤牌以外
}
}
}
/* …… */
}
表ドラはインスタンス変数 _baopai
、裏ドラは _fubaopai
に追加する。
参照はそれぞれメソッド get baopai()
、get fubaopai()
で行う。
裏ドラは表ドラ発生のタイミングで同時に追加するが、牌山を「閉める」(ツモれない状態にする)までは参照できない。
get baopai() { return this._baopai.filter(x=>x) }
// カンドラなしの場合はカンの度に _baopai
// に '' を加えるので、それを取り除く
get fubaopai() {
return ! this._closed ? null // 牌山を「閉める」まで裏ドラは見せない
: this._fubaopai ? this._fubaopai.concat() // 裏ドラあり
: null; // 裏ドラなし
}
constructor()
で表ドラ、裏ドラを設定するが、裏ドラあり が false の場合は null とし、追加ができないようにする。
constructor(rule) {
/* …… */
this._baopai = [this._pai[4]];
this._fubaopai = rule['裏ドラあり'] ? [this._pai[9]] : null;
/* …… */
}
gangzimo()
でインスタンス変数 _weikaigang を true にすると kaigang()
が呼び出せるようになる。
gangzimo() {
/* …… */
this._weikaigang = this._rule['カンドラあり'];
/* …… */
}
kaigang()
が呼ばれると、カンドラ、カン裏を追加する。
_weikaigang が false のときは kaigang()
を呼び出せない。
kaigang() {
/* …… */
this._baopai.push(this._pai[4]); // カンドラを追加
if (this._fubaopai && this._rule['カン裏あり'])
this._fubaopai.push(this._pai[9]); // カン裏を追加
this._weikaigang = false;
/* …… */
}
後乗せのカンドラは次の打牌と同時に開かれる。これは打牌者はカンドラが何であるか知らないが、その打牌で和了した場合にカンドラが有効になることを意味する。これと同様に考えるならば、カンヅモ後に続けてカンをし、それに槍槓が発生した場合、カンをしたものはカンドラを知らないが、和了時にはカンドラが有効になるのが正しいと思う。 電脳麻将ではこの考えにしたがい、カンが連続したときはそのカンが暗槓、加槓にかかわらず一律直前のカンドラを有効にしている*2。
天鳳では連続したカンが暗槓ならば直前のカンと暗槓のカンドラが2つ続けて開かれるが、連続したカンが加槓のときは次の打牌までカンドラは開かれない。つまり連続したカンが加槓だと槍槓が発生してもカンドラは有効にならない。これはルール上の矛盾だと思うので、電脳麻将は天鳳ルールを採用しない。
カンドラ発生のタイミングはクラス Majiang.Game
が制御する。
gangzimo()
で カンドラ後乗せ の値にしたがい、カンドラを即時に発生させるか保留するかを決定する。
gangzimo() {
/* …… */
if (! this._rule['カンドラ後乗せ'] ||
this._gang.match(/^[mpsz]\d{4}$/)) this.kaigang();
/* …… */
}
保留した場合は、直後の dapai()
もしくは gang()
でカンドラが開かれる。
dapai(dapai) {
/* …… */
if (this._gang) this.kaigang();
/* …… */
}
gang(gang) {
/* …… */
if (this._gang) this.kaigang();
/* …… */
}