トップページ⇒tech
62コメント23KB

麻雀のアガリ・テンパイ判定ルーチン

■ このスレッドは過去ログ倉庫に格納されています
0001デフォルトの名無しさんNGNG
何か凄まじくなって自分でもわけわかんなくなっちゃうんですけど。
スッキリとまとめることはできませんかね?
あと、チンイツの解析が全然うまくいかないんで、いいアルゴリズムがあれば教えていただきたい。
#麻雀板で聞くべきですか?
0002よくわからんのであげないNGNG
チンイツって、同じハイの種類だけで構成されているかどうかだけを
判定すればよいのじゃないの???
0003デフォルトの名無しさんNGNG
ちょっと考えれば楽なんじゃないかなぁ…
(麻雀用語良く覚えてないから記述が正確かどうか自信ないけど)
まず、シュンツ、コーツになってるものにわけるでしょ、
(これに複数の場合があるけど、以下の解析で役になってるやつ採用)
そんでそれぞれに19牌つきか、それ以外か、とかワンズだかソーズ
だか字牌だかって属性ふっていって、それみて判断ちゅうことでしょ。
(一部例外の役除く)

ってんでうまくいかないの?
00043NGNG
複数の解釈がある場合は高い役採用だね。ちょっと訂正。
00051NGNG
>まず、シュンツ、コーツになってるものにわけるでしょ、

一番聞きたいことをさらっと流さんでくれ(kw
それがうまくいかんのよ。
0006デフォルトの名無しさんNGNG
すべての組み合わせパターンをトライして、一番役が高い
のをサーチすればいいんでしょ?アガリ判定はそんなに
難しくないのでは?
でもテンパイは結構トリッキーかも。
00073NGNG
>>5
ああごめんなさいね。素直にやるなら以下のような感じかな。
これに高速化の工夫なり、手の判定のための属性つけなり、
例外的な役のための判定を付け加えればよいんでないかな。

牌の配列をaとする。
二枚同一の牌のある全ての組み合わせについて、
その二枚をaから除外し、pickup(aの最初の要素, aの最初の要素を除いた残り)を実行
上のpickupが無事終了したら、役になってる
二枚同一牌の組み合わせがない→終了

pickup(牌 p,牌の配列 a){
 pに繋がるもの(シュンツ、コーツを形成するもの)を二枚aからピックアップ
 ピックアップの仕方の全ての場合について、pickup(aの最初の要素, aの最初の要素を除いた残り)
 ピックアップする事ができない→役になってない終了
}
0008デフォルトの名無しさんNGNG
適当に計算しまくれば良いんだよ。
最近のCPUはめちゃ早いから多少無駄な計算しても十分OK!
0009デフォルトの名無しさんNGNG
ゲームプログラミング遊びのレシピに載ってる。
0010デフォルトの名無しさんNGNG
yaccで書くと楽そうだな。
0011デフォルトの名無しさんNGNG
>>10
真意を教えてくれる?
0012デフォルトの名無しさんNGNG
>>11
直感的にそう思ったんだけど
ちょっと考えたらだめっぽい。
もっと考えるとやっぱりいいかもしんないけど。
0013デフォルトの名無しさんNGNG
『ナノピコ教室』に清一の待ち牌を求める問題が載っているな。

和了の判定はまだ見当が付きやすいだけど、聴牌はちょと面倒そうだね。
5枚目の牌を待つような判定をするなよ〜。
0014デフォルトの名無しさんNGNG
linux マージャン 出検索すれば
ソースが見れます
0015デフォルトの名無しさんNGNG
組み合わせは囲碁将棋に比べれば少なそうだし
単純な縦型検索でも結構いけないかな?
0016デフォルトの名無しさんNGNG
>>13
聴牌でも何枚待ちか〜でも>>7にあるやつを少しいじるだけで余裕でしょ。
探索の手数は、少し多くなるけどね。(それは本質的にしょーがない。)
0017デフォルトの名無しさんNGNG
>>9

バグってる

>>14

ANSIのCじゃない
0018デフォルトの名無しさんNGNG
>>17
>ANSIのCじゃない
ただのクレクレ君か。
0019ちょっと考えてみたNGNG
アガリ、聴牌当たり牌検索関数

面子セット    :探索中に見つかったすべての面子パターンを記録する
アガリ手     :アガリ手が見つかったとき登録する
聴牌フラグ    :聴牌だったらON 最初はOFFではじめる
待ちパターンセット:聴牌だったときの待ち牌を登録する

start
与えられた手牌から面子セットにまだ登録されていない面子を探し出す。
(対子は1つまで、または七対子ですべて対子という制約条件付)

    →見つからない。
       このさき探索してもむだなのでリターン。

    →見つかった。
      →残り手牌がもうない。
         点数がすでにあるアガリ手より高ければアガリ手に登録
         リターン

      →残り手牌が待ちになっている。
         五枚目だったりフリテンでなければ待ちパターンに登録
         聴牌フラグを立てる
         リターン

      →その他
         残り手牌を引数にして再帰
0020ちょっと考えてみた追加NGNG
startのところは見つかる限り繰り返しね。
あとフリテンでもツモできるから待ちは待ちだね。
0021某ランドNGNG
あがりはカンが無ければ
1) 単純にソートして
2) 23333 32333 33233 33323 33332 2222222
 の5通りに区分けしてそれぞれのシュンツ コウツ トイツ 判断 が簡単では?

てんぱいは、
1) 単純にソートする
2) 先頭から順に1個をオールマイテイとして あがり検索
0022デフォルトの名無しさんNGNG
>>21
その視点はいい(見習いたい)と思うが、単にソートしただけでは
"122334" という並びから [123] と [234] の順子を見つけられないぞ。
002322NGNG
...と書いては見たが、
「順子を見つけよう」という方針で "1 があったから 2 と 3 があれば取り除き"、
残りについて同じことを繰り返せばいいわけね。
俺の脳みそがソートよりも単純でした。
0024デフォルトの名無しさんNGNG
うむ、なんで自分はリーパイせんと、てんぱってるのかも判らないのか
やっとわかった。脳のマージャン中枢がPC以下だったんだ。一生やめよ。
0025某ランド>22NGNG
あ、なるほど。という事は
5+1通りじゃなくて 2^4*5+1=81通りに区分けが要りますね
S=シュンツ K=コウツとして
23333 32333 33233 33323 33332 2222222
の3の所は S or K
Sの時は同じ数字が並んでいれば保留して調査させる
0026デフォルトの名無しさんNGNG
うーん。思っているより簡単じゃないぞ。
昨年末には18禁でえらいことになったゲームも有るからな。

因みに、同じパイの枚数を数えるのが肝だ>1
0027デフォルトの名無しさんNGNG
関係ないけど18禁だとリーチ後のカンの処理ってまともにやってる
とこ少ないね(除くアーケード)
0028デフォルトの名無しさんNGNG
>>25
4枚使いの処理にも場合分けが必要な気がします。
0029デフォルトの名無しさんNGNG
まともなゲームを作るつもりなら、槓もマトモに扱うべきだけど、
とりあえず和了の判定なら槓の数だけ手牌の数が多いし
(明槓にしろ暗槓にしろ)どの4枚が槓子かは確定しているから
刻子の特殊な場合とすればいいやね。
で、聴牌判定は、当面は‘槓しない’という方針で逝っときゃいいさ。
0030ボソNGNG
結局1はついてこられずに脱落か…(?) (キチンと理解しているならまとめるなりしなよ)

まーそもそもこんなクソ簡単な事も人に聞いちゃうレベルだからねぇ
0031デフォルトの名無しさんNGNG
ていうか、この程度のことにソートを使おうとしている時点
ですでに逝ってしまっている気が(^-^;)...

上がりのチェックだけならこんな感じでいいんじゃない?
(但し、国士・チートイは除く)

int t[44] =
/*
手牌の牌種ごとの持ち数。
全ての要素の合計は 3*X+2 枚(山からツモった時の状態とする)。
*/
{ /*九連宝燈状態*/
3,1,1,1,2,1,1,1,3,/*筒子*/ 0,/*番兵*/
0,0,0,0,0,0,0,0,0,/*索子*/ 0,/*番兵*/
0,0,0,0,0,0,0,0,0,/*万子*/ 0,/*番兵*/
0,/*東*/ 0,/*番兵*/
0,/*南*/ 0,/*番兵*/
0,/*西*/ 0,/*番兵*/
0,/*北*/ 0,/*番兵*/
0,/*白*/ 0,/*番兵*/
0,/*発*/ 0,/*番兵*/
0,/*中*/ 0,/*番兵*/
};

void f(int i)
{
for(;!t[i];i++)
if(43<=i) /*上がってる!*/
return;
if(3==t[i]){ /*刻子*/
t[i]-=3;
f(i);
t[i]+=3;
}
if(t[i+1]&&t[i+2]){ /*順子*/
t[i]--,t[i+1]--,t[i+2]--;
f(i);
t[i]++,t[i+1]++,t[i+2]++;
}
}

int main(void)
{
int i;

for(i=0;i<44;i++)
if(2<=t[i]){ /*雀頭*/
t[i]-=2;
f(0);
t[i]+=2;
}
return 0;
}

0032デフォルトの名無しさんNGNG
31の方法も一種の数えあげソートだと思う
003331NGNG
絶対そういう突っ込みが入ると思った。
003431NGNG
舌足らずでした。

32 の言っていることは大当たりです。

何が言いたかったかというと前述 21,22 の"122334"の話の
ように現実の麻雀牌が持つ位相をコンピューター内に持ち
込もうとして苦心しているあたり、「ちょっとセンスに
問題あるんじゃない?」と思っただけです。スマン。
0035デフォルトの名無しさんNGNG
つーか、>>7=3の方法でいいんじゃないの?
直感的にわかりやすいし、面子が分割できるので、役の判定も容易になる。
三連刻の状態ををシュンツととる/コーツととるのような多様性にも対応できる。
再帰を使うので計算時間はかかるが、今時のCPUなら問題にならない程度だろうし。
i-modeのjavaだとちときついか。
00361NGNG
>>30
いやいや、付いていけなくなってるわけじゃないですよ。ただあんまりここ来ないもんで。
皆さんレスありがとうございます。おかげさまであがりかどうかの判定は完璧に行きそうです。
しかしながら、やっかいなのが「上がり形を全部見つける」ことでして、これがどうもうまくいかない、というか
うまくいっているのかどうか自信がもてません。
ループが大量かつ煩瑣になるため、清一色のような特に多義の解釈が多い手では、
その全てを検索できているかどうかどうもよくわからないのです。
上手い方法はありませんかね。
0037デフォルトの名無しさんNGNG
まず手牌(晒していない牌)ソートする。
チートイツ、国士判定。
for (手牌)
 その牌が頭になるー>取り除いて再帰関数呼び出し
}
保存された上がり型から一番点の高いやつを選ぶ。

再帰関数(){
 もし残り牌がないなら、今のきりわけ方から点数計算して保存、return
 for(残りの牌)
  同じ牌が2枚あるー>取り除いて再起呼び出し。
  X,X+1,X+2になるー>取り除いて再起呼び出し。
 }
}
0038デフォルトの名無しさんNGNG
>>36
>うまくいっているのかどうか自信がもてません。

この辺りの感覚がよく理解できないんだけど…。アルゴリズムを
見たら、うまくいっているか、穴があるかなんてすぐに分かるよね。
ループが大量?…再帰使えばそんなこと無いと思うけど。どうせ大した
場合の数じゃないんだし、じゃかすか再帰させればいいじゃん。
そしたらサクっと読みやすいんだし。
003931NGNG
>>35
もしかしてコードを読んで(or 理解して)くれて
いないんでは?(まあ、いいけど...)

もし、理解しているつもりならここでひとつクイズ。

上記 31 の刻子判定部で

if(3==t[i])

としているところは

if(3<=t[i])

としても問題なく動作するはずです。
ではなぜそうしていないのでしょうか?
そうしない利点は何でしょうか?
(暇な奴の回答もキボーン)
0040名無しさん@お腹いっぱい。NGNG
[3][3][3][3][4][5]
とあるとき、
先ず[3]*4を除してしまうと順子が判定されないからってことでいいの?>31
004140NGNG
……おっと。四つ見つかっても除くのは3つなんだね。すまそ。お次の人どうぞ。
0042暇な奴の回答NGNG
31のプログラムは実は「上がり形を全部見つける」ことができるものだ。

3==t[i] が 3<=t[i] よりいいのは、こうすると重複する検査がなくなるからだ。

例えば、fの呼び出し時に 配列 t の状態が、
int t[44] = { 4, 1, 1, 0, 0, (以下すべて0 ) };
であるような場合について考えると、3<=t[i] だと、

1筒のアンコを抜く
1-2-3筒の順子を抜く
上がってる!

1-2-3筒の順子を抜く
1筒のアンコを抜く
上がってる!

このように 上がりの形が同じであるものを2回調べてしまう。
3 == t[i] だと 1回しか調べない(下の方だけ)

ってことで合ってる?
004331NGNG
>>42
ピンポンパン!
大当たり。

>>35
分かった?
0044デフォルトの名無しさんNGNG
>>31の数えあげ式は あがりのチェックは簡単だがテンパイに応用しようとすると
  途端に難しくなる。

>>21の理牌式はその点も考えているから
 >ちょっとセンスに 問題あるんじゃない? は言い過ぎじゃないか?
0045デフォルトの名無しさんNGNG
あたまわりいなあ。
画像を役全部の分用意して一部分を切り取って保存して役画像と一致するか見りゃいいだろうが!ぼけ!!
そう、何千通りの画像を用意して。
0046デフォルトの名無しさんNGNG
>>45
生姜臭え
0047デフォルトの名無しさんNGNG
何千通りってくだり もの悲しいね
0048デフォルトの名無しさんNGNG
>>45
上がり形は約1200億通りほどあるので、
1画像1kBとしてもおよそ20万GBほどの容量が必要になります。
0049デフォルトの名無しさんNGNG
>>44
>テンパイに応用しようとすると
>  途端に難しくなる。

とのことだが、

聴牌判定しようとしている手配に1筒を加えたものが
上がりになっているかどうかを判定する。

聴牌判定しようとしている手配に2筒を加えたものが
上がりになっているかどうかを判定する。

聴牌判定しようとしている手配に3筒を加えたものが
上がりになっているかどうかを判定する。

この調子で 34回(牌の種類分)調べればいいだけでは?

もちろん、実際は34回もしらべなくてもいい。
005044NGNG
確かに。 でも (聴牌判定しようとしている手配数)x(牌の種類数)の組み合せが
要る訳で ”これがオールマイティなら” よりも重そうに思ったもので。
005145NGNG
>48
アホだなあ。牌のパターン描いた絵から13牌取って並べればいいだろう。
0052デフォルトの名無しさんNGNG
>>45
上がり形は約1200億通りほどあるので、
1秒間に1万通り検索したとしてもおよそ4ヶ月半かかります。
0053デフォルトの名無しさんNGNG
http://www.at-m.or.jp/~onitama/junk/mahjong.txt
ここでも読んで頭冷やせ
0054デフォルトの名無しさんNGNG
>>31の方法では
234/234/234という分割が出来ないと思う。
0055デフォルトの名無しさんNGNG
>>54
できるよ。
刻子を取り除いたパターンの探索が終わった時点で
書き戻してるじゃん。
0056名無しサンプリング@48kHzNGNG
perlで出来ますか?
0057デフォルトの名無しさんNGNG
age
0058トリッキーの1NGNG
このスレッドって完結しているよね。
後は役の判定ですか。

それよりも、麻雀思考ルーチンを作ってみたい。
複数の参加者を集めて、思考ルーチン対決とか面白いかも。
ベースプログラム(配牌とか役の判定とか)は誰かが作って、
参加者は純粋にプログラムのみを作成する感じで。

言語を選ばないようにしようとすると面倒だけど(COMかソケット?)
仮に誰かがやってくれるなら是非参加したいな。

誰かがやるならば、ね。俺は面倒そうなので企画はいやです(笑)
というわけでsage
0059デフォルトの名無しさんNGNG
えっとねぇ、今古いの探してみてるんだけど、順子、刻子、対子
の数を数え上げて分類するしかないみたいだね。
思考ルーチン含めてCで1000行。
0060デフォルトの名無しさんNGNG
>>53
私もそこ紹介しようか?と思ったら外出だった。
0061デフォルトの名無しさんNGNG
みなさん思考ルーチンはどうしてますか?
僕は西原のマンガよんで、棒テン即リー全ツッパな
プログラム書いて殴られました。
0062名無しさん@お腹いっぱい。NGNG
殴られるの...
■ このスレッドは過去ログ倉庫に格納されています