*All archives* |  *Admin*

<<03  2017/04  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  05>>
牌譜検討モード製作その2
牌譜の解釈までが昨日終わって、今日は出来合いの麻雀AIで実際に計算させるところをやってみます。

読み取って計算させる牌譜は昨日と同じ。
牌譜URLを載せ忘れたので載せておきます。
http://tenhou.net/0/?log=2017022819gm-0029-0000-4dfdc487&tw=0

AIの思考方法は前に作ったのと同じです。
詳しくは↓のリンクを参照のこと。
floodgate for mahjong へ参戦その6・一人麻雀再帰計算関数の作り直し
floodgate for mahjong へ参戦その8・バグ取りとかテストとか

それでなんとか途中でエラーで止まらずに1試合分完走してくれました。
計算結果がこちら。
shutu170302-01.txt
毎ツモごとに2シャンテン以下なら再帰計算で局収支等を求めます。
(まだロンとか鳴きとかは考慮できてない。)

最初に局情報と現在ツモ番と巡目とドラ表示牌を出力し、
次に各人の手牌(他家の晒してる以外の手牌は不明なので「?」にしている)と捨て牌を出力、
そして、この情報をもとに現在ツモ番者の最適打牌をAIが計算して出力する、という流れです。

かなりの情報量ですが、かなりいい感じです。
なんかおかしなところがないかもうちょっと精査する必要はありますが。

1試合分計算し終わるのにおよそ5分かかりました。
1打当たりだとほぼ数秒以内には計算し終えることができているので、まぁ及第点でしょう。
ここから他家からロンとか鳴きを考慮する分とシミュレーションパートも入ることを考えれば、最終的には計算時間はもっと増えそうですが、
まぁAIとして最低限動くレベルにはなりそうです。

ファイルサイズが10KB(mjscoreっぽい形式)から400KBに膨れました。行数で1万行をちょっと超えるくらい。
現実に1局面の何切る問題としてみるときはテキストエディタの検索機能(「南1局0本場」で検索後、「8巡目」とかでさらに検索するみたいな。)で目当てのところまでたどり着く必要がありそうです。


後、前回課題として挙げた、mjscoreっぽい形式じゃないと解析できないという問題ですが、
やっぱり自分一人でURLから自動でmjscoreにするプログラムを作るのはちょっとしんどそうです。

調べてると「天鳳の牌譜変換β」というサイト(http://sion.mokuren.ne.jp/analyzer/index_conv.php)があってそれを使えば、
人力でURLからmjlog形式に変換して、mjlogファイルを牌譜の保存場所に放り込んで、牌譜解析スクリプトでmjscore化する、という作業で、
一応、他人様の何切るもAIで計算することは可能になりそうなことが分かりました。(めんどくさいけど。)
まぁどうせ、ほぼ自分ひとりもしくは仲間内だけでしかAIは公開しないので、人力でもできるんならあえて難しいところに触る必要はないかなーと思ってます。
そういうことを考えるよりはAI本体の性能向上をやりたいです。

というわけで、牌譜検討モードもいちおうできたので、また次回からはAI本体の作業に戻ろうと思います。
次こそは他家ロンをやります。
スポンサーサイト
牌譜検討モード製作その1
既存の牌譜検討ソフトがバグっていちいち今のAI用プログラムと並行して直すのがめんどうになったのと、
入力値(手牌とか捨て牌とか)を手入力するのがめんどうになったので、
天鳳の牌譜を読み込んで、AIに計算させるプログラムを作ることにしました。

最初はあまり欲をかかずにいつも使い慣れてるmjscoreっぽい形式のテキストファイルを読み込むことから始めます。

牌譜解析用プログラムから関数とか変数をコピー&ペーストして、形式が合わないところ(同一変数の問題とかグローバル変数とローカル変数の関係あたりとか)を適宜調整して、
牌譜のテキストを解釈して内部変数に落とし込むところまで今日はできました。

今回読み取る対象の牌譜がこれで、
nyuryokuhaifu.txt

解釈後に内部変数から各種数値を書き出したのがこちら。
shutu170301-01.txt
出力についてはとりあえず既存の関数を切り貼りしただけなので、ところどころ天鳳式入力法(赤5が0、字牌がzとか)と独自方式(赤が大文字、字牌は漢字1字)がごっちゃになってますが、まぁそれは本質的な問題ではないので置いとくとします。

後はドラとか上がり時打点の情報とかも入れ忘れてますが、既存の牌譜解析プログラムをほぼそのまま持ってきているので、必要になれば取り出しもできる、と。


今後AIでの計算をやっていくわけですが、現状だと読み取り対象のファイルはmjscoreっぽい形式のテキストファイルしか読み取れないので、
天鳳Windows版(有料)で牌譜をローカルに保存して、牌譜解析スクリプトからテキスト書き出ししたものしか計算できない、ということになってます。

つまるところ、他人様の何切る問題を解析するのは事実上できない(もしくは大変手間がかかる)という問題です。
ちょっとこれはよろしくないですね。
できれば牌譜のURLを入力値として、それを解釈して内部変数に落とし込んで、さっきみたいなテキストに出力(AIの評価値をくっつけたもの)するところまでは行きたいところです。

前回、あまり天鳳の牌譜の書式には触りたくないとは書きましたがその辺は避けて通れなさそうな感じですね。

データにタッチできて、書式を正しく理解できればいいなぁ(という願望)。

難しそうなところは後回しにして、先にAIの評価値を出力するところから作ることにしますか。
経過報告
ここ数日くらいあんまりやる気が出なくて、AI作りの作業は進んでないです。

それで暇つぶしに従前に作った四麻計算機を引っ張り出してストックしてあった何切る問題を解こうと思ってプログラムを実行させると、

「StackOverflowException」

なんじゃこりゃぁ。

調べてみると、再帰的に関数を呼び出す方式で、段数が深くなっていくと保存すべきデータが解放されずにどんどん溜まっていって、上限を超えた時に発生するエラーらしいです。
今回の牌姿だと5巡目2シャンテン、残り山52枚で、山1枚消費に対して再帰を1回かけるので、最大52段の再帰に耐えられずにエラーという構図です。

これは再帰を使ってる以上、避けられない根本にかかわるバグです。これは困った。

それで数日くらいいろいろ調べまわって応急処置的な感じだけども、ストックできるデータ量の上限を一時的に増やすことでなんとかこのエラーを回避することに成功しました。

ここを参考にさせてもらいました。
http://blog.livedoor.jp/tofjw/tag/.NET
http://www.atmarkit.co.jp/fdotnet/dotnettips/356buildevent2/buildevent2.html

ビルド後に実行するコマンドラインがC++でしか使えないらしかったので、中身は空のC++プロジェクトを1個作って既存のVB.NETプロジェクトとの依存関係がどうのこうのとかなりてこずりました。いやー大変だった。

で、もう一回動かしてみるとこんどはいつもの、配列に対するOutOfIndexエラー。
なんかシャンテン数を入れる変数が変なことになってて向聴を維持できる打牌が発見できないバグらしい。
AI作りで関数の組み直しのときにも似たようなバグが出て直したな…、どうやって直したか忘れたな…、と遠い目。

ちょっともう疲れたので、従前の四麻計算機を放棄して、今のAI用プログラムに統一することでなんとかしようかと思っています。
根幹にかかわる致命的なバグ(StackOverflowException)はがんばれば回避できそうなことが分かったので。

それで、牌譜検討ができる機能をAI用プログラムに入れる必要があるのですが、従前のようにいちいち手入力するのは手間なのでやめたいです。

理想形としてあるのは、
天鳳のログのURLと特定の局面(東2局1本場7巡目の南家の手牌、とか)を示すものを入力する→各打牌候補のAIの評価値が出る
なんですけど、

天鳳の書式がよくわからなくて難しそうだなぁと思っています。なんかバイナリデータがうにゅうにゅとか、ダウンロードと解凍がうみょうみょとか、HTMLとかXML形式がうみゃうみゃとか。

とりあえずよくわからんものには最初は触れない方向で、
いつもの使い慣れてるmjscore形式っぽいテキストファイルからデータを読み取る
→内部変数に入れて親の1巡目からゲームを動かす
→ゲームを動かしながら毎ツモごとに各途中情報とAIの評価値を全部テキストに書き出す
→後は見る人(主に自分)ががんばって排出されたテキストログを見ろ!

という超投げやりスタンスを取ろうかと思っています。

mjscore形式だと1局10~20行くらいだけど、
それが、AI計算で排出されるテキストだとツモ番が4×12巡まであったとして、その毎ツモごとに10~20行の計算結果がずらーっと並ぶので、1局500~1000行くらいとすごい量になりそうだなぁ。
まぁ、その辺はできてから考えよう。
floodgate for mahjong へ参戦その9・攻守バランスのテスト
前回までは立直に対して「聴牌なら押す、1シャンテン以上なら降りる」という単純なアルゴリズムにしていましたが、
瞬間危険度を加味した局収支の大小で押すかどうかを決めることにします。

なお、再帰計算を行わない(3シャンテン以上)なら無条件で降りるということにします。

1試合目。
↓試合ログ
http://gimite.net/mjai/log/2017-02-19-165602.mjson.html
↓送受信メッセージと内部計算ログ
shutu170219-01.txt
2巡目でいきなりドラそばの4sを放す。
面子手二向聴だから手替わり0回として再帰計算をやってるので、4sのくっつきを見ることができてないですね。
2シャンテンで手替わりまで考慮すると計算時間が膨れ上がるからちょっと難しいんだよなぁ…。
局収支微差ならドラとドラそばの評価値を強制的に上げるみたいなのが必要かなぁ。

4巡目赤5sツモ、打5m。現状、フリテンを全く考えていない(というか、ロンという概念すらない)ので赤5sを残してます。

5巡目カン3p聴牌を取らず。ここでもロンという概念がなく、流局までツモれる前提にしているので、聴牌外しを過大評価し、愚形立直を過小評価しているような気がします。このへんは今後改善させていきましょう。

6巡目リーチを受ける。
ベタ降りなら序盤3pの外の1p対子落とし。
だけど、くっつきの1シャンテンで、危険度を考慮しても局収支は打1pより上回ってる(局収支2265点)ので、1mを押しています。
ただ、これも瞬間の危険度だけで、今後切る牌のことを全く考えてないから、やっぱりシミュレーションをやらないとダメっぽいですね。
それと、ベタ降りの放銃失点について、一発で打点上がってる分が入ってないような気がします。

うーん。
とりあえずやるべきことがいっぱいあることだけはわかった。
次は他家からロンを含めた再帰計算をやるようにしましょうか。

・やることリスト
他家からロンを含めた再帰計算
自分鳴き判断を含めた再帰計算
フリテン・同順フリテン処理
シミュレーションパート
再帰計算途中打ち切り処理
ドラ・ドラそばの評価値
一発の平均打点
floodgate for mahjong へ参戦その8・バグ取りとかテストとか
昨日の赤5の危険度のバグは既存部分(カベ効果補正のパラメータで引数の順番を間違えてた)に潜んでいて、発見するのに苦労しました。

円滑にデバッグをするために、コンソール上で表示してる文字列をログのテキストファイルに出力して、
さらに、そのログをファイルから読み取って前の試合の局面を再現できる機能をつけました。

今日もAIに打たせた後、ぼーっとログを眺めていると、なんか聴牌なのにリーチを打ってくれないケースがちょこちょこ見受けられたので、検証してみます。

今日の問題の局面はこちら。
170217-03.png
聴牌なので、ベタ降りはしなくて、ダマ・立直・聴牌外しのどれかを選択するところです。

上家の立直を無視するならドラ2sがあまり残ってなくて、3sは見た目は4枚生きてるので、7mか西を切って3s待ちリーチするところ(ツモで点パネのため打7mか)…と思いきや、AIは4s切りダマを選択しました。

この局面について、各選択肢ごとに局収支・和了率・和了時平均得点をウォッチしてみます。
170217-04.png
観測された値を見てみると、
確かに打4sダマが一番局収支の数値が大きくなってます。

ただ、和了率(ここでは他家を無視した一人麻雀再帰計算におけるツモだけの和了率)では打7mリーチが23%くらいで、
上がれない確率が77%もあるので、リーチ棒を失う影響がかなり大きく(1000点×77%で770点分)、リーチによる打点上昇の影響よりも大きいということが表れている、のだと思います。

ただ、これは一人麻雀の結果であって、他家からのロンを一切考慮してない(特に今回はダマだと役がないので、リーチで1ハン縛りを解消してロン和了も狙うメリットが大きい)のはやっぱり問題ですね。
現状は他家からのロンをまだ実装できてないので、この結果はまぁしょうがないということにしましょう。

次は他家からのロンを加味させるのをやることにしましょう。
その前に、ベタ降り移行するアルゴリズムをただの「1シャンテン以上」からちゃんとしたものにしようかと思っています。

具体的には今自作AIのテスト相手である「manue」というAIの思考パターンをちょっと拝借しようかと思っています。
それは、他家の攻撃を一切考慮しない時の局収支(これは今の再帰式で求めることができる)と切ろうとしてる牌の危険度と放銃時失点(これもベタ降りプログラムで関数化している)の3つの数値から以下の値を計算して、
(1-危険度)×(他家無視局収支)-(危険度)×(放銃時失点)
この値が最大となる打牌を選ぶというアルゴリズムです。

これによって、最大限攻めるパターン(危険度は高いが他家無視局収支も高い)とベタ降り(危険度はほぼ0だが、他家無視局収支はノーテン罰符と同等レベルのマイナス点)とその中間パターンで一番よさそうなものが自動的に選ばれると思われます。

このアルゴリズムのいいところはそれぞれの打牌候補について再帰計算ができていれば、追加の計算時間はほぼ0で攻守のバランスがある程度考えられるという点です。

ただ、悪いところもあります(参考にさせていただいて自分で使う上での課題を述べているだけで、manueの悪口をことさら喧伝しようという意図ではないです)。
それは、
・被ツモを全く考慮してない。(本家manueの方に入ってるかどうかは不明)
・瞬間の危険度のみを考えているので、将来の他家の挙動の要素が全く入っていない
・鳴かれる可能性の多寡が入っていない?(これは未確認)
・点棒状況判断ができない。
ということです。

2つ目について補足すると、押した場合と降りた場合では平均局終了巡目が変わってくるのに、この巡目以降に引いてくる危険牌のことや被ツモのことを考えてないのはどうかということです。
一般には降りる方が局が長引きやすく、将来の失点が大きくなると考えられます。

このへんを全部考慮するにはシミュレーションで挙動を確認するのが一番いいのですが、
シミュレーションの欠点として、計算時間が余分に(それもそこそこのお時間)かかってしまうということです。
打牌候補が多いほど、線形的に計算時間は増えていくので、
現実的にはmanue方式で簡易的に瞬間的危険度を入れた局収支を計算した後、
上位2つの打牌候補とか、局収支が微差の打牌候補についてのみ、正式なシミュレーションをやるというのが落としどころでしょうかね。

どっちにしても他家からロン・鳴きの影響は重大なので近いうちに実装を試みます。
他家の将来の挙動についてはどうしたものかなぁと考えています。

再帰計算だと流局までツモれるのが前提としていて、これはあんまりよくないのですが、
他家状態(非リーチ面前・立直・副露)についてまで分類を増やして、全数探索で再帰計算すると同一局面が発生しにくくなって計算時間が爆発的に増えてしまうので、そういう方向性はとれません。

今プランとして考えているのは、現在の局面における他家状態の組み合わせに対して、自分が和了しないという条件付きでの局終了巡目と局終了時得失点(自分の和了がないのでだいたいマイナス)を実測値から測定して、その確率に応じて途中で局の打ち切りを行うというのを考えています。
うまくいくかはよくわかりません。

とまぁ、いろいろと考えるべきことはありますが、ちょっとずつ消化していきましょう。
プロフィール

nisi5028

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

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

この人とブロともになる

QRコード
QRコード