*All archives* |  *Admin*

<<04  2017/05  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 31  06>>
先制両面ダマ4ハンを牌譜解析
昨日出題した先制両面ダマ4ハン手に絡んだ話題で、


シミュレーションではそこそこ大差で、リーチ有利になっているのですが、
170513-02.png
実測値の観点からもどちらが有利かを調べて、裏を取っておこうと思います。

集計条件はちょっと前にやった愚形立直のみのときと似たような感じ。
・自分子で両面ダマ聴牌で(どちらで上がっても)ロンで4ハン30符。
・両面の端寄り・中寄りは問わない。
・誰も動きなし(純先制状態)
・リーチの場合はリーチ宣言時に判定、ダマの時は毎順判定。
・最終局結果をカウントする。

↓結果がこちら
170513-01.png
リーチの方はそれなりに整合しているっぽいですが、ダマの方は実測の方がかなり上目の値になっています。
ダマシミュレーションはかなり過小評価をしているということですね。
とはいえ、まだ局収支ではリーチが有利そうですが、点棒状況が絡めばけっこうびみょうになってきそうです。

ダマシミュレーションが過小評価になっている原因として考えられるのは、

・1順当たり他家からロン率について、1副露の副露ロンと同等と仮定して計算している。
おそらく0副露ダマと1副露だと0副露ダマの方が上がりやすいと考えられる。(実際、和了時ツモ割合を見るとかなりダマシミュレーションの方が高くなってる)

・ダマにしていれば危険牌を引いてきてもメンツのスライド等で対処できるケースがそれなりにあり得る。

・ダマにしていれば最終盤の超危険牌を引いたときに降りることも可能。
(ダマシミュレーションの方はそれを反映してないので、放銃率が高い。)

・サンプル数の偏りの可能性。
場に安いとか誰かがすでに待ち牌を切ってるとか端待ち等で、場況がいいものほどダマにされやすいかもしれない。
(証拠があるわけではないですが。)

というわけで、ダマに関するシミュレーションはけっこう足元があやしいです。
このように実測との整合性をたまにはチェックしないといけなさそうですね。
高速シャンテン計算プログラムのソースコード2
昨日の記事(高速シャンテン計算プログラムのソースコード)で、ソースを晒してみたところ、かなり有益な情報を頂きました。

前のコードは配列の操作とかをいろいろやっていた分、計算時間的に損をしているらしく、ビット列で操作する方が計算時間が早くなるみたいです。

その発想はAI計算部分で部分的に取り入れてはいましたが、シャンテン計算の関数はけっこう前に作ったものをそのまま利用していたので、ビット列(もしくは長整数型)で計算時間短縮のギミックをまだ搭載していませんでした。
というわけで、前のコードをなるべくビット列計算をメインになるよう改造してみました。
ソースコード2
hashtable2.txt

normalshantenfunc_game関数の代わりにnormalshantenfunc_game2関数を面子手シャンテン数計算に使用します。

normalshantenfunc_game2関数の中で、まずは再帰計算をしない限り(必要になるのは相当レアで複雑な牌姿に限定される)、Integer型配列はいらないので、その部分は全部取っ払いました。
次にハッシュ関数を乗算や累乗で計算していたのをビット演算子に変えたのが、tehaitolongtaiou関数です。
この関数は引数がInteger型配列と始点と終点のインデックスで戻り値がLong型整数です。
次のようにハッシュ値を計算します。
'例 引数{1,1,0,2}→(2進数で)10101100→(10進数で)172
'1と1の間にある0の個数が手牌の枚数…最上位に1を置いて、先頭は1枚なので01、2番目が1枚なので01、3番目が0枚なので1、最後尾が2枚なので00、の順で2進数を並べる
ポイントは異なる牌姿であれば必ず異なる戻り値を返す、つまり入力の牌姿と戻り値の長整数型が完全に1対1対応しているという点です。
0の個数がすべての牌の枚数(max14枚)で、1の個数が始点から終点までの牌の種類数(max37種)というわけで、maxで51ビット使うので、ぎりぎり長整数型(64ビット)でおさまる範囲です。
計算式で用いているのは+1の加算と左シフト演算のみなので、従前の乗算、累乗よりは高速で計算できると思われます。
コードの中身も前のnormalshantenfunc_game関数より2の方がやや読みやすくなったかもしれません。

また、ハッシュ関数の計算式が変わったので、ハッシュテーブルの中身を置き換えることも必要です。
それがソースコード2.txtの下のhashtable2.txtとhashyomikomi2関数です。
hashtable2.txtも前のhashtable.txtと同じところに保存しておいてください。

また、従前よりもハッシュ値の数値が大きくなってるので、検索時間の速度を考慮して、普通の配列ではなく、Dictionaryクラスを使用します。(hashmentusu3、hashkouho3)
キーがLong型のハッシュ値、値が対応するメンツ数(メンツ候補数)です。

修正点は以上なので、実践してみます。

このコードをビルドしてそのまま実行すると、
"2m4m5m8m8m9m9m5P6p8p3s4s5s東"というてきとうな牌姿に対して、1000万回シャンテンチェックを繰り返します。(1万回終了ごとにコンソールに終わった回数が表示される。)
従前のtotalshantenfunc_game関数だと私の環境で100万回に20秒、毎秒5万回というスピードでした。
これが改造したtotalshantenfunc_game2関数に変えると1000万回終えるのに30秒、毎秒30万回となりました。
およそ6倍くらいにスピードアップしたので、まぁ上出来でしょう。
爆打のプログラムはこれよりもっと早いらしいです。

まだ改善できる点はあるにはあるのですが(一部配列参照が残っているところとか)、
ビット演算は処理速度的にはいいのですが、プログラムの保守というか可読性みたいなのがちょっとネックです。String型とかInteger型配列の方が頭の中では考えやすいです。
いったん正しいロジックで組めればいいのですけど、私のおつむでは正しく組んだ上で、それ以上の手直しとかができる自信がありません。なんとなく考えやすいInteger型配列でなれ合ってしまうあたり、トップクラスのプログラマーにはなれない感じです。
まぁ、無料で公開するレベルの話なので、このレベルでお許しください。

計算の正確性についてはちょっと前にfloodgate for mahjongで対局した試合のリプレイでAIに再計算させて、1試合分動かしたところ従前の関数と違う数値が出たところはなかったので、おそらくだいじょうぶのはずです。

一応、ソースコードの中には従前で使用しているものも残しているのですが、以下のものは新関数では使わないので削除してもらって構いません。
Dim hashmentusu2(1562500) As Integer 'ハッシュテーブルメンツ数
Dim hashkouho2(1562500) As Integer 'ハッシュテーブルメンツ候補数
Sub hashyomikomi() 'hashtable読み込み
Function normalshantenfunc_game(ByVal tehaifuroigaiakanasi() As Integer, ByVal furosu As Integer) As Integer 'メンツ手のシャンテン数を返す。(ハッシュテーブル+パーツで区切って高速化)
Function totalshantenfunc_game(ByVal tehaiakanasi() As Integer, ByVal furosu As Integer) As Integer() '現在のシャンテン数を返す(0-最小、1-国士、2-チートイ、3-メンツ手)
高速シャンテン計算プログラムのソースコード
昨日の雑談で書いた、高速シャンテン計算関数について、
必要になる部分だけ抜き出して、各関数の説明書きとかを書いたので、ソースを公開します。

言語はVB.NETで書いています。そんなに難しい文法は使ってない(配列と繰り返しとテキストファイル読み込みと再帰関数程度)ので、他言語を使う方は適宜置き換えて使ってください。(必要な部分だけとはいえ、そこそこの分量があるので、大変かとは思いますが。)
ソースの本体は「ソースコード.txt」、それの説明文が「使用説明.txt」です。
また、「hashtable.txt」も計算に必要なテキストファイルになるので、指定の場所に名前を付けて保存してください。
ソースコード
使用説明
hashtable.txt

詳しくは「使用説明.txt」をご覧下さい。
質問等があればブログのコメントなりツイッターなりでしてください。

それでは、現在、もしくは将来の麻雀研究者に向けて、このソースが少しでも足しになることをお祈りしております。
麻雀研究しない方も「普段の研究ではこういうことを書いてるんだなー」と横目で見ながらお楽しみ?ください。
聴牌打牌と放銃率
ツイッターで聴牌打牌で放銃率が上がる現象について調べてくれ、という質問があったので、今回はそれを調べてみます。

牌譜解析の集計条件は、
・自分以外の3人のうち一人がリーチしている(他二人はリーチでなければ他の状態は問わない)
・自分は聴牌している。
・自分が牌を切った時に分母にプラス1、そのときにロンの声がかかった(立直者以外からのロンも含む)時に分子にプラス1
・分類は巡目と牌区分(対象は一人いる立直者に対して)と自分が前巡の段階ですでに聴牌しているか否か

結果がこちら。
170509-01.png
全部の牌トータルで見ると、前巡で既に聴牌している場合より、前巡は不聴(つまり聴牌打牌)の場合の方が放銃率はやや高い傾向にあるようです。
なので、張った瞬間に当たり牌になりやすいというのはある意味正しいです。

しかし、それぞれの牌の区分ごとで言うとすでに聴牌のケースも聴牌打牌のケースもほとんど変わらないので、
同じ無筋同士なら、特別聴牌打牌の方が当たりやすいとは言えないです。

おそらくは、聴牌打牌の方がリーチ者の無筋(危険牌)になってるケースが多い(全体から見た割合が多い)ため、個々では放銃率が変わらなくても、トータルでの放銃率が上がる、とかそんな感じでしょうか。

このテーマはこんなところにして、後は雑談。

1週間くらい前から1日1問ツイッターで押し引きを中心に何切る問題を出して、シミュレーション結果を正解として出していますが、そこそこ好評いただいているようです。フォロワー数も順調に伸びつつあります。
まぁ、見た目地味な研究よりはみんな実際の何切るの方が好きなんだろうなーとは思います。(別にそれが悪いと言いたいわけではないです。)
でも、今シミュレーション結果が手軽に出せるのも、基礎研究の積み上げのたまものなので、まぁそういう背景もあるということでひとつ。

後は(シミュレーション結果を正答としたときの)みなさんの正解率がかなり高いのはすごいですね。こちらとしては多数派とシミュレーション結果が一致しているということはそれなりにみなさんの感覚と適合しているということだと思うので、一安心です。

今問題の正答用に使っている局収支シミュレーションだとある程度の制約があります。
・基本的に二向聴以上の手はできない。
・変化を考えるのは少し手間がかかる。(日1で次々出題することを考えると、あまり手間はかけたくない)
・他家の打点はかなり大雑把(赤ドラの所在は考えてるけど、表ドラは平均のパラメータの中に全部押し込めてる。)
・聴牌気配の濃さ薄さ(特にダマテン)についてはあまり考えられない。(これも平均値そのまま。)
こういう制約に引っかからないような状況を問題として選んでいます。
次世代のシミュレータができたらこういう制約(特に前者2点)はなくなって、作問の幅と質が大幅に広がるのですが、やるには頑張って基礎研究しないといけないですね。
基礎研究の間はブログの記事は単調になりがちですが、その分を毎日の何切る出題で補えばまぁ研究サボってる感はあまりでないかなーとか思ってます。(むしろ両方ともやるので相当頑張ってる方。まぁ何切るの方は出来合いのシミュレータにパパッと入力値放り込んで10秒くらい待つだけなんでそっちはそんなに難しくない。むしろペイントとかエクセルで加工する方が時間かかるくらい。)

後はちょっと前までは技術的な問題でfloodgate for mahjongに接続することができなかったのですが、その問題がクリアできて無事接続できたので、もうそろそろAI作り(≒次世代シミュレータ)に取り掛かろうかなーと思って、今日ひさしぶりにプログラムを開いて、コードをちょっといじりました。
スタックオーバーフローのエラー回避のために全体的にコードを書きなおさないといけないのですが、まぁ宣言している変数の数が無駄に多いこと多いこと。スタックの自己管理のためにほとんどすべての変数をまるごとスタックに詰め込んでは取り出しての繰り返し、みたいな感じでとっても大変です。
スタックの自己管理にあたって、私にとっては新技術となるクラスの宣言というのをやっています。今まで自作クラスを全く作らずにやってきたというのもある意味すごい話だけど、今回そこは避けて通れなさそうです。まだ作りかけだけど、その部分がうまくいくかどうかが気が気でないです。またどうせ、大量のバグ祭りになるんだろうなぁと思うとちょっとゆううつ。

クラスの宣言と言えば、floodgate for mahjongのツモ切りプレイヤーのサンプルプログラムを見て活用しようとしたけど、たくさんのクラスとかコンストラクタ?みたいなのとかポインタ?とか、わたしのおつむではわかりませんのー、みたいな感じで撤退したりとか。まじでプログラミングスキル低いわーみたいなのですね。他の人が作った高尚なプログラムが理解できず、自作のおもちゃしか扱えないかわいそうな子です。

自作のおもちゃと言えば、(たぶん)高速シャンテン計算関数(笑)とか公開したら誰か使う人いるかな?
AI製作にあたってグローバル変数を極力使わないで、単純に引数渡しの戻り値返しの単純なシャンテン計算関数はできてるから一応、その部分だけ切り取ってお見せするとかはできるけど。まぁ、手牌とか鳴き牌とかのパラメータ指定の方法がくせがあるだろうから、他の人は使いにくい可能性はかなり大きいけど。
まぁ、たまにはスキルの自慢もしてみたい(プログラミングスキル低いわーと言ったとたんこの発言なので、とても支離滅裂感あるけど。)のと、将来の麻雀研究者が参入してきやすいみたいな高尚っぽい名目も添えて。たまには麻雀界に貢献?でもしてみようか。めっちゃ上から目線だけど。

ぐだぐだ書くのはこの辺にしておきましょう。では今回はこんなところで。
残り枚数とチーテン入れる巡目の関係その12・ドラ1の供託2本
今回はいつもの無難なテーマに戻ります。
面前一向聴VS副露聴牌の供託2本でドラ1の場合をやります。
形は両面両面のチーテン、聴牌時完全安牌に戻します。

↓タンピンドラ1の場合。
170508-01.png
↓タンヤオドラ1の場合。
170508-02.png
↓境界巡目まとめ表
170508-03.png
供託2本のドラ0とだいたい同じくらいの境界巡目になっています。

供託0本だったらドラ0よりドラ1の方がもともと早い巡目から鳴くことになっていたので、
供託2本による影響(境界巡目の早まり方)がドラ0よりも弱いという風にも見ることができます。
タン(ピン)ドラ1は鳴いても2000点で、面前リーチによる打点上昇が大きいので、相対的に供託リーチ棒の影響がドラ0よりも小さくなる、みたいな解釈でいいですかね。
プロフィール

nisi5028

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

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

この人とブロともになる

QRコード
QRコード