麻雀アプリを作る際の最初の関門はシャンテン数計算と和了形算出でしょう。*1
例えば以下の牌姿は何シャンテンでしょうか?
シャンテン数計算
面子の分け方
シャンテン数計算でよく知られた公式
- 8 ー 面子数 ×2 ー 搭子数 *2
に上記の牌姿を当てはめるときに、索子をどう分けるかが問題になります。通常は「できるだけ多くの面子を取り出す」が正解になるので、それを適用すると索子は に分けられ、式に当てはめると(2面子1搭子で)3シャンテンとなります。ですがそれは不正解です。索子は に分けなければなりません。この分け方だと(1面子4搭子で)2シャンテンであることが分かります。
ところが索子は同じ形なのに
の場合は「できるだけ多くの面子を取り出す」が正解になるのが厄介です。
5枚目の牌を待つ形
以下の牌姿は何シャンテンでしょう?
普通にプログラムを組むと を もしくは に分割し「テンパイ」と判定します。ですが和了牌となる は「5枚目の牌」であり存在しません。このような牌姿ではリーチはかけられずノーテン罰符の収入も得られないとするのが一般的です。*3
和了点計算
和了形は複数に解釈できる場合がありますが、ルールでは和了点が最高となる牌姿に解釈しなければなりません。いわゆる 高点法 です。以下の牌姿は子のロン和了で考えてください。
七対子か面子手か
七対子形と解釈すれば 断幺九+七対子 で 25符3翻 = 3200点、一般形と解釈すれば 断幺九+二盃口 で 40符4翻 = 満貫 となります。
順子か刻子か
四暗刻単騎のテンパイ形なので萬子を刻子と考えてしまいがちですが、萬子を刻子とすると 三暗刻のみで 50符2翻 = 3200点。一方、萬子を順子とすると 一盃口+純全帯幺九 で 40符4翻 = 満貫 となるのでこちらを選ばなければなりません。
雀頭はどれか
を雀頭とすると 平和+断幺九+一盃口 で 30符3翻 = 3900点、 を雀頭とすると 断幺九+一盃口+三色同順 で 40符4翻 = 満貫。
待ちは何か
を両面待ちとすると 40符1翻 = 1300点 だが、嵌張待ちとすれば符ハネして 50符1翻 = 1600点。
役満か数え役満か
九蓮宝燈なので役満なのですが、仮に リーチ+一発+ツモ+ドラ+裏ドラ もつくと 清一色+一気通貫 も合わせて数え役満になります。和了点のみで牌姿を選択していると数え役満が選ばれる可能性もあるので、役満を優先するようプログラムを書く必要があるでしょう。
翻数はいくつか
を単騎待ちと解釈すれば 一盃口+純全帯幺九 で 40符4翻 = 満貫、両面待ちと解釈すれば 平和+一盃口+純全帯幺九 で 30符5翻 = 満貫 となります。 どちらでも満貫ですが、より翻数が多い方を選択すべきでしょう。
符はいくつか
を順子とすれば 平和+一盃口+清一色 で 30符8翻 = 倍満、刻子とすれば 三暗刻+清一色 で 50符8翻 = 倍満。どちらも8翻の倍満ですが、より符の多い方を選ぶべきでしょう。
以上、電脳麻将 開発の際にバグった牌姿を集めてみました。今はこれらをテストケースに入れてます。
*1:ところがネットでは「牌山生成」にこだわる人が多いようですな😁
*2:向聴数を求めるアルゴリズム - あらの(一人)麻雀研究所
*3:これを副露牌や河に捨てられた牌にまで適用する流儀もあるようですが、それはやりすぎと思います