清一研究基礎 - (2008/07/01 (火) 01:19:08) の編集履歴(バックアップ)
清一解析の基礎
概要
清一の全聴牌型について、その結果が掲載されているサイトは簡単に見つけることが出来るが、その求め方等、基礎的な部分について記述されているサイトが少なかったのでこのページを作成した。
牌式
牌式とは一見してわかりにくい手牌を、わかりやすく表現するために草鹿三郎氏が考えた清一手牌の表現方法である。ただ、草鹿三郎氏が考えた方式には弱点があったので、それを浅見了氏が改良した。改良した牌式を、浅見牌式と呼ぶことにする。詳しくは麻雀祭都 研究室 牌式を参照のこと。
清一解析の基礎データとして浅見牌式を拡張した以下の書式で作成することにする。拡張した牌式を、デジタル牌式と呼ぶことにする。
牌式 , 待ち牌 , 面張数 , 手牌枚数
純正九連宝燈をあらわしたデジタル牌式は、以下のようになる。
311111113,123456789,9,13
デジタル牌式の作成
作成手順
- すべての和了型を挙列
- その和了型から1枚引いたものがテンパイ型
- 同一のテンパイ型とその和了牌(引いた1枚が和了牌になる)をまとめる
- 和了牌の枚数と、手牌が何枚であったかを付加する
9種類ある純正九連宝燈を参考に、上記手順を追ってみる。
すべての和了型を挙列するとこうなる。牌式とそれに対応した牌姿である。
411111113 11112345678999 321111113 11122345678999 312111113 11123345678999 311211113 11123445678999 311121113 11123455678999 311112113 11123456678999 311111213 11123456778999 311111123 11123456788999 311111114 11123456789999
和了型から1枚引いたらテンパイ型。カンマ(,)の後ろが引いた牌である。
311111113,1 311111113,2 311111113,3 311111113,4 311111113,5 311111113,6 311111113,7 311111113,8 311111113,9
それらをまとめる。
311111113,123456789
和了牌の枚数と、手牌の枚数を付加する
311111113,123456789,9,13
1.すべての和了型を求める
ご存知のように、清一における麻雀の和了型は4面子1雀頭、あるいは7対子となる。また、雀頭と刻子は9種類、順子は7種類ある。
それらを組み合わせることで、すべての和了型は求まる。
ここからはプログラミングの話になるのだが、ご了承願いたい。
雀頭を1から9まで繰り返す 各面子は1から9までの暗刻と、123から789までの順子の繰り返し
C言語風に記述すればこうなる。
暗刻は1~9とし、順子は123が10、234が11、・・・、678が15、789が16とする。 for( head = 1; head <= 9; head++ ) { for( m1 = 1; m1 <= 16; m1++ ) { for( m2 = 1; m2 <= 16; m2++ ) { for( m3 = 1; m3 <= 16; m3++ ) { for( m4 = 1; m4 <= 16; m4++ ) { 手牌は 雀頭 head + 面子1~4; } } } } }
これで、清一におけるすべての牌の組み合わせが求まるの。しかしこのままだと、ある牌が5枚以上存在になったりするので、それは取り除く必要がある。
ここで牌式を有効に使うことができる。すなわち、9個の要素を持つ配列を用意し、それを牌の持つ値と組み合わせて使うのである。
int tehai[9]; 純正九連宝燈は 1112345678999 であるが、これを 1が3枚 2~8はそれぞれ1枚 9は3枚とすると tehai[0] = 3; tehai[1] = 1; tehai[2] = 1 ; ・・・ ; tehai[7] = 1; tehai[8] = 3; と表現するのである。
まず手牌を構成する牌を配列に格納する。その後、先の判断をすれば不正な組み合わせは取り除かれる。
ある牌が5枚以上なら和了型から取り除く for( i = 0; i <= 8 ; i++ ) { if( 5 <= tehai[i] ) { ある種の牌が5枚以上あるので不正 } }
このようにして、不正な組み合わせを見つけ出し除外した結果が、七対子を除く清一のすべての和了型である。
七対子の和了型は以下のように求める。
1から9までのそれぞれの牌が0枚なのか2枚なのか、すべての組み合わせを求め、対子が7個あればそれは七対子の和了型となる。
それをアルゴリズム的に解釈すれば、2進数で 0 0000 0000 から 1 1111 1111 までの間で、1が7個あれば七対子和了型である、と言える。
for( x = 0 ; x < 0x1ff ; x++ ) { 数値 x のうち、ビットが立っている個数が7個あれば七対子 }
これで七対子のすべての和了型が求まる。
以上で、清一のすべての和了系を挙列することができる。
2.和了型からテンパイ型を求める
和了型をあらわす配列のうち、0でない要素から1を引いたら、それはテンパイ型をあらわす配列になる。
int tehai[9] = { 4,1,1,1,1,1,1,1,3 }; //九連宝燈 11112345678999 をあらわす牌式配列 for( i = 0 ; i < 9 ; i++ ) { if( tehai[i] != 0 ) { tehai[i]--; } 配列 tehai がテンパイ型になるので出力する for( n = 0 ; n < 9 ; n++ ) { printf( "%d", tehai[n] ); } printf(",%d\n", i ); tehai[i]++; //元に戻す } このような出力が求まる。 311111113,1 401111113,2 410111113,3 411011113,4 411101113,5 411110113,6 411111013,7 411111103,8 411111112,9
『1.すべての和了型を求める』の結果に、以上の操作をすることで、すべてのテンパイ型が求まる。ただし、同一データが多数存在することになるので、以下の処理をして同一データを削除する。
全てのテンパイ型をsortコマンドで並べ替えuniqコマンドで重複データを削除する。
具体的にはこのようにする。
全テンパイ型出力プログラム > 重複ありファイル sort 全テンパイ型 | uniq > 重複なしファイル
3.同一のテンパイ型とその和了牌(引いた1枚が和了牌になる)をまとめる
これまでの処理で、すべてのテンパイ型とその和了牌が求まった。ただこのままではとても使い物にならないので、同一牌姿のテンパイ系をまとめることにする。
九連宝燈のテンパイ型は現状でこうなっている 311111113,1 311111113,2 311111113,3 311111113,4 311111113,5 311111113,6 311111113,7 311111113,8 311111113,9 こうまとめたい 311111113,123456789
重複が無い状態にした結果を、「重複なしファイル」に出力したとして。
- 「重複なしファイル」から1行読み込み、先行バッファに保存しておく。
- 「重複なしファイル」からもう1行読み込んで、それを先行バッファと比較する。
- 牌式の部分が一致するのであれば、和了牌の部分をまとめる。一致しないのであれば、同一牌姿をまとめたデータを出力し、次の牌姿のチェックを行う。
牌姿保存用のデータエリアを2つ用意しておく while( 先行バッファに1行読み込める間 ) { while( もう1行読み込む ) { if( 先行バッファと今読み込んだ行の牌式が同じ ) { 先行バッファと今読み込んだ行の和了牌を保存しておく } else { 牌姿と保存しておいた和了牌を全て表示 break; } } }
これで同一牌姿は存在しなくなる。
4.和了牌の枚数と、手牌が何枚であったかを付加する
これまでの処理で、手牌13枚の清一のテンパイ型とその和了牌は求まった。それで終わってもかまわないのだが、今後の研究のために、和了牌の枚数と手牌が何枚なのかを示すデータを付加する。
九連宝燈のテンパイ型は現状でこうなっている 311111113,123456789 こうまとめたい 311111113,123456789,9,13
同一牌姿の無いデータが「テンパイデータ」ファイルにあるとして。 while( 「テンパイデータ」から読み込める間 ) { 読み込んだ行の牌式部分から手牌の枚数を求める(現状では13固定でもかまわない) 和了牌部分が何枚なのかを求める 読み込んだ1行と、手牌の枚数、和了牌の枚数を出力する }