麻雀の向聴数のアルゴリズム

猫と麻雀 麻雀のプログラミング

一般手の向聴数

向聴数の公式

面子の数を \( a \)、面子候補 (搭子、対子)の数を \( b \)、雀頭を \( c \) とすると、向聴数 \( f(a,b,c)\) は、

\[ f(a, b, c) = 8 – 2a – b – c \]

と表現できます。ただし、\( 0 \leq a + b \leq 4 \wedge -1 \leq c \leq 1 \) であり、

\[ -1 \leq f(a, b, c) \leq 8\]

を満たします。さらに、

\[ g(a, b) = 8 – 2a – b\]

とし、

\[f(a, b, c) = g(a, b) – c\]

とします。

テーブルを作成

萬子、筒子、索子についてそれぞれ \( c \) を固定した時の \( g(a, b) \) を最小化する \( (a, b)\) を求めるだけでは、十分ではありません。これは、

一萬三萬四萬四萬五萬七萬

を考えると分かります。\( g_0(1, 0) < g_{-1}(0, 3) \) となるので、\( 3 \) 面子候補とし分解するのが最適となりますが、他の種類の牌で面子候補ができている場合、\( g_{-1}(1, 0 ) \) と分解しなければなりません。したがって、\( g(a, b) \) を最小化する \( (a, b) \) と \( a \) を最大化したときの \( b \) の最大値を求める必要があります。例えば、上記の例では、\( (a, b) = (1, 0 )\) です。

以上より、清一色の手牌において \( c \) のパターンが \( 3 \) 通り、最適化のパターンが \( 2 \) 通りなので、全体で \( 6 \) 通りの \( (a, b) \) の組が必要となります。

また、字牌についてはこのようなことを考える必要はないので、\( g(a, b)\) を最小化すればよいです。

向聴数を求める

萬子、筒子、索子、字牌について \( c \) を固定したときの \( (a, b) \) の値を次のように定めます。\( x \) を牌の種類、\( i \) を \( c \) によって決まる値、\( j \) を最適化のパターンとすると、\( x(i, j) = (a, b)\) としてパラメータを表現します。\( c = 1 \) のとき \( i = 0 \)、\( c = 0 \) のとき \( i = 1 \)、\( c = -1 \) のとき \( i = 2 \) とします。\( j = 0 \) のとき \( g(a, b) \) を最大化、\( j = 1 \) のとき \( a \) を最大化したときの \( b \) の最大化とします。

また、萬子、筒子、索子、字牌をそれぞれ、\( m, p, s, z \) とします。

このとき、向聴数は次の3つを考えれば良いです。

雀頭を仮定

例えば、萬子で雀頭ができていると仮定すると、

\[ (m(0, 0), m(0,1)), (p(2, 0), p(2, 1)) , (s(2, 0), s(2, 1)), z(2, 0) \]

で向聴数を求めます。このとき、\( 8 \) 通りの \( m, p, s, z \) のパターンについて、\( f(a, b, c) \) を計算します。また、筒子で雀頭を仮定すると、

\[ (m(2, 0), m(2,1)), (p(0, 0), p(0, 1)) , (s(2, 0), s(2, 1)), z(2, 0) \]

で向聴数を求めます。

雀頭候補

例えば、萬子で雀頭候補ができていると仮定すると、

\[ (m(1, 0), m(1,1)), (p(2, 0), p(2, 1)) , (s(2, 0), s(2, 1)), z(2, 0) \]

で向聴数を求めます。

雀頭候補なし

\[ (m(2, 0), m(2,1)), (p(2, 0), p(2, 1)) , (s(2, 0), s(2, 1)), z(2, 0) \]

で向聴数を求めます。

注意点

ある牌の種類を\( i \) とし、そのパラメータを \( (a_i, b_i) \) とすると、上記のパターンで \( f(a, b, c) \) の \( a , b\) は  \( a_i, b_i \) の和で求まりますが、\( b = \min(4 – a, b) \) となります。

感想

\( g(a, b) \) を最小化する \( (a, b)\) を求めるだけでは、十分ではないというのは、面子過多かつ 上記のような\( 6 \) 枚のパターンのみなので、\( c =  0 \) のときの分解が \( g_0(1, 0) \) が最適であることを考えると、この値を利用すればもっと簡潔に書けるかもしれません。まあ、他の牌の種類で雀頭や雀頭候補を仮定したとき、\( g_1(1, 0) \) を使うのは不自然なので、冗長な形となりました。

コメント

タイトルとURLをコピーしました