麻雀アプリのデバッグに使える牌姿

麻雀アプリを作る際の最初の関門はシャンテン数計算と和了形算出でしょう。*1

例えば以下の牌姿は何シャンテンでしょうか?

s1s3s3s3s4s5s5s6s8z1z1z5z6z7

シャンテン数計算

面子の分け方

シャンテン数計算でよく知られた公式

  • 8 ー 面子数 ×2 ー 搭子数 *2

に上記の牌姿を当てはめるときに、索子をどう分けるかが問題になります。通常は「できるだけ多くの面子を取り出す」が正解になるので、それを適用すると索子は s3s3s3 s4s5s6 s1 s5 s8 に分けられ、式に当てはめると(2面子1搭子で)3シャンテンとなります。ですがそれは不正解です。索子は s3s4s5 s1s3 s3s5 s6s8 に分けなければなりません。この分け方だと(1面子4搭子で)2シャンテンであることが分かります。

ところが索子は同じ形なのに

s1s3s3s3s4s5s5s6s8z1z1z5z5z5

の場合は「できるだけ多くの面子を取り出す」が正解になるのが厄介です。

5枚目の牌を待つ形

以下の牌姿は何シャンテンでしょう?

m1m1m1m1p1p2p3s1s1s2s2s3s3

普通にプログラムを組むと m1m1m1 m1m1 もしくは m1m1m1 m1 に分割し「テンパイ」と判定します。ですが和了牌となる m1 は「5枚目の牌」であり存在しません。このような牌姿ではリーチはかけられずノーテン罰符の収入も得られないとするのが一般的です。*3

和了点計算

和了形は複数に解釈できる場合がありますが、ルールでは和了点が最高となる牌姿に解釈しなければなりません。いわゆる 高点法 です。(以下の牌姿は子のロン和了として説明します)。

七対子か面子手か

m2m2m3m3m4m4p5p5p6p6p7p7s8 s8

七対子形と解釈すれば 断幺九+七対子 で 25符3翻 = 3200点、一般形と解釈すれば 断幺九+二盃口 で 40符4翻 = 満貫 となります。

順子か刻子か

m1m1m1m2m2m2m3m3m3p8p9p9p9 p7

四暗刻単騎のテンパイ形なので萬子を刻子と考えてしまいがちですが、萬子を刻子とすると 三暗刻のみで 50符2翻 = 3200点。一方、萬子を順子とすると 一盃口+純全帯幺九 で 40符4翻 = 満貫 となるのでこちらを選ばなければなりません。

雀頭はどれか

m2m2m3m4m4m5m5p2p3p4s2s3s4 m3

m2 を雀頭とすると 平和+断幺九+一盃口 で 30符3翻 = 3900点m5 を雀頭とすると 断幺九+一盃口+三色同順 で 40符4翻 = 満貫

待ちは何か

m1m2m3m3m4p5p6p7z5z5z7z7z7 m2

m2 を両面待ちとすると 40符1翻 = 1300点 だが、嵌張待ちとすれば符ハネして 50符1翻 = 1600点

役満か数え役満か

m1m1m1m2m3m4m5m7m8m9m9m9m9 m6

九蓮宝燈なので役満なのですが、仮に リーチ+一発+ツモ+ドラ+裏ドラ もつくと 清一色+一気通貫 も合わせて数え役満になります。和了点のみで牌姿を選択していると数え役満が選ばれる可能性もあるので、役満を優先するようプログラムを書く必要があるでしょう。

翻数はいくつか

m1m1m1m2m2m2m3m3m3p7p8p9p9 p9

p9 を単騎待ちと解釈すれば 一盃口+純全帯幺九 で 40符4翻 = 満貫、両面待ちと解釈すれば 平和+一盃口+純全帯幺九 で 30符5翻 = 満貫 となります。 どちらでも満貫ですが、より翻数が多い方を選択すべきでしょう。

符はいくつか

s1s1s1s2s2s2s3s3s3s5s5s7s8 s9

s1 s2 s3 を順子とすれば 平和+一盃口+清一色 で 30符8翻 = 倍満、刻子とすれば 三暗刻+清一色 で 50符8翻 = 倍満。どちらも8翻の倍満ですが、より符の多い方を選ぶべきでしょう。

以上、電脳麻将 開発の際にバグった牌姿を集めてみました。今はこれらをテストケースに入れてます。

*1:ところがネットでは「牌山生成」にこだわる人が多いようですな😁

*2:向聴数を求めるアルゴリズム - あらの(一人)麻雀研究所

*3:これを副露牌や河に捨てられた牌にまで適用する流儀もあるようですが、それはやりすぎと思います