FC2ブログ

*All archives* |  *Admin*

<<10  2016/11  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30  12>>
全ツ型四人麻雀計算機その5・実際の当たり牌が切られる倍率と一度切った牌が再度切られる確率
今日のも細かいパラメータの話で退屈ですが、ぼちぼち書いていきます。

1個目が自手リーチに対して他家が当たり牌を切ってくる確率をチェックします。

↓こちらの表です。
161114-01.png

それで、当たり牌の切られ率÷当たり牌かどうか関係なしのすべての牌の切られ率で比率を取ったものが↓こちらです。
この数値が1より低いと、同ランクの牌でも、実際の当たり牌が止められる傾向を表しています。
161114-02.png
・数牌の0枚持ち牌とかだと比率が0.7とか0.8あたりなので、当たり牌が止められる傾向が高い。
・数牌2枚持ちや字牌はほぼ等倍なので、当たり牌が止められる傾向はあまり見られない。

どういう形で処理していくかは考え中。数牌0枚持ちとかだと影響はそこそこあるので、まったく無視するというわけにもいけないですし。
牌分類を数牌・字牌のみにし、見えてる枚数別は分けずに合算するとかして、データ量を減らしたらちょっとはわかりやすくなるか。

2個目。リーチ者がいない状況で、他家がすでに捨ててる牌をもう一度捨てる確率。
161114-03.png
左が全体、中が一度切ってる牌、右が切ってない牌。

パッと見で両者の違いはあんまりなさそうです。これなら無視できるレベルですね。無駄なパラメータは計算速度の観点からも極力排除する方向で。

プログラムの中身的な作業でやったのは、
・第二段階でフリテンを考慮する。(ただ、第一段階の最適打決定時には一人麻雀でフリテンを考慮してないので微妙かもしれない。)
・第一段階の再帰式で四人麻雀の要素のうち、自手聴牌でロンの可能性だけを入れた。ただし、捨て牌情報の記憶はできないので、現物筋待ち判定はかなりザル(計算開始時点の捨て牌のみを参照している。)。

今日も丸一日パラメータの採取だけでリソースをほぼ使い果たしたので、デバッグはこれからです。
四人分考慮するので、再帰計算が従前の四倍とかかかってくると予想されます。ここから鳴きも考慮させるのが一苦労。
並行して大量のパラメータ採取もやっていきます。

それにしても歩みがのろいですなぁ。あまり進んでる感はないです。現時点で四人麻雀計算機の記事Part5なので、この調子でいくと完成するまでにPart100とかになりかねないなぁ。
全ツ型四人麻雀計算機その4・被和了失点の移植と切られ率再考
今日はあんまり進んでないです。

さっきまでやってたのが、他家和了時の打点分布を従前の局収支シミュレーションから移植する作業。
パラメータ表からコピペして、コードをコピペで移して、
宣言してない変数とか関数があったりしたのでそのへんを適宜修正。
仮置きで放銃時6000点とか被ツモ時2000点とかしてたところを失点関数に置き換えてなんとか完了。

他家和了時の失点について四人麻雀計算機と局収支シミュレーションで比較します。
161113-01.png
自分は南家で、上が四人麻雀計算機、下が局収支シミュレーション(自分副露聴牌・相手子リーチ一発順と親リーチ一発順、他家降りの場合。)
上がられる時の約3分の1が親だからだいたい親:子が1:2の内分点くらいになってるからまぁ大体合ってるか。
使ってるパラメータが同じものだから当然と言えば当然ですが。


後は昨晩取ったパラメータ類を貼っておきます。計算機へ織り込むのはこれから。
ちょっとまずそうなパラメータがあったので取り直しをしています。
それが、他家がある牌を切る率という基本的なデータ。

問題点として、
・非リーチ面前者だけでなく、副露者の切った牌も含まれていた。(副露してたら危険牌を切る度合いが強まる。)
・リーチ宣言牌も中に含んでしまっていた。(リーチ宣言牌は危険牌を切る度合いが強まる。)
なので、副露者は除外し、リーチ宣言牌は別分類させました。

↓リーチ者なしの切られ率(非リーチ宣言牌とリーチ宣言牌)
161113-02.png
↓リーチ者ありの切られ率(非リーチ宣言牌)
161113-03.png
↓リーチ者ありの切られ率(リーチ宣言牌)
161113-04.png

実際の上がり牌が切られる確率と上がり牌の分布も同時に調べていたつもりだったのですが、集計が終わる直前にコード書くのをミスってたことが発覚。
また明日やり直しですね。

パラメータ表のサイズがえらい勢いで膨れていってるけど、この調子で大丈夫か?ロード時にパラメータ表を読み込む時間が長くなるとかはあんまりよろしくないのだが。
全ツ型四人麻雀計算機その3・自家他家とも面前リーチのみ
四人麻雀計算機の続き。

ものすご~く苦労してようやく自分も相手も面前リーチのみを考慮した場合のそれらしいのができました。
エラーで止まったり変な値が出まくったりしてここまでいくのが大変だった。

方式は前に述べた通り、
第一段階。(手替わり1回とか0回を上限として)今後取り得るすべての(14枚)手牌と巡目ごとに一人麻雀計算で最適打を決定させておく。
第二段階。前の局収支シミュレーションと同様、100000回シミュレーションを行う。自分の挙動は第一段階で求めた最適打テーブルから検索して淡々と摸打して、面前聴牌したらリーチする。他家の挙動はだいたい局収支シミュレーションと同様の予定。
です。

ただ、ちょっとつまづいた点があって、それは
第一段階で記憶させる情報は巡目&14枚手牌&リーチフラグ&残り手替わり回数ごとに最適打を1枚、なのですが、
前に調べたのと同じ情報が出てきたら上書きするような仕組みになっています。
この中に入ってない情報として捨て順(およびツモってきた牌)があるのですが、同じ巡目とか同じ手牌でも捨て順だけが異なっている状況がたびたび訪れ、そのたびに上書きされていきます。
捨て順が異なると見えてる枚数、ツモ率に影響が及んで、返す値が微妙に異なってくるので少々やっかいです。(変動幅はそこまで大きくはありませんが。)

捨て順によって局収支等(もしくはごくまれに最適打が変わることもあり得なくはない)が異なるものを使うのがちょっと気持ち悪いので、思い切って第一段階の計算ではツモってきた牌を見えてる枚数にカウントしないという処置を取りました。これなら捨て順が異なっても返す値は同じものになるので上書きによって結果が変動するということはおそらくないと思います。その一方、一度ツモってきた牌が見えてる枚数にカウントされないので、同じ牌をツモる率が正確なものより高くなります。極端なケースだと1mを5回以上ツモるということも低確率ながら起こり得るので、そのへんはあんまりよろしくないです。苦悩の末の決断です。

とまぁ、いろいろ考えながら作っているのですが、現状はこれだけのことを第二段階のシミュレーションで考慮しています。
今現在、前の局収支シミュレーションから移植したパラメータは「リーチ発生率」だけで、他はパラメータがないか、移植を後回しにしたかの理由で仮置きのパラメータにしています。

・自分のツモ番
聴牌で上がり牌を引いたら得点計算して上がる。
上がれなかった時、一発状態を解除、自分が先制リーチ者なら経過巡目+1
第一段階で求めた最適打を検索してその通りの打牌をする。(面前でテンパイしたらリーチ宣言、残り手替わり回数に応じて手替わりも考慮する。)

・自分の切り順
他家リーチ者への放銃判定。(放銃率は一律5%、放銃時失点6000点。)
ダブロン、トリロン流局を考慮する。
放銃がなくてリーチ宣言状態ならリーチ成立として局収支-1000点させる。
四人リーチ流局を考慮する。

・相手のツモ順
リーチ状態なら被ツモ判定。(ツモ率は一律5%、被ツモ時失点2000点。)
先制リーチ者なら経過巡目+1。切る牌はツモ切り。
リーチ状態でないならリーチ宣言判定。(リーチ発生率に基づく。)
切る牌は他家切る率パラメータに基づく。

・相手の切り順
自分が聴牌の時、ロン和了判定。
他の他家がリーチの時、横移動判定。(横移動率は一律3%。)
ダブロン、トリロン流局を考慮する。
ロンがなくてリーチ宣言状態ならリーチ成立させる。
四人リーチ流局を考慮する。

今のところこれだけです。

それで、捨て牌をランダム生成して(自分の捨て牌については現在の有効牌は除外してもう一度乱数を振りなおすようにした。)、
プログラムを走らせる。そしてようやくまともそうな値が出てくる。
161112-01.png
和了時ツモ割合はほぼ5割。良形リーチだともうちょっとツモ割合高かったような気はするのだが。
他家被和了率もまだ自手一向聴の割にはちょっと低くない?


この牌姿は複雑なので、それよりもっと簡単なノーマル両面両面一向聴でやってみて、従前の局収支シミュレーションと比較してみます。
161112-02.png
やっぱり1順当たりロン和了率はもっと低くて、他家被和了率はもっと高いですなぁ。数値の信頼性が高いのは従前の方です。

こうなった原因として考えられるのは、
・他家切る率は見えてる枚数と現物筋無筋くらいのざっくりした分け方なので、実際のプレイヤーは同ランクの牌でもカベとか外側牌とかの読みを使って、より通りやすい方を選んで切ることが多い。
・他家の挙動について、面前リーチのみを考えていて、鳴きを考慮してない分、実際は今の仮定よりは鳴きで攻めてくる。

てきとうなところでプログラムを止めて他家切る率などを観測したのが↓の表になります。
161112-03.png
リーチの待ちが58sでそのどちらかが他家の1回の切り順で切られる確率はおよそ1.8%くらい。

それに対し、従前の局収支シミュレーションで使ってる1順当たりロン和了率のパラメータがこちら。
161112-04.png
両面25,58の出る率は1.4%なので、今回の四人麻雀計算機では出る率を過大評価してるというのが分かります。

とりあえず同ランクの牌同士で、実際に当たりの牌とそうでない牌を比べてどのくらい出る率に差が出るかを別途取る必要がありそうですね。
その他に今思いついてる中で取らなければいけないパラメータの種類はこんな感じ。
・牌を切った時の放銃率
・他家リーチに対してある牌が上がり牌である確率
・リーチ宣言牌種類分布
・ドラのツモりやすさと出にくさ
・先制リーチと追っかけリーチの打点分布
・実際の上がり牌の出にくさ
・切った本人がすでに同じ牌を切ってるかどうか

リーチ系統だけでこれだけなので、副露とか染めはもっと多くなるだろうなぁ。

プログラムのコーディングについては最難関の最適打テーブルからの呼び出しは終わってるので、後は単調な工程をいかに集中力を切らさずに正確にできるかですね。

後は計算速度面も課題です。
第一段階で全部のツモと全部の手替わりを巡回して、情報の登録・上書きをしないといけないので、その分の時間が余計にかかるのと、第二段階のシミュレーションのスピードが0.1秒で100~300回くらいで、10000回なら10秒くらい×打牌候補の数だけかかるので、かなりおそいです。トータルで30秒とか1分とか。ちょっとこれだときついなー。
ただの牌譜検討として使う分には我慢できるレベルですが、
AIとして動かすことを考慮するのは速度面が改善されなければちょっと難しそうです。
一人麻雀計算機その13・見やすさ改善
今日の成果はこちら。

2016年11月11日 ver1.08
備考欄の追加。
最終結果を表形式で見やすくした。
入力値と最終結果をpng画像で出力する機能を搭載
捨て牌のランダム生成機能搭載。
打牌のみを変えて集計を再度した場合、最終結果の表をリセットしないよう変更。
バグ修正

今回は本体機能よりも見やすさ改善の方向です。(とある方からアドバイスをいただきました。ユーザー様の意見はとても貴重です。)

画面イメージを見たら一目瞭然だと思います。
161111-01.png
中央にエクセルと同じ感じの表を搭載して、そこに最終結果を表示できるようにしました。
非常に前より見やすくなりました。
調べてみればいろいろと便利な機能が見つかるものですなぁ。

出力結果を見て「この打牌も比較したい」みたいなのがあれば打牌固定にチェックを入れてもう一度集計すると、
161111-02.png
別の打牌で計算したものも表に追加されると。
特にシャンテン戻しする打牌は打牌固定にチェックを入れないで計算させると処理がスルーされてしまうので、このようなやり方が効果的です。

後はちょっとまずそうなバグが1件あったのでそこもこっそりと修正。

http://fast-uploader.com/file/7034411008114/

最新版はこちらからになります。

四人麻雀計算機の方はバグが出て苦戦中。
1枚ツモって切るだけの動作がうまいこといかないです。
再帰計算部分で記憶したはずの手牌が存在しなくて参照できない系。
これからもうちょい頑張ってバグつぶしに取り組みます。

続きを読む

全ツ型四人麻雀計算機その2・他家から切られる率とランダム捨て牌生成
まずはパラメータ取りの作業から。
絶対に必要になるのが他家からある牌が切られる確率です。

分類は、
・巡目
・リーチ者の有無(リーチ者ありなら切り順の他家は不聴であるとする。)
・牌の種類
・自分の手持ち枚数
・自分から見えてる枚数
・(リーチ者ありの時)その牌がリーチ者の現物か筋か無筋か

最低限、この程度の分類は必要でしょう。

それで昨晩、牌譜解析プログラムを動かしてパラメータを採取しました。
毎巡目に37種類の牌を逐一チェックする系なので、データが出るのに丸一日かかっちゃいましたね。まぁ一週間とかかかった一向聴の解析に比べれば待ち時間のイライラ度合いはそれほどでもないですが。

↓がリーチ者なし
161110-01.png
特に序盤は19字牌が出る確率が高いという感じだと思います。

↓こちらがリーチ者あり(モロひっかけと両筋が逆の数字になってるので読み替えてください。分類が1次元増えて、全データ量がものすごく膨大になってますが。)
161110-02.png
大雑把に言えば、現物や筋は出やすい、無筋は出にくい、19牌は無筋でもそこそこ出やすい、くらいですかね。

このパラメータを使って最初に作ったのが捨て牌のランダム生成関数です。入力作業を楽にするために作りました。使い道としては捨て牌状況がない平面図で仮に捨て牌を生成してシミュレーションするためというくらいです。

「ランダム」というのは乱数を使うという意味で、すべての牌が等確率で切られるという意味ではないです。さきほどの切られる確率のパラメータを使用します。
ただし、このパラメータをそのままの数値で使うと37種類の牌の切られる確率の合計がちょうど1にならないので、各牌の切られる確率を合計確率で割って、合計確率がちょうど1になるように調整をしています。(厳密にはこの作業をしても割り算が丸め誤差によってぴったり正確にならない(小数点10桁とかのレベルで)ので、さらに余ったor足らない確率は1mが切られる確率に加減して調整させています。)

初期の見えてる枚数は現在の自分の手牌+ツモ牌の14枚として、親から順番に現在巡目-1になるまで1枚ずつ捨てていって(切った牌は見えてる枚数+1枚する。)、最後に各人の捨て牌を表示させるという簡単なものです。

ただ、この方式だと自分の有効牌が捨て牌として選択されてフリテンになってしまうということが往々にして起こり得るので、
まぁその辺はもう一回生成し直すとかフリテンの可能性になる牌だけ都度、別の牌に書き換えるとかてきとうに対処します。

という注意点のもと、何パターンか生成してみました。
161110-03.png
なんとなく序盤は19字牌が中心でだんだん中張牌が増えていくというオーソドックスな捨て牌っぽく見えます。

5枚目の牌が出現するみたいなやばいバグは今のところは出てないです。

切られる率の内部変数はこんな感じ。
161110-04.png
1巡目は3種類のオタ風が切られる確率が45%ととても高いです。その分中張牌がとても少ないと。

そこから5巡の間に19字牌を中心にばらばらーと切られて、6巡目になったら切れてない中張牌でも一けたパーセントくらいまで切られる率が増えています。


今のところシミュレーションの方は外側のハリボテだけ作りました。具体的な中身を書くのはこれからです。
まずは一番簡単な、自分は他家からロンできる・他家がリーチをする・他家がリーチ後上がる、のパターンだけで作ってみようと思います。もちろん最初なので鳴きは自分・他家ともに考慮しません。
局収支シミュレーションのコードを流用しつつ、うまく作れるといいですね。パラメータを移植するだけでもけっこうな手間になりそうですが。
全ツ型四人麻雀計算機その1・捨て牌情報の取り込み
細かいバグ取りをしつつテスト90問やって、大きな問題はなさそうなので、ひとまず一人麻雀計算機つくりはミッションコンプリートということにします。

最新版をアップロードしておきました。適宜ご利用ください。
http://fast-uploader.com/file/7034237748995/


次は四人麻雀への拡張を考えます。

まずは簡単なところからで、場に出てる枚数のカウントからです。

場に切られてる牌と自分の手牌と他家仕掛け牌とドラ表示牌で見えてる枚数と自分の手牌にある枚数別のツモ率はすでにパラメータとして取ってあります。
見えてる枚数別手持ち枚数別ツモ率
こちらのリンクです。

このパラメータを使ってツモ率の数字を置き換えます。
今回は簡単のため、
・ドラのツモ率は通常と同じとする。(これは要検証。)
・現在の巡目以降、他家は牌を捨てることはない。
・特に山読み的なことはしていない。

2個目の仮定はあまり好ましくはないのですが、現状、他家がどの牌を切るかのパラメータがない(今晩調べる予定。)ことと、
他家の現在の巡目以降の捨て牌情報を反映させようとすると、記憶すべき情報が爆発的に増えてしまって(というか捨て牌まで同一の状況はおそらく二度とこない)、処理速度的にもたないのであきらめました。
まぁ、現在の巡目以降は平均的に切られていって、そんなに大きくどちらかに偏って数値が大きく動くとかはないとは思うので、そこまで大きな問題にはならないとは思います。

捨て牌情報なしの従前のままのがこちら。
161109-01.png

それでてきとうに6巡目までの全員の捨て牌を設定して(端牌字牌が多い想定。)、見えてる枚数別手持ち枚数別ツモ率を適用したのがこちら。
161109-02.png
全体的に局収支と和了率が下がってますね。
関連牌で言うと2m8p9p8sが1枚ずつ切れている状況です。24枚の捨て牌のうち4枚が関連牌というのは通常よりも多くて、それでツモ率が従前のものより低くなってるとかそんな感じでしょうか。

7巡目時点のツモ率を比較したものがこちらです。
161109-03.png
左が捨て牌情報なしの従前のもの、真ん中が捨て牌情報ありの場合、右が両者の差(左-右)を取ったものです。
プラスならこの捨て牌状況において通常よりツモりにくい、マイナスならツモりやすいということを表しています。

1枚場に出てる2m8p8sあたりはツモりにくくなってて、1枚しか出ていない端牌(1mとか9mとか)と中はツモりやすいという風に出ています。おそらく通常なら今回の設定よりもっと端牌字牌の割合が多いんでしょうね。

関連牌はプラス(ツモりにくい)ところが多くて、その分局収支や和了率に響いているのでしょう。たぶん。


今後の方針について。
四人の挙動を再帰式で計算するのはおそらく無理(単純に再帰式を使う回数だけで4倍に増えるし、記憶すべき情報が膨れ上がってしまうので、再帰式1回あたりの計算量も今より多くなってしまう。)ので、次のようなことを考えています。

第一段階。(手替わり1回とか0回を上限として)今後取り得るすべての(14枚)手牌と巡目ごとに一人麻雀計算で最適打を決定させておく。
第二段階。前の局収支シミュレーションと同様、100000回シミュレーションを行う。自分の挙動は第一段階で求めた最適打テーブルから検索して淡々と摸打して、面前聴牌したらリーチする。他家の挙動はだいたい局収支シミュレーションと同様の予定。

問題点。

・第一段階の最適打決定のところは現在巡目以降の捨て牌を記憶できないので、第二段階で他家から切られて薄くなった受け入れがあるから最適打を変えるみたいなのはできそうにない。
・他家のリーチが入ってもお構いなしに自分の手が最もよくなる「最適打」しか打てない。放銃の危険性が高いから打牌を変えるみたいなのは実装できるかどうかわからない。
・別途、他家がどの牌を切るか(自手リーチのときの打牌分布も含む)などのパラメータを大量に取る必要がある。

道のりは長いですなぁ。
ま、気長にやっていきますか。
一人麻雀計算機その12・テストと改善その4
今日の成果はこちら。

2016年11月8日 ver1.06
打牌固定しない場合で、黒5が手牌にあるのに赤5が打牌候補になってる時と、
一向聴・手替わり1以上のとき、手替わり0で予備調査を行い、最善手候補より500点以上局収支が低い打牌は本調査を省くよう修正。
一向聴の最善手候補の有効牌が12枚以下の時は、本調査のスキップは行わない。
手牌入力値が変わった時、固定打牌のチェックを外すよう修正。
学習モードを削除
計算速度の改善。


1個目は前回話をした一向聴・手替わり1以上のときの打牌のより分けについて。
従前はシャンテンを維持できるすべての牌について計算していましたが、明らかに損な打牌についても計算してるケースがあったので、そういうのは計算から省くようにしました。(3444m56789p6799s9sなら打4mとか打9sとか。)
ただ、テストを進めていくと、狭い一向聴のケースでは変化が重要になることが多いためか、手替わり0の予備調査では不利であっても変化も考慮することで覆るケースも散見されました。
二向聴戻しがキャッチできないのはしょうがないとして(「固定打牌」で別途調べることで対応する。)、一向聴で実は有利になる打牌を計算から取りこぼすことがないように、受け入れ12枚以下(予備調査での最適打)の場合は本調査の省略はしないことにしました。

それで昨日から今日にかけてウザク本300問テストをやって、それがなんとか終わったのですが(けっこう大変だった…)、
終わってみてまたコードを見直したところ、より高速化できるプランが浮かんだので、いろいろいじくってみました。
そうしたら結局、計算の本体関数がほぼ全面修正という結果になりました。

手を加えようと思ったのが、記憶検索のところが中心です。ざっと箇条書きで挙げると、
・何度も使うハッシュ値を変数に保存して使い回す。
・再帰関数の引数に有効牌情報を入れた配列と有効牌枚数の変数を加え、再帰先でそれを参照することで有効牌の計算回数を減らす。
・異なる打牌のときに記憶情報をリセットしていたものをそのまま情報を引き継いで使い回し。
・記憶情報を格納するコレクションをList型からDictionary型に乗り換え。

1個目と2個目が無駄の排除です。バグというほどではないけれど、同じような計算をしてるところを1回にまとめて計算時間を減らしています。
3個目は異なる打牌の時にも結局似たような牌姿に戻ることがけっこう多くて、その都度計算し直していたのを、記憶情報を引き継ぐことで再計算の時間をカットするのが目的です。ただし、貯めておくレコードの件数が増えてかえって検索コストがかかるというデメリットもあるので判断が難しかったです。
この点については4個目で改善されて、実際に両者を比べて時間が短くなったので、晴れて採用となりました。

4個目はいろいろ調べてみるとDictionary型というものを使うと、文字列をキーとして値を高速検索できるらしいことが分かりました。
いちいちListをFor文で検索して、さらに別のList(局収支とか和了率とか)を参照しなくても、文字列と各種数値をセットで扱うことができ、高速検索可能なTryGetValue関数で臨時で設けた変数に一発で検索結果を引き渡せると。
また値の型は配列型でもOKなので、いちいち
Dim tmptmpkyokushusi As Integer
Dim tmptmphouraritu As Integer

とか宣言してたのを
Dim tmptmpsu(6)As Integer
If kioku_dic_kyokushusitati(tmpkey4, tmpkey3).TryGetValue(tmpstring2, tmptmpsu) Then '記憶検索

とかやってればスピードは上がるし、コードも見やすくなるしで一石二鳥です。
入門書には載ってない情報だったので、まさにインターネット先生のお力ですね。

その効果がこちら。左が前回で、右が今回。
161108-01.png
本調査(打4mと打9s)の計算を省略してるのも込みですが、効果はてきめんです。検索の時間が10分の1くらいまで減っています。
ほぼ全面改修になって、バグが出まくったのを乗り越えた甲斐があるというものです。

打牌の選択肢が多くて、変化も(1回が上限ですが)考慮していて7秒で終わるなら十分戦力ですね。いやー、やってよかった。
ただ、かなり手直ししたので、もう一度テストが必要です。さすがにもう一度300問やり直すモチベーションはないので、後半の90問くらいのテストにとどめておこうかしら。

さすがに全く未テストのものをお出しするわけにはいかないので、今日の最新版のアップはなしで。

さすがにこれ以上の計算速度の改善はもうできないとは思うので、テストが終わって何もなければ、次回報告と最新版アップをして、無事一人麻雀計算のフェーズは終了と。
いよいよ四人麻雀への拡張の道も見えてきました。
一人麻雀計算機その11・テストと改善その3
今日の成果はこちら。

2016年11月6日 ver1.05
9の牌が孤立対子・暗刻のときにパーツ切り分けできなかったバグを修正。
出力値に聴牌時良形割合を追加。
ハッシュテーブルの内容を変更。(計算スピードアップ。)

1個目はシャンテンチェックの関数内で孤立の9の牌が対子・暗刻のとき、それをメンツ・メンツ候補として認識しないというかなり重篤なバグがあったので、それを修正しました。
現在ウザク本の牌姿をテストデータとしてかたっぱしから入力している中で、このバグが引っかかりました。早期に見つかってよかったです。あぶないあぶない。やっぱりテストは重要ですね。現在Q057まで走破したので、このまま300問までテストを続けていこうと思います。
これは放置するのがまずそうなので、後で最新版をアップしておきます。

2個目は出力値について。
従前は局収支・和了率・聴牌率・和了時得点・和了時一発割合を出してましたが、テストデータを眺めてる中で、聴牌時良形割合が欲しいなーと思ったので、それも同時に出すようにしました。

こんな感じです。ウザク本Q057。
161106-03.png
一応、今回は打4pより打7pの方が聴牌時良形割合が高くてやや局収支で上回ってます。
ただ、
・他家の和了を考慮してない。(スピードが遅い手が過大評価されやすい。)
・鳴きを考慮してない。(役牌対子・タンヤオが過小評価されやすい。)
・テンパイしたら即リーチで他家からの出上がりを考慮しない。(良形(特に平和)の方が4人麻雀の感覚より評価が高く、愚形(特に19字牌待ちと七対子)の評価が下がりやすい。)
というので、あんまりあてにはできないかもしれません。
そこはごめんなさいと頭を下げて、今のところはただの参考値で、今後の4人麻雀への拡張をこうご期待ください、くらいでお茶を濁す感じですかね。

3個目は計算スピード関連の話です。
とてもいい情報をコメントでお寄せいただきました。

ハッシュを探索する時間に悩まれているようですが、
1-9の数牌が0枚から4枚の組み合わせの全パターン(=5^9=1953125パターン)を9桁の5進数と考えて面子、ターツ数を順番に配列に入れておけば、
新たに面子、ターツ数を求めたいパターンから5進数に変換する計算コストだけで答えが得られるようになります。


初見ではこれをやると、配列の見た目サイズがバカでかくなって(実際に5進数に直すと1562500という大きさになった。)、配列への参照が時間かかるのでは?と思ったので敬遠していましたが、
まぁ戻すのはいつでもできるし、試しにやってみようか、ということでやってみたのがこちら。
(ウザク本Q032から。シャンテンを維持できる7p以外のすべての牌についてそれぞれ計算した時間の合計です。)

161106-02.png
左が変更前。右が変更後。手を入れたのは「12:ハッシュテーブル検索」の項目ですが、効果は一目瞭然です。かなり早くなってます。
従前だとString型リストにハッシュ値で割り振って文字列検索をかけてましたが、
新しいのだとコリジョン(衝突)なしのハッシュ値なので、検索の手間がいらないという利点が配列の見た目サイズのでかさを上回ってるんだなーと思います。小難しいこと考えずにさっさとこっちの方法でやっとけばよかったですね。

この方式だと検索する必要がないので、使用頻度が多いものを前方に持ってきた、最適なハッシュテーブルを作るみたいな発想はまったくの不要となります。喜ばしいことだとは思うのですが、苦労して作ったコードがあんまり意味ないものとなってしまうことへのむなしさはちょっとあります。この作業のためにウザク本300問走破を目指してたのですが、ちょっとモチベーションは下がりますね。まぁテストの重要性は先ほど再認識したので、黙々とやりますけれど。

シャンテンチェックの方は十分早くなったので、今度は記憶検索の方を早くしたいなーと思うのですが、こっちは13枚とか14枚、(もしくは37種類の牌ごと)の手牌情報を参照するので、コリジョンなしのハッシュ値に変換するのは相当困難な気がします。
37種類の牌を単純に5進数変換とかにすると7.27596E+25という天文学的な数値が。うへぇ、これは無理じゃー。実際はmaxで14枚なのでそれよりは圧倒的に少ないでしょうが、うまいことコリジョンしないハッシュ関数を見つけるのはしんどそうです。
あんまり難しいハッシュ関数にしてもハッシュ値の計算に時間がかかっては元も子もないし。
現行の手牌以外の情報+最初の2牌の情報でグループ分けしたString型のリストを前方検索するくらいしか能がなさそうです。
それともまだ何かいい方法が眠っているのか…。


今後の改善予定。
打牌固定しないときはシャンテン数を維持できるすべての牌を切るという想定ですが、明らかに不利な打牌、例えば両面をカンチャンにするとか、黒5を持ってるのに赤5を切るとかも入っていて、計算に時間がかかって非常にかったるいので、さすがにそういうのは省こうかなーと思ってます。
具体的にはこの程度の条件が当てはまるなら計算を省く方向で考えてます。
・黒5を持ってるのに赤5を切る場合。
・手替わり1以上かつ一向聴のとき、先に手替わり0の計算をしてから(一向聴ならすぐ終わるので問題にはならない。)、局収支が最善より500点以上低い打牌は本題の手替わり1以上の計算を省く。

他はノープランです。のんびりテストを続けていきます。


最新版をアップロードしようとしたけどなんかうまくいかないので今日は上げるのやめときます。
一人麻雀計算機その10・初回リリース
今日の成果はこちら。

2016年11月5日 ver1.04
入力値に同種の牌が5枚以上あるときエラーメッセージを出して処理を止めるよう変更。
ハッシュテーブルを更新・保存する機能を追加。
計算時間がやや改善。
その他雑多なバグ修正。

今日も地味目な内容だけど、計算速度向上について。

大きな変更は2点。

1点目は昨日作ったハッシュテーブルについて、いろいろな計算をやっていく中で、よく使われるパターンを前方に持ってきて、
検索にかかる時間を短縮しようという試みです。
とりあえずテーブルの更新のギミックは作れました。
後はとにかく計算をしまくって最適化の実作業をやっていくところですが、それは明日以降に回します。

2点目は一度計算済みの局収支等を呼び出す検索についてですが、
昨日は巡目でだけタグをつけてましたが、一発フラグと残り手替わり回数は整数になっててそれも簡単にタグをつけてグループ分けできるので、そのようにしました。
タグの内容(ハッシュ値)は(巡目)+18×(一発フラグ)+36×(残り手替わり回数)で1~144の数字です。
144個の別々のリストを作って、次々と手牌情報と局収支等を放り込んでいくと。
使ってないリストの方が圧倒的に多いですが、そうして分散させることで1個のリストの検索時間はたぶん短くなるだろうと。

さらにそれだけに飽き足らず、手牌情報についてもハッシュ関数でグループ分けして放り込むことにしました。
ハッシュ値の内容は(1枚目の数牌の数字)+(2枚目の数牌の数字)で0~18の数字です。ただし、字牌とか裸単騎とかで数字が分からないときは0としています。これで144個×19個で2736個のリストが生成されることになります。
さすがにやりすぎですかね。
実装して試して見たけど、普通に使う分にはそこまで大幅なスピードアップにはならないみたいです。
多分真価を発揮するのは手替わり2回以上とか三シャンテン以上とか計算時間がものすごくかかるケースとかだと思います。
それはまだ試してません。

今日の計算時間はこんな感じ。
161105-01.png
左が手替わり0、右が手替わり1。
おそらく昨日よりは時間が短くなってるはず。
一向聴ならシャンテンを維持できる牌が通常4~8種類くらいだから一打に5秒(手替わり1回)なら、20秒~40秒の試行時間であると。ただし、一度計算したものは検索で情報をゲットできるので、1巡後くらいにはデータが出そろっているのでその辺はクリアされてるかなぁと。
もちろん手替わり0回(直接シャンテンを減らす受け入れのみ)なら一瞬なので、打牌に困ってタイムオーバーという事態は今の一人麻雀計算機なら起こらないでしょう。

とりあえず手持ちのアイデアは全部打ち止めです。初期よりは相当早くなってるのでまぁ満足です。(まぁ最初がひどすぎたのですが。)
後は手牌をたくさん入力して過程をぼーっと眺めるのと、ハッシュテーブルの更新とをのんびりやります。


一区切りついたので、いったんソフトを公開してみようかなーと思います。
昨日の考え通り、フルバージョンではなく機能を限定した体験版みたいなものになります。

画面のイメージはこんな感じ。
161105-02.png
操作はいたってシンプル。

1・一人麻雀計算機.exeを起動する。

2・手牌、ツモ牌、ドラ表示牌、打牌候補の4情報を入力。

3・シャンテン数計算ボタンを押す。

4・数秒後、真ん中のリストボックスに結果(局収支・和了率・聴牌率・和了時平均得点・和了時一発割合)を表示される。

これだけです。とても簡単。


複数の打牌候補を比べるときはこんな感じ。
161105-03b.png
平面図なら打8pがよさそうだと、局収支の数値を比べればわかります。

ダウンロードのURLはこちらからになります。
https://www.axfc.net/u/3739137

とりあえず今回が初回なので、割と保守的に考えて、フルバージョンと比べてかなりの部分、機能を制限しています。
その分操作がかえってシンプルになって分かりやすくなったかもしれませんが。
しばらく反応を見ながら、体験版で操作できるところを今後拡充することも検討します。

使ってみて、「こういう機能が欲しい」みたいな要望などがあればコメント欄等でどしどしお寄せください。

もしかしたらファイル操作が絡む関係(exeファイルと同じディレクトリにあるparametahyou.txtとhashtable.txtからの読み込みとshutu.txtとhashtable.txtへの書き込み操作があります。)か、exeファイルを開くとき、ウィルスソフトにブロックされる可能性があります。(うちのウィルスバスターは反応した。)
もちろん実際にウィルスとかを仕込んでるわけではないですが(そのへんはこちらからは「信頼して実行の許可を出してくれ」くらいしか言えませんが)、
使用全般も含めて、自己責任でお願いします。

お堅い話は置いといてぜひ使ってみてください。
牌効率系の問題についての回答のひとつになります。
このソフトがあなたの快適な麻雀ライフの一助となることを願っております。
一人麻雀計算機その9・テストと改善その2
今日の成果はこちら。

更新履歴
2016年11月4日 ver1.03
計算時間をさらに圧縮。(記憶探索を同一巡目のもののみを探索するように変更。)
天鳳方式の牌の入力方式に対応
和了時一発割合の出力値を追加
高速シャンテンチェック関数について、1面子2塔子と0面子4塔子の両方が取れるとき(13578899mなど)に誤って後者を優先させていたバグを修正。
シャンテンチェック関数をハッシュテーブルから検索する方式を追加。

2016年11月3日 ver1.02
計算時間をさらに圧縮。(シャンテンチェックでパーツごとに切り分けてからメンツ数・メンツ候補数を数える方式に変更。)
シャンテンチェックの関数を従前のものと今回のパーツ切り分けバージョンの2つから選択可能に。

2016年11月3日 ver1.01
計算をバックグラウンドで行うように変更
手牌情報の出力について1m,2m,…,8m,9m,5M,1p,…の順から1m,2m,3m,4m,5m,5M,6m,7m,…の順に変更
一向聴以上の手替わり時、一発が消えていたバグを修正
計算が正常に終了した時に自動でテキスト出力する機能を追加
計算時間を大幅に圧縮。(一度計算した手牌の有効牌を記憶して呼び出す方式に変更。)

2016年11月2日 ver1.00
一人麻雀計算を実装

2016年10月29日 ver0.5
初回公開
シャンテンチェック、有効牌の計算、和了時のメンツ切り分け、役判定、得点計算を実装


割と地味ですが、確かな品質向上です。
主な修正は3つ。

1個目は一向聴で手替わり1回のとき10秒くらい時間を取っていた記憶検索について。
今までは巡目&手牌情報&一発フラグ&残り手替わり回数の文字列を結合したやつを単純にかたっぱしから記憶していって、先頭から順に検索するのではなく、
巡目でラベル付けして、1~18個の文字列リストの配列を新たに作り、
巡目でグループ分けして、手牌情報&一発フラグ&残り手替わり回数だけをリストたちに次々放り込む手法を取ってみました。
これなら関係ない巡目の情報は検索しないで済むので、処理時間が短くなると思われます。
後でいろいろ調べていて気づいたのですが、これってハッシュ法の思想そのままでしたね。昨日は「自分では絶対思いつかない」と言っておきながら、あっさり前言が覆されるという。
これによって記憶検索が1秒ちょっとと、計算時間が10分の1に近い短縮率になりました。とてもいい結果です。

2個目が天鳳方式の牌の入力方式に対応したという点。
今までは数牌なら2文字、字牌なら漢字1文字をそれぞれ対応させて、入力した手牌情報の文字列を先頭から順に読んでいく独自の文法(基本的には東風荘のmjscore形式)を使っていましたが、天鳳の牌理ツールの表現方法との対応があった方がいいのでは?的なツイートが流れてきたので、そちらもできるようにしました。
天鳳の牌理ツールの文法が独自方式と異なる点は
・赤5に対応する数字が「0」
・字牌は「z」の文字を使う。
・同色の牌が並んでいるときは先に出てきている牌の色情報を省略できる。(1m2m3m→123m)
このへんはDo文と文字列操作の組み合わせで何とかなりました。
副露手の記述方法は従前と同様(チーポン明槓が「;」、暗槓が「;;」)としています。
担当しているチェックボックスのチェックのオンオフが切り替わった時にすでに入力済み情報を両方式に相互に変換して表示する機能もついでにつけています。
ちょっと動かしてみましたが特に天鳳方式と従前方式では計算速度はそんなに変わらないっぽい(もちろん計算結果は同一。)ので、それなら天鳳方式の方が色情報が省略できるとか、いちいち日本語入力に切り替えずに入力できるという点で優れているような気がします。

3つ目が昨日出てきてた、シャンテン数を計算する関数にハッシュテーブルを取り入れて検索することです。
これはいろいろなパターンのパーツ構成に対してあらかじめメンツ数とメンツ候補数を計算しておき、テーブルに仕込んでおくことで、シャンテン数を計算する再帰式を呼び出すのを極力回避するのが骨子の1点目。
2点目は単純に全パターンをリストにすると量が膨大で検索する時間がかかってしまうので、ハッシュ値というパーツ構成情報の一部みたいなものごとにグループ分けして、そのグループごとにリストを管理して検索時間を減らそう、という思想です。
さっきやった記憶検索のときと似ていますね。

シャンテン数を計算する関数の前半はパーツごとに区切る作業からやります。
パーツとは同色の牌のうち、±2以内の距離にある牌の塊のことです。例えば135899mなら135mが1個の塊で、5と8は3だけ距離が離れていて、シャンテン数を計算する上では相互に関係することがないので、別の塊扱いで899mが塊2つ目です。
次に135mと357mでは数字は違いますが、シャンテン数を計算する上では同等です。(0面子1塔子。)
なので、数字については区別をせず、塊の先頭の牌から手持ちの枚数をサイズ9の配列に順に放り込んでいきます。
各塊ごとにサイズ9配列を計算していって、次のメンツ切り分け関数へこの配列を引数として渡すわけです。

メンツ切り分け関数はサイズ9配列を引数として、メンツ数とメンツ候補数の組を返す関数です。
なので、サイズ9配列に対応する2変数の組をあらかじめ計算しておいて、ハッシュテーブルとして保存しておけば、
引数として渡されたサイズ9配列の内容を検索して、ヒットしたら即座にメンツ数とメンツ候補数の組がわかるというわけです。再帰式は使わないのでスピードが増すであろうと。

ただし、http://ara.moo.jp/mjhmr/shanten.htmにあるように、全パターンを登録するとリストの要素数が多すぎて、検索時間がかかってかえって効率が悪くなる可能性があります。
なので、あらさんの教えに従い、合計枚数が8枚以下のパーツのみを計算してテーブル化しておきました。

実際に作った(計算したのは全部プログラムがですが。)のがこんな感じの表です。

1
00 0000000
01 1000000
01 1010000
02 1010100
02 1010101
03 1010102

21 1041100
12 1042000
01 1100000
02 1101000

12 4200000
12 4201000
20 4210000
21 4300000

2
01 0000000
01 1000000
02 1010000
02 1010100
03 1010101

18
11 0000000
12 0100000
12 0101000
12 0110000
12 0200000
20 1000000
20 1010000
21 1100000
21 2000000

19
20 0000000
21 0100000
21 1000000

20
21 0000000

各区分ごとの冒頭にある1~20の数値はハッシュキーで、(塊の先頭の数字の手持ち枚数)+4×(2番目の数字の手持ち枚数)です。サイズ9配列のうち先頭の2つをとってきたものです。この計算式ならば1種類の最大枚数が4枚なので、1,2枚目の枚数が違うならば異なるハッシュ値を返してくれます。(もちろん同じ枚数なら同じハッシュ値。)
各行は「xy zzzzzzz」という形式になっていますが、xがメンツ数、yがメンツ候補数、zが配列の後半7つの手持ち枚数です。
例えば、ハッシュキーが1の「21 1041100」なら先頭が1枚で2番目が0枚(1+4×0=1)なので、1010411の並び、つまり13555567mみたいな形です。この形は13mが1メンツ候補、555mが1メンツ、567mが2メンツの計2面子1メンツ候補ですが、行の先頭を見てみると確かに「21」となっているのがわかります。

ハッシュテーブルの作成のコードはこんな感じ。
table_sakusei.txt
テーブルの総行数は2587行とそこそこです。
これをハッシュキーで20個に振り分けていくので、単純計算だと平均で2587÷20で130個の要素数があるリストを検索することになります。
なお、塊の総枚数が9枚以上のパーツはハッシュテーブルに載っていないので、従来通り再帰式で計算をしています。

それで今日いろいろ作って計算時間を出したのがこちら。
161104-01.png
一番左がハッシュテーブルなしで手替わり0、中央がハッシュテーブルなしで手替わり1、一番右はハッシュテーブルを用いた手替わり1回です。

いろいろ妙手順を尽くして合計6秒まで短縮することができました。最初は集計に5分かかってたものがここまで短くなったのですから感動ものですね。経過時間の中身は大半が記憶情報の検索時間で、ハッシュ値も用いて高速化しているので、さすがにこれ以上は厳しいですかね…。

後、思いつくのはこれくらいです。
テーブルを見ると出現頻度が低いと思われる、4枚使いのパターンも入っていて、こういうのを後ろに回す一方、
一度計算して出現頻度が高そうなパターンを前方に持ってくることでハッシュテーブル検索を効率的に行うみたいな。(あらさんのサイトに同様の記述があります。)
また、リストに載ってない9枚以上パターンについても一回計算したものはテーブルに載せておくみたいな。
さらにリストが多くなってきて効率が悪くなって来たら、ある程度から後ろはテーブルからばっさりカットしちゃうとか。

プログラムが自分で学習してテーブルを再生成するようなイメージですね。

もしくはハッシュ関数の取り方をもうちょっと工夫してみるとか。(再ハッシュも視野に入れる。)

とまぁ、専門的な話でハッスルするのはこのあたりにしておきましょうか。


また別の話ですが、ソフトの公開方法をどうしようか悩み中です。

ソフトをばんばん公開して、みなさんに使っていただいて世に広める、というのもまぁそれはそれでいいのですが、
これを作るのに相当苦労してるので、その苦労の成果をタダで大盤振る舞いして、研究の中身が安く見られるのはちょっと気分的に嫌だなーいうのもあります。
前に公開した得点計算と有効牌だけ計算するツール(ver0.5)はまぁ成果としては大したことないのでいいのですが。

そのへんのさじ加減を考え中です。
一つ考えているのは
・親交のある方には無償でフルバージョンを配る。
・一般の方には機能を制限した体験版みたいなのを公開する。
・体験版じゃなくてフルバージョンが欲しいみたいな人にはシェアウェア的ななにかか、フリーソフト(寄付歓迎)とかでそのへんは適度に。
くらいの案。

すでに作ってある局収支シミュレータの最新版を一般公開せずに出し惜しみしてるのもまぁ、そういう気持ち面が要因のひとつです。
今レベルだとお金取れるレベルではないような気はしますが(あらさんのものと同程度かそれより下の部類なので。)、
これが4人麻雀への拡張までフリーソフトでやってしまうとさすがにフリーでは研究を大安売りしすぎじゃない?みたいな気はします。
そのへんの感覚とか相場観みたいなのはよくわからないですが、どんなもんなんでしょうね。
プロフィール

nisi5028

Author:nisi5028
FC2ブログへようこそ!

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
FC2カウンター
フリーエリア
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード