(2022/10/12追記)
この記事をリメイクしました。そちらでは手牌の画像と待ちの画像も載せています。また、ここで求めたリストよりも多くなっています(洗い出すパターンの定義を変えたため)。
https://zenn.dev/firedial/articles/dce2ad243b3516
(2022/10/15追記)
理論もリメイクしました。用語も一部修正しています(標準形から基本形に変更など)。
https://zenn.dev/firedial/articles/2480e844bf5a44
そのため、当記事は deprecate 的な扱いとします。用語など食い違う場合は上記の記事が正とします。
はじめに
このサイトに 7 枚待ちのパターンが全て載っている。 10 枚と 13 枚はどうなるのか求めたくなるのが人間の心理である。
今回は haskell でプログラムを書いた。
https://github.com/firedial/mahjong_wait
※七対子形は考えないことにする
用語の定義
勝手に定義した用語がある。また英訳は適当である(コード書く際につけた名前)。
- 手牌(hai): 考える対象の牌の形
- 順子(shuntsu): 三つ組 (1, 2, 3), (2, 3, 4), ..., (7, 8, 9)
- 刻子(kotsu): 三つ組 (1, 1, 1), (2, 2, 2), ..., (9, 9, 9)
- 面子(mentsu): 順子 or 刻子
- 雀頭(atama): 二つ組 (1, 1), (2, 2), ..., (9, 9)
- 雀頭接続順子(atama connected shuntsu): 五つ組 (1, 1, 1, 2, 3), (2, 2, 2, 3, 4), ..., (6, 6, 6, 7, 8) と (7, 8, 9, 9, 9), (6, 7, 8, 8, 8), ..., (2, 3, 4, 4, 4)
- 待ち送り形(sendable form): 雀頭 or 雀頭接続順子
- 正規形(regular form): 手牌の枚数を 3 で割った余りが 1 であるような手牌
- 非正規形(irregular form): 手牌の枚数を 3 で割った余りが 2 であるような手牌
- 和了形(agari form): 手牌が非正規形である場合 0 雀頭 n 面子の形、正規形のときは 1 雀頭 n 面子の形
- 聴牌形(tempai form): 有効牌を付け加えることによって和了形になる形
- 待ち(wait): 正規形の場合は和了形になる牌、非正規形の場合は(手牌に関係ない)雀頭を付け加えた手牌で和了形になる牌のこと
- 面子既約(mentsu irreducible): どの面子を別の色の数字に変えても待ちの種類数が変わらない形
- 雀頭既約(atama irreducible): どの雀頭を別の色の数字に変えても待ちの種類数が変わらない形
- 雀頭接続順子既約(atama connected shuntsu irreducible): どの雀頭接続順子を別の色の数字に変えても待ちの種類数が変わらない形
- 既約(irreducible): 正規形の時は面子既約かつ雀頭既約かつ雀頭接続順子既約、非正規形の時は面子既約である形
- 長さ: 手牌の最大数字 - 手牌の最小数字 + 1
- 前方重心(former gravity): 手牌が左右対称形 or 手牌の最大数字と最小数字を枚数を見比べていき、初めて異なる枚数になったとき、小さい数字の枚数が多い形
- 標準系(normal form): 前方重心 and (長さ 8 以上 or 手牌の最小数が 2 である)
※場合によっては面子は手牌のことを指したりするが今回はその意味では使わない
※待ち送り形の正式な定義は、和了形かつ聴牌形である面子既約な形であるが、考慮する手牌の最大の枚数は 13 枚なので 8 枚以上の待ち送り形を考える必要はない(おまけで触れる)
求める前の考察
既約
待ちに関係ない面子などは省いて考えたい。
面子既約
🀐🀑🀒🀓🀕🀖🀗
この牌形は7枚使っているが 🀕🀖🀗 があってもなくても 🀐🀓 待ちである。
なので、これは 🀐🀑🀒🀓 のノベタン待ちと解釈すべきである。
今求めたいのは、すべての順子や刻子が待ちに関係しているパターンである。
雀頭既約
🀐🀐🀐🀑🀒🀘🀘
次にこの牌形を考える。待ちは 🀐🀓🀘 である。ただ、対子 🀘🀘 は待ちにかかわっているが、🀘 である必要はない。🀐🀐🀐🀑🀒🀙🀙 だったとしても、待ちが 🀐🀓🀙 となるだけである。
雀頭接続順子既約
🀐🀐🀐🀑🀒🀖🀗🀘🀘🀘
上記のような牌形のとき待ちは 🀐🀓🀕🀘 であるが、 🀖🀗🀘🀘🀘 は他の色に変えて 🀐🀐🀐🀑🀒🀙🀙🀙🀚🀛 のようにしても待ちは 🀐🀓🀙🀜 であり種類数は変わらない。
その他の待ち送り形の既約について
麻雀の手牌の最高枚数が 13 枚なので 5 枚以下の待ち送り形の既約を考えればいい。
標準系
平行移動や左右反転させた手牌は同じとみなしたい。なので下記のように標準形を定義してその形のみを考える。
平行移動
🀐🀑🀒🀓 という形と 🀒🀓🀔🀕 という形は両方ともノベタン待ちになっている。それら、つまり平行移動は同一とみなしたい。
長さが 7 以下のとき
長さが 7 以下の時は、2 から始まる手牌を採用をすれば問題ない。
長さが 9 のとき
長さが 9 の場合は平行移動ができないので気にしない。
長さが 8 のとき
下記のように、両端の待ちが平行移動によってなくなる可能性もあるので 1 から始まるやつも採用する。
例)
🀐🀐🀐🀑🀑🀑🀒🀓🀔🀕🀕🀖🀗 は 🀐🀑🀒🀓🀕🀖🀘 待ちである。
これは既約である。
一方右に一つずらした形 🀑🀑🀑🀒🀒🀒🀓🀔🀕🀖🀖🀗🀘 は
🀑🀒🀓🀔🀖🀗 待ちである。
そこから 🀖🀗🀘 を省いた 🀑🀑🀑🀒🀒🀒🀓🀔🀕🀖 の待ちも同じになるので面子既約にならない。
※ 🀐🀐🀐🀑🀒🀒🀓🀔🀕🀖🀗🀗🀗 のように右にずらしても既約のままという手牌もある。これは別途チェックする
対称形
🀐🀐🀐🀑🀒 と 🀖🀗🀘🀘🀘 も実質同じ形である。それらは前方に重心があるものを採用する。
つまり、両端の数の枚数を見たとき 🀐3枚と 🀒1枚となっている 🀐🀐🀐🀑🀒 を採用する。
手牌を全パターンなめるアルゴリズム
別名、牌くるくる(勝手にそう呼んでいる)。
手牌は長さ 9 のリストで表現し、 1 番目は 🀐 の枚数, ..., 9 番目は 🀘 の枚数を格納する。
手牌を渡して、次に考えるべき手牌を返す関数を以下のように作る。
A. 先頭が 0 のとき(🀐 の枚数が 0 枚のとき)
初めて 0 でない数字を見つけ、その数字を 1 引いてその前を 1 とする。
例)
[0, 0, 0, 3, 2, 1, 4, 2, 1]
-> [0, 0, 1, 2, 2, 1, 4, 2, 1]
-> [0, 1, 0, 2, 2, 1, 4, 2, 1]
-> [1, 0, 0, 2, 2, 1, 4, 2, 1]
B. 先頭が 0 でないとき(🀐 の枚数が 1 枚以上のとき)
先頭を 0 にし、 初めて 0 でない数字を見つけ、その数字を 0 にしてその前をその数字 + 1 とする
例)
[1, 0, 0, 2, 2, 1, 4, 2, 1]
-> [0, 0, 3, 0, 2, 1, 4, 2, 1]
そうすることによって、全てのパターンをなめることができる。
初期値を [0, 0, 0, 0, 0, 0, 0, 0, n] と与え、[n, 0, 0, 0, 0, 0, 0, 0, 0] となるまでループさせる。
※これだと最後の [n, 0, 0, 0, 0, 0, 0, 0, 0] のパターンを見れていないことになるが、1 から始まって 4 枚以下という手牌は標準形ではないので気にしない
求め方
牌くるくるをしながら、下記手順で確認していく。全部 yes になったものが求めるべき手牌である。
A. 手牌が正当であるかどうか(5枚以上使われているものがないか)
B. 手牌が標準形であるかどうか
C. 手牌が聴牌形であるかどうか
D. 手牌が既約であるかどうか
結果
https://github.com/firedial/mahjong_wait/blob/main/result.txt
ここにリスト化した。読み方はここを参照。
※間違っているかもしれないので、信じすぎないように
手牌 1 枚
🀑
単騎待ちのみ。
手牌 4 枚
🀑🀓🀙🀙
🀑🀒🀙🀙
🀑🀑🀙🀙
🀑🀒🀓🀔
🀑🀑🀑🀓
🀑🀑🀑🀒
これらの 6 通り。嵌張、両面、シャンポン、ノベタン、四暗刻聴牌のときに鬱陶しい待ち*2 である。
手牌 7 枚
19 通り。冒頭に乗せたサイトと結果一致。
手牌 10 枚
手牌 10 枚から、通り数のカウント方法によって若干変わってくる。
それは、待ち送り形が 2 種類になるのと、長さ 8 の手牌が出てくるからである。
待ち送り形が増えること
10 枚になったことにより、5 枚の待ち送り形を使って以下のような形を考えられる。
🀑🀑🀑🀒🀓🀙🀙🀙🀚🀛
この形も 10 枚待ちで、和了牌は 🀑🀓🀙🀜 である。
しかし 🀑🀑🀑🀒🀓🀙🀙 と実質同じなので、今回はカウントしない。
長さ 8 の手牌
平行移動によって同じとみなすかは手牌による。以下長さ 8 である全パターン 6 通りである。
🀑🀑🀑🀓🀔🀕🀖🀘🀘🀘 の待ちは 🀒🀓🀖🀗 であり、左に平行移動したとき、
🀐🀐🀐🀒🀓🀔🀕🀗🀗🀗 の待ちは 🀑🀒🀕🀖 と、実質同じとみなせる。
同様に、
🀑🀑🀑🀒🀓🀔🀖🀘🀘🀘 の待ちは 🀕🀖🀗 であり、左に平行移動したとき、
🀐🀐🀐🀑🀒🀓🀕🀗🀗🀗 の待ちは 🀔🀕🀖 と、これも実質同じとみなせる。
一方、
🀑🀑🀑🀒🀓🀔🀕🀖🀗🀘 の待ちは 🀐🀒🀓🀕🀖🀘 であり、左に平行移動したとき、
🀐🀐🀐🀑🀒🀓🀔🀕🀖🀗 の待ちは 🀑🀒🀔🀕🀗🀘 となって、待ちはそのまま平行移動していない。
通り数
71 通り。長さ 8 のときの手牌の平行移動を同じとみなしたら 68 通り。
長さ 8 のときの待ちが変わらない平行移動のみ同じとみなしたら 69 通り。
手牌 13 枚
171 通り。長さ 8 のときの手牌の平行移動を同じとみなしたら 160 通り。
長さ 8 のときの待ちが変わらない平行移動のみ同じとみなしたら 167 通り。
おまけ
待ち送り形
待ち送り形の正確な定義は、和了形かつ聴牌形である面子既約であった。
そのリストは下記である。
🀑🀑
🀑🀑🀑🀒🀓
🀑🀑🀑🀒🀓🀔🀕🀖
🀑🀒🀓🀓🀓🀓🀔🀕
🀑🀑🀒🀒🀓🀓🀔🀔
🀑🀑🀑🀒🀒🀒🀓🀓
🀑🀑🀒🀒🀓🀓🀔🀔🀔🀕🀖
🀑🀑🀒🀒🀒🀓🀓🀓🀔🀕🀖
🀑🀑🀑🀒🀒🀒🀓🀓🀓🀔🀕
非正規形でも待ち送り形にならないものは下記。
🀑🀓
🀑🀒
🀑🀒🀓🀔🀕
かわいそうな待ち
🀐🀐🀑🀑🀑🀒🀒🀒🀓🀔🀕🀖🀘
の待ちは 🀗🀘 の 2 種 7 枚。
一方 🀑🀒🀙🀙 の待ちは 🀐🀓 の 2 種 8 枚。
13 枚全てが待ちに絡んでいる牌なのに和了牌枚数が両面待ちに負けている。
九蓮宝燈と八連宝燈と七連宝燈
待ちの枚数が 23 枚となる手牌は、同色のすべての牌が和了牌となっている。
有名なのは役満でもある九蓮宝燈だ。
🀐🀐🀐🀑🀒🀓🀔🀕🀖🀗🀘🀘🀘
他にも待ちが 23 枚となる手牌として、
🀑🀑🀑🀒🀓🀔🀕🀖🀖🀖🀖🀗🀘 と平行移動した、
🀐🀐🀐🀑🀒🀓🀔🀕🀕🀕🀕🀖🀗 がリストにあった。
🀑🀑🀑🀒🀓🀔🀕🀕🀖🀖🀖🀖🀗 もあった。
これらを俗に八連宝燈と呼ぶ。
また、
🀑🀒🀒🀒🀒🀓🀔🀕🀖🀖🀖🀖🀗
🀑🀒🀓🀓🀓🀓🀔🀕🀕🀕🀕🀖🀗
もリストにあがっていた。これらを俗に七連宝燈と呼ぶ。
9 種類すべてが和了牌となるもの
架空の 5 枚目を使わないのであれば、九蓮宝燈のみであった。
架空の 5 枚目を使ってもいいのであれば、八連宝燈と
🀑🀒🀒🀒🀒🀓🀔🀕🀖🀖🀖🀖🀗
を除く七連宝燈のみであった。
あとがき
4 年ほど前に同じことを C 言語で書いたが、そのプログラムがどっかいったのでもう一度書いてみた。牌くるくるのアルゴリズムを思いつく(思い出す?)までにかなり時間がかかった。当時はパターンが何通りか求められただけで満足してしまい、中身は特に見ていなかった。
久々にちゃんと記事を書いた。プログラムを書くのにに1ヵ月、記事を書くのに2週間ほど。この内容で初めての同人誌を書きたいなぁと思いつつ、いつになるのか未定。ここで槍を飛ばしてもらいつつ、修正版を出せたらいいなぁと思いつつ、いつになるのか未定。まず、どうやって同人誌を出すかわからないから調べないとなぁと思いつつ、いつになるのか未定。いつになるのか未定。