1 はじめに このソースコードは高速にシャンテン計算をするための関数です。 ソースは完全公開しておりますので、自由にお使いください。 2 使用方法 (1)Visual Studioを起動し、新しいプロジェクトを作成する。 (言語はVisual Basic、コンソールアプリケーションを選択。) (2)「ソースコード.txt」の全文をコピーして貼り付ける。 (3)ビルドを行う。 (4)生成されたフォルダ(プロジェクト名)\bin\Debug(もしくはRelease)の下に「hashtable.txt」を名前を付けて保存する。 (5)ビルドで生成されたexeファイルを実行する。手牌情報を入力するとシャンテン数が計算されて結果が表示される。 3 各関数の説明 ・Main関数 exeファイルを実行した時に呼び出される本文 1行目に「手牌を入力してください。」と表示されるので、 続いて手牌情報を入力(ここでは例として「2m3m4m5M6m7m8m8m4p5p3s3s4s北」と入力する。) すると、シャンテン数を計算してその結果が表示されます。 先ほどの例だと 「 1シャンテン 国士12シャンテン チートイ4シャンテン 面子手1シャンテン 」 と表示されます。 その後、何かキーボード入力するとプログラムが終了します。 ・hashyomikomi関数 引数なし、戻り値なし プログラムの最初、Main関数の冒頭で1回だけ呼び出します。 exeファイルと同じディレクトリにあるhashtable.txtから 手牌の各パーツの情報をハッシュ関数で数値化したものとメンツ数・メンツ候補数の組を読み込んで hashmentusu2配列とhashkouho2配列に保存されます。 ハッシュ関数についての詳細は後述します。 ・tehaitointhairetu関数 引数 String型変数tehai 戻り値 Integer型配列(要素数39) String型tehai引数の情報のうち副露部分以外をInteger型配列に変換します。 マンズは1〜10、ピンズは11〜20、ソーズは21〜30、字牌は31〜37を使用し、 各牌ごとの枚数を各要素に代入したものが戻り値となります。 (0番と38番は非使用です。) 例 引数「2m3m4m5M6m7m8m8m4p5p3s3s4s北」 戻り値 {0, 0,1,1,1,0,1,1,2,0,1, 0,0,0,1,1,0,0,0,0,0, 0,0,2,1,0,0,0,0,0,0, 0,0,0,1,0,0,0, 0} ・haitoint関数 引数 String型変数hai、Boolean型変数akaari 戻り値 Integer型 1牌の情報haiを1〜37の数値に変換します。 akaariがtrueであれば、赤牌は10,20,30番に、falseであれば赤牌は5,15,25番になります。 haiの情報が規定のものと異なっていた場合はコンソールにエラーメッセージが表示され、-1〜-3が返されます。 ・tehaitofurosu関数 引数 String型変数tehai 戻り値 Integer型 tehaiの副露の数(「;」「;;」の数)をカウントして返します。 ・akanasihenkan関数 引数 Integer型配列(要素数39) 戻り値 Integer型配列(要素数39) 手牌情報(Integer型配列)の赤牌(10番20番30番)の牌の数を0として、黒5の枚数に+1させたものを返します。 例 引数{0, 0,1,1,1,0,1,1,2,0,1, 0,0,0,1,1,0,0,0,0,0, 0,0,2,1,0,0,0,0,0,0, 0,0,0,1,0,0,0, 0} → 戻り値{0, 0,1,1,1,1,1,1,2,0,0, 0,0,0,1,1,0,0,0,0,0, 0,0,2,1,0,0,0,0,0,0, 0,0,0,1,0,0,0, 0} ・kokusishantensu_game関数 引数 Integer型配列tehaiakanasi(要素数39)、Integer型furosu 戻り値 Integer型 手牌情報(tehaiakanasi)から国士のシャンテン数を計算して返します。 副露数1以上のときは100を返します。 ・titoishantensu_game関数 引数 Integer型配列tehaiakanasi(要素数39)、Integer型furosu 戻り値 Integer型 手牌情報(tehaiakanasi)からチートイのシャンテン数を計算して返します。 副露数1以上のときは100を返します。 ・normalshantenfunc_game関数 引数 Integer型配列tehaifuroigaiakanasi(要素数39)、Integer型furosu 戻り値 Integer型 手牌情報(tehaifuroigaiakanasi)から面子手のシャンテン数を計算して返します。 詳しい計算方法は後述します。 ・totalshantenfunc_game関数 引数 Integer型配列tehaifuroigaiakanasi(要素数39)、Integer型furosu 戻り値 Integer型配列(要素数4) 手牌情報(tehaiakanasi)から国士、チートイ、面子手のシャンテン数をそれぞれ計算して返します。 戻り値の配列について、0番が3種の中で最小のシャンテン数、1番が国士、2番がチートイ、3番が面子手のシャンテン数です。 ・mentu_cut2関数、tatu_cut2関数 面子の抜き出し及び、メンツ候補の抜き出しを再帰計算する関数です。 normalshantenfunc_game関数の中で使います。 4 面子手シャンテン数計算方法 以下の手順で計算します。 (1)雀頭候補(手牌に2枚以上ある牌)を抜き出す。 (2)手牌を各パーツごとにバラバラにする。(色が異なる場合と距離が2以上離れてる場合は別のパーツとみなす。) (3)各パーツについて、ハッシュ値を計算する。 ハッシュ値=(先頭の牌の枚数)+4*(2番目の牌の枚数)+4*5*(3番目の牌の枚数)+…+4*5^7*(9番目の牌の枚数) 例 35567のパーツの場合 先頭から{1,0,2,1,1}なので、1+20*2+100*1+500*1=641 (4)ハッシュ値と対応するメンツ数(hashmentusu2配列)・メンツ候補数(hashkouho2配列)の組が存在すれば、 その数をそのパーツのメンツ数・メンツ候補数として、加算する。 (多くの場合はハッシュ値とその組がhashtable.txtに載っているので、高速で計算できる。) 例 35567(ハッシュ値641)のパーツの場合 hashtable.txtで「641 11」という行があり、 プログラムの冒頭のhashyomikomi関数でhashmentusu2(641)=1,hashkouho2(641)=1がすでに代入されているので、 これらの数値がこのパーツのメンツ数・メンツ候補数として、全体の数値に加算される。 (5)ハッシュ値と対応するメンツ数(hashmentusu2配列)・メンツ候補数(hashkouho2配列)の組が存在しないときは そのパーツからメンツを抜けるだけ抜く(mentu_cut2関数)後、メンツ候補を抜けるだけ抜く(tatu_cut2関数)。 メンツ数・メンツ候補数が最も大きくなるような抜き方をしたときのメンツ数・メンツ候補数をそのパーツのメンツ数・メンツ候補数として、加算する。 (再帰計算なので、計算速度は低速だが、多くの場合はhashtable.txtに計算済みのものが載っているので、使用頻度は高くない。) (6)各パーツのメンツ数とメンツ候補数の合計について、8-2*メンツ数-メンツ候補数-雀頭(1)を面子手シャンテン数とする。 (7)雀頭候補が複数ある時は(1)〜(6)を繰り返す。また、雀頭なし((1)を省略)とした場合も計算する。 最も面子手シャンテン数が低くなる雀頭の抜き方(or雀頭なし)について最終的な面子手シャンテン数として、 normalshantenfunc_game関数の戻り値とする。