はじめに
麻雀に関するプログラムを書く場合、多くの人はシャンテン数計算から手を付けるのではないかと思います。シャンテン数計算に関しては、あら氏の手法 が古典的によく知られた方法になっていますが、この方法が間違っていると 批判する記事1 もあります。これに関する私の考えを述べてみたいと思います。
あら氏の手法
『あらの(一人)麻雀研究所』で説明されている方法です。手牌から 面子 と 面子候補(搭子および対子)を抜き出し、5ブロックの制約条件の下で以下の公式に当てはめて算出します。
- シャンテン数 = 8 − 面子数 × 2 − 面子候補数
例えば上記の場合、![]()
![]()
、![]()
![]()
、![]()
、![]()
、![]()
、![]()
で 2面子、3搭子、1対子に分割できますが、5ブロックとして使えることを考慮すると、2面子、2搭子、1対子となるため、1シャンテンが解となります。
ただし、あら氏の手法は下記のような4枚使いの牌がある場合に計算を誤るとされています。
上記をテンパイと判定しますが、
はすでに尽きているので和了することはありません。
tomohxx氏の手法
Cryolite氏は 「(向聴数) = 8 - (面子の数)×2 - (面子候補の数)」という向聴数計算アルゴリズム(笑)は間違っています において、あら氏の手法は特定の牌姿においてシャンテン数を間違えるとして tomohxx氏の手法(および同様の考えに基づいた 自らの世界最高速の手法)を推奨しています。
この手法はあらかじめ全ての和了形を求めておき、それと自分の手牌との「距離」をシャンテン数とするものです。あと1枚入れ替えれば和了形となる形をシャンテン数 0 (つまりテンパイ)とします。
先ほどの手牌の場合は、
という和了形との距離が2となるため、シャンテン数は1と計算できます。
電脳麻将の手法
拙著 対戦型麻雀ゲームAIのアルゴリズムと実装 で説明し、電脳麻将 で使用している手法です。基本的には あら氏の手法と同じですが、計算式を以下に変更しています。
- シャンテン数 = 13 − 面子数 × 3 − 面子候補数 × 2 + 孤立牌数
この変更は、鳴き判断で「役に対するシャンテン数」を求めようとするときに威力を発揮します2。タンヤオに対するシャンテン数なら、幺九牌を取り除いた手牌に対してシャンテン数を計算しています。
上記の手牌では幺九牌
、
を取り除いた11枚の手牌でシャンテン数を計算して 1 とします。手牌が13枚ないと、あら氏の計算式は使えないことに注意してください。例えば手牌の全てが幺九牌のとき、タンヤオに対するシャンテン数は 13 とすべきです3。
考察
どんな手牌に対してもシャンテン数を正しく計算することが目的ならば、tomohxx氏の手法以外ありえません。ですが、麻雀アプリの目的はシャンテン数を正しく計算することではない はずです。シャンテン数はリーチ可否判定とノーテン罰符の精算判定で必要ですが、それ以外ではAIがよい手を見つけるための「ガイド」にすぎません。5枚目の牌を待つ形をテンパイと誤認しなければよいのです4。
そして tomohxx氏の手法では電脳麻将のAIの実装に必須である「役に対するシャンテン数」の計算が面倒です(全ての和了形を役に分類しなければならない)。ガイドとして使うのであれば、これは致命的弱点になります。また、和了形一覧という巨大な表を持たなければならないことはWebアプリの弱点になりえますし、和了形一覧を求めるために別のアルゴリズムが必要になることで完備でないようにも感じます。
私の結論は、目的に合わせて選択すればよい です。電脳麻将では今までの手法を使い続けることに何のためらいもありません。自分の手法を喧伝するために他の手法を貶めることに意味はないし、それは恥ずかしい行為とさえ思います。使う人が選択すればいいのですから。