*All archives* |  *Admin*

<<01  2017/02  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  03>>
経過報告
ここ数日くらいあんまりやる気が出なくて、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つの打牌候補とか、局収支が微差の打牌候補についてのみ、正式なシミュレーションをやるというのが落としどころでしょうかね。

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

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

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

とまぁ、いろいろと考えるべきことはありますが、ちょっとずつ消化していきましょう。
floodgate for mahjong へ参戦その7・ベタ降りプログラムと再帰式牌効率プログラムの実戦投入
攻撃面は2シャンテン以下は再帰計算、3シャンテン以上は1次有効牌が一番多い棒テンAIということで、
鳴きはできないけど、一応最低限は打てるようになったので、
今回は守備面を考えます。

まずは簡単のためにリーチがかかったとき、1シャンテン以上ならベタ降り、というシンプルなアルゴリズムにしてみます。

ベタ降りプログラムの中身は具体的なテスト風景を見ながら話をしたほうがよいでしょう。

ログ1
http://gimite.net/mjai/log/2017-02-16-223414.mjson.html

ログ2
http://gimite.net/mjai/log/2017-02-16-224639.mjson.html

ログ2の9巡目、親リーを受けた一発目に全く通ってない赤5pを切ってしまってます。
170216-01.png
中身を取り出してみると、
170216-02.png
手に持ってる全種の牌の中から3者それぞれに対して、危険度(放銃率)とその牌が当たった時の平均失点をパラメータから取ってきて、
(危険度)×(平均放銃失点)の3人分の和が最も最小になる牌を選んで切ります。
(なお、実際の放銃率については3人分の和を取るのは厳密には誤りで、正確には1-(1-下家への放銃率)×(1-対面への放銃率)×(1-上家への放銃率)になりますが、天鳳ルールだとダブロンがあって、放銃が重複したときは二人に対して点棒を支払うので、むしろ単純な加算のほうが良いと判断しました。)

この場合はリーチの現物の9m切りが正しい(赤5p以外の中では実際に(危険度)×(平均放銃失点)の3人分の和が最小になってる)のですが、
なんか赤関連の危険度でバグがあるのか、危険度が0になっているので、赤5pを誤って切ってしまってます。

ここ以外は降りに関しては今のところ想定通りに動いているので、赤関連バグだけ直してもうちょっとテストを続けてみることにします。
floodgate for mahjong へ参戦その6・一人麻雀再帰計算関数の作り直し
ただの棒テン即リー全ツッパAIから一人麻雀計算機のときに作った再帰計算による打牌選択をさせるように試みてみます。

今回は受け入れ枚数の多寡ではなく、手役や最終的な和了率まで考慮した局収支が最も多い打牌を選択させるようにします。
計算時間の問題で、シャンテン数ごとに
・聴牌…ダマ(手替わり1回までと途中で立直に切り替えも考慮)と即リーチと聴牌取らず(再聴牌する有効牌が最も多い1打のみ)
・1シャンテン…手替わり1回で再帰計算(シャンテン戻し考慮せず)
・2シャンテン…手替わり0回で再帰計算(シャンテン戻し考慮せず)
・3シャンテン以上→基本的には棒テン即リー全ツッパAI。ただし、国士チートイがらみは面子手シャンテン数との兼ね合いで別途場合分け。

という規則で計算させます。

プログラムの大枠は一人麻雀計算機のときに作っていましたが、後々いろいろと融通が利くようにグローバル変数をなるべく使わない方式にするために引数とか関数の使い方をいろいろと作り替えました。

とりあえずそれっぽいのができたのでテストしてみます。
牌姿は1m3m4m5m6m3p4p5p8p8p3s4s5s6s。
親の1巡目。(ただし、ダブリーは考慮しない。)
を、入力値として、最適打が出力値として出るようにします。

聴牌なので、設計通りなら3sか6s切ってダマ聴牌取り、即リーチ、聴牌外し(今回は一番広い1m切り)の中で最も局収支がいいものが出力値として出てきます。
今回はあからさまに聴牌外しが有利になるような牌姿(打1mでタンピン三色狙い)にしておきました。

デバッグ開始。
170215-01.png
一応、目論見通り、結果は「1」(牌番号1は1m)になっていますが、けっこう計算時間が遅いです。
およそ10秒超。
まぁ複数打牌候補でそれぞれダマとリーチと聴牌外しをやってるのでこのくらいは想定はしていたけど、まだツモしか見てない段階でこれはけっこうつらい。

各パーツごとにかかった時間も一緒に測定してみました。
分け方は↓のときとだいたい同じ。
一人麻雀計算機その9・テストと改善その2
3番(局収支とかの記憶検索)と6番(打牌候補選定)で時間を食っています。

ここをなんとかするか…といろいろと省力化のために知恵を絞ってみました。

・記憶検索について、検索のキーにしてるのがString型(「1m3m4m5m6m3p4p5p8p8p3s4s5s6s」とか)にしてるのを整数のLong型に変換してみた。
37種の牌から14枚を取る組み合わせ、ということでパッと思い浮かんだのが重複組み合わせ(nHr=(n+r-1)Cr)です。
14個の○と37個の仕分け線/の50個の図形を並べた組み合わせの数です。
○//○/○/○/○///…
1m1枚、2m0枚、3m1枚、4m1枚、5m1枚、6m1枚、7m0枚…
50C14で10の11乗くらいなのでLong型(8バイト)あれば手牌1式(String型orInteger型配列)と整数1個が1対1対応できる、と思います。(実際は同じ牌を5枚以上使えないので手牌として適さない○/列もありますが、数え上げやすさ重視で。)
この○と/の列を0と1に置き換えてビット演算子によってLong型の整数にすればString型で検索するよりは速度が上がるかなーと。
この数え方だと2^50=10^15乗まで膨れるが、なんとかぎりぎりLong型で収まる。

・それによって文字列を使う必要がなくなったので、手牌のInteger配列を文字列に直す工程を削除(デバッグでのチェックが見づらくなったが、速度には代えられない。)

・打牌候補選定のためのシャンテンチェックも一度計算した手牌は再計算しないで記憶情報から引っ張ってくる方式に変更。

そうしていろいろ改善した結果がこちら。
170215-02.png
4秒まで縮めることができました。
これなら今のところは使用に耐えるか。

もうしばらくいろんな牌姿を入力してテストをやっていきます。
floodgate for mahjong へ参戦その5・国士チートイ偏重対策
単なる棒テン即リー全ツッパAIだと、チートイのシャンテン数<面子手のシャンテン数のときに、
暗刻から1枚切ったり、場に出てる牌を切ってシュンツを壊してしまうケースが散見されるという課題を前回挙げました。

同じことは国士に対しても言えます。
配牌がバラバラのときに、国士のシャンテン数が最も低かった時に国士決め打ちで初手赤5切りみたいなのがありました。

このあたりを改善するために、面子手・チートイ・国士のシャンテン数ごとに場合分けをして、微妙にアルゴリズムを変えてみます。

具体的なチャートは以下の通りです。

○国士シャンテン数について(国士のシャンテン数が最小のとき)
 国士2シャンテン以下→国士決め打ちで真ん中牌・ドラから切り出す。

 国士3シャンテン
  ・チートイシャンテン数
   チートイ3シャンテン
    面子手3シャンテン→1次有効牌が最も多い打牌
    面子手4シャンテン→対子・暗刻以外の牌で面子手4シャンテンをキープできる打牌(国士はみない)
    面子手5シャンテン以上→チートイに決め打ち(対子暗刻ドラ以外の牌で場に多く切れてる牌or真ん中牌)(国士は見ない)
   チートイ4シャンテン→面子手にしぼる(面子手1次有効牌が最も多い打牌)(国士はみない)
   チートイ5シャンテン以上かつ面子手5シャンテン以上→国士決め打ちで真ん中牌・ドラから切り出す。

 国士4シャンテン以上→面子手にしぼる(面子手1次有効牌が最も多い打牌)(国士はみない)

○チートイシャンテン数について(チートイのシャンテン数が最小のとき)
 チートイ1シャンテン以下
  面子手1シャンテン以下→1次有効牌が最も多い打牌
  面子手2シャンテン以上→チートイに決め打ち(対子暗刻ドラ以外の牌で場に多く切れてる牌or真ん中牌)
 
 チートイ2シャンテン
  面子手2シャンテン→1次有効牌が最も多い打牌
  面子手3シャンテン→対子・暗刻以外の牌で面子手3シャンテンをキープできる打牌
  面子手4シャンテン→チートイに決め打ち(対子暗刻ドラ以外の牌で場に多く切れてる牌or真ん中牌)

 チートイ3シャンテン
  面子手3シャンテン→1次有効牌が最も多い打牌
  面子手4シャンテン→対子・暗刻以外の牌で面子手4シャンテンをキープできる打牌
  面子手5シャンテン→チートイに決め打ち(対子暗刻ドラ以外の牌で場に多く切れてる牌or真ん中牌)

 チートイ4シャンテン以上→面子手にしぼる(面子手1次有効牌が最も多い打牌)

(チャート終わり)


一言で言えば「面子手優先・遠い国士チートイはみない」です。

とりあえずこのように変えてみて何戦か打たせたところ、一見まともそうな挙動になりました。

まぁ場合分けが多くてかなり大変でしたけど、多少プログラムの行数が増えてもビルド時間の増加とexeファイルのサイズがちょっと増えるだけで処理速度は多分落ちないなので、まぁよいことにします。アウトライン機能で非表示にまとめればコードの可読性もそこまで悪くないですし。

再帰計算の関数を作るのはまだこれからです。ぼちぼちがんばりましょう。
floodgate for mahjong へ参戦その4・棒テン即リー全ツッパAIを投入
今日は前に作った麻雀ゲームから棒テン即リー全ツッパAIのところを移植して、サーバーと通信させるところまで成功しました。

何戦かテストで打たせてみて、打牌選択、リーチ宣言、ツモ和了、ロン和了が今のところうまくいってるのを確認。

↓ログの一例
http://gimite.net/mjai/log/2017-02-10-172058.mjson.html
http://gimite.net/mjai/log/2017-02-10-173004.mjson.html
http://gimite.net/mjai/log/2017-02-10-173847.mjson.html

ログを眺めていると棒テン即リー全ツッパAIの弱さがよくわかるなぁ。
シャンテン数と1次有効牌の残り枚数のみが判断材料なので、メンツ手シャンテン数>チートイシャンテン数になってるときに、そうそうに七対子に決め打って、暗刻やシュンツを崩すケースが頻発しています。
あとは頭が他にない時に両面対子を早い段階で頭固定するケースもでてきてます。

まぁ、棒テンを忠実に再現していて、単純なCPUらしいといえばらしいですが。

次はちゃんと期待値計算をするプログラムをAI化するところですね。
棒テン即リー全ツッパAIは麻雀ゲームを作るときに、グローバル変数をなるべく使用しない設計にしてあるけれど、
ちゃんとした四麻計算機はそういう処理をまだやってなくて、関数の設計のやり直しから入らないといけないから完成まではちょっと時間がかかりそうな気がします。

あとは処理速度的な問題をどうするかを考えないといけません。
とりあえず最初は他家を考慮しない再帰パートのみでシミュレーションパートはやらない(つまり鳴きと他家攻撃は全く考えない全ツッパAI)、ということと
情報の更新タイミングをなるべく減らして計算時間を節約するというのを考えています。

情報の更新タイミングを減らすというのは具体的には
シャンテン数が進む有効牌について、
・対子・暗刻の牌が他家から出た時
・0枚持ち・1枚持ちの有効牌で2枚目以降が見えた時
に限って最適打テーブルを再計算するということです。

逆にそれ以外のケースについてはそこまで場況(見えてる枚数)の情報が増えてないということなので、前の巡目で計算した最適打テーブル(当該巡目以降のあらゆる手牌についての最適打情報がすべて載っている)をそのまま活用するということです。
さすがに2枚目以降が見えた時はそこの塔子・対子の受け入れがかなり弱くなると思われるので、増えた情報により最適打が変わってくるケースが出てくるので、再計算をした方がいいと思っています。

後は現在のシャンテン数ごとに計算方式を変えるということですね。
・3シャンテン以上…棒テン即リー全ツッパAIをそのまま使う。(ただし、七対子偏重を防ぐための仕組みは何かしら考える。)
・2シャンテン…手替わり回数0として一人麻雀の期待値計算。シャンテン戻しは考慮しない。
・1シャンテン…手替わり回数1として一人麻雀の期待値計算。シャンテン戻しは(最初は)考慮しない。
・聴牌…ダマとリーチのそれぞれを計算していい方を採用。シャンテン戻しも1番受け入れの広くなる1打のみに限って考慮する。
こんな感じでしょうか。聴牌についてはちょっとコードを書き直す必要がありそうです。
とりあえずこれでやってみますか。
floodgate for mahjong へ参戦その3・AI製作の目的とか
今日は残りの命令(鳴き・和了とか)について受け取った文字列を内部変数に落とし込むところまで終わりました。

棒テン即リー全ツッパAIまでは既存関数のコピペでだいたいなんとかなるでしょう。たぶん明日くらいにはできる。


今日はどうして麻雀AIを作ろうと思ったかをつらつらと書いていきます。

麻雀AIといえば、みなさんご存じのとおり、「爆打」というすごく強いAIがすでに存在しています。
私ごときの技術力では「爆打」には到底かなわないと思っているので、
よく将棋とか囲碁とかポーカーとかで話題になってるとおり、
「人間を超えるソフトを作る」とか「最強のAI」とかにはあまり関心はないです。
そこまでたどりつく自信は全くないので。

関心がある、言い換えればAIを作る一番の目的は、
「似たようなソフトどうしの成績を比較して、勝ちやすい戦術を探りたい」ということです。

例えば立直判断なら「先制愚形立直のみは絶対打たないソフト」とそういうこだわりのない「普通のソフト」(局収支or試合単位収支を単純に最大化する戦略をとる。)の両方を多数試合(できればさまざまなAI相手に)やらせて、統計的に有意な差が出ればうれしい、もしくは有意な差が出てくるまで試合数を重ねる(AI相手なら多数試合が比較的容易と思われる)みたいなことができればいいなぁと思っています。

「先制愚形立直のみは絶対打たないソフト」のアルゴリズムをどう作るかですが、
単純に役なしドラなしになったら強制的にダマ聴牌にさせるとか、
もっと根本的に、愚形立直のみの評価値(局収支)を下げることによって手作りの段階からでも愚形立直のみを避ける手組(シャンテン戻しなど)をさせるなど、
まぁ方策はないわけではないです。

他にも、もっと重要なところが押し引きのラインですね。
もちろん、計算上押しと降りを比較して、数値が良い方を選ぶということはできますが、
さっきと同じようにどちらかの戦略に数値を補正することで、ふつうより押し気味AIとふつうより降り気味AIみたいなのも作るのは可能です。
補正のしかたも、一律で押しにプラス点とかマイナス点させるとか、受け入れ枚数・高さなどに応じて補正点を変えるとかいろいろやりかたがあります。(例えば、良形なら単純な収支よりもかなり押すけど、愚形になったらかなり引き気味になるとか。)

そういうのをいろいろ比較してこっちの戦術の方が勝ちやすいみたいなのが見えてくれば面白いかなーとは思っています。

もちろん、対人間でやってるわけではないので、「そんなもんに意味はない」と批判されるかもしれませんが、まぁそのへんは自己満足的なところですね。批判の声は一応聞きはするけど、まぁ深く考えずに受け流すみたいな。

まぁ後は技術的・計算時間的な問題ですかね。
愚形立直のみをどうするかというのは実際目にする局面はそう多くはない(感覚的には数試合に1回あるかどうか。)上、結果に重大な影響が出る可能性が低い(局収支的には拮抗してるわけですし。)ので、結果が収束する→統計的に有意差が出るまでかなりの試合数が必要になることが予想されます。

1試合5分で終わるとすると、1000試合で5000分(≒80時間≒3日)、10000試合で30日…。
けっこう大変そうですね。10000試合でも十分かどうかはかなり怪しいですし、
そもそも1試合5分で終わるかどうかもかなり過小評価な気がするし。

そうすると、現状の計算機ベースだと時間的にだいぶ苦しいですね。どこかで省力化(計算の省略とか)は必要になりそう。
まぁそのへんはおいおい考えていきましょうか。
うまくAIが作れたら高性能のパソコンでも買おうか。

あとはそうですね。大量の牌譜の収集とかもできそうですね。もちろん人間同士の対局よりかは価値は落ちるとは思いますし、現状でも大概の研究をするには十分なサンプルサイズがあるので、どうしても必要というわけではないですが、AIの傾向を調べるという上でもまぁあってもいいかな、と。

それをするには自前でサーバー機能を付ける必要があったりとか、牌譜出力機能が必要とかはでてきそうですが、まぁそれもある程度うまくいってから考えようか。自前のAI同士の対局ならわざわざjson形式で文字をやり取りせずとも、前に作ったゲーム機能の延長線上でなんとかなりそうだし。

それと、染め手移行のアルゴリズム(パラメータ取りも含めて。)ずっと放置してやってないからそっちもやらないとなぁ。
染め手も重要と言えば重要ですけど、優先順位的にはふつうのAI作りよりは下かなぁ。


つらつらーと書いていったらなんかそれっぽい文章になった気がする。まぁこれからもいつものペースでこつこつがんばりましょうか。
floodgate for mahjong へ参戦その2・経過報告
前回、GitHub(https://github.com/mahjong-server/mahjong-server)で公開されてるツモ切りプレイヤーのコードを修正・加筆することにより、floodgate for mahjongで自作AIを戦わせようと思ったのですが、
接続に成功したのは1回だけで、それっきり接続できなくなってしまいました。

また、Visual Studioをエディタとして使ってるのですが、なぜかコンパイルが通らない(Cygwinでは通るのだが…)ので、まったくデバッグができないという事態になっています。

なので、C++のコードを手直しするのを諦めて、普段使ってる環境(VB.NET)で新しく一からTCP通信・json形式のプロトコル対応・AI本体を作ることにしました。

言語はVBで、いつものフォームアプリケーションではなく、コマンドプロンプトとかで動くコンソールアプリケーションを選択。
ほとんど文法はいつもと同じなので、安心。

System.Net.SocketsクラスのTcpClient関数でTCP通信をやってみることにします。
すると、
ホスト名「http://mjai.hocha.org/」、ポート番号「11600」だと「拒否」と返されて通信できない。うむー。
そこからいろいろと調べて、練習用の「gimite.net」、ポート番号「11600」、room「manue-1kyoku」というサーバー相手だと文字列の送受信ができることを確認しました。

READMEに書いてる
http://www.logos.t.u-tokyo.ac.jp/mjlog/
(試合結果が載ってるところ)
もなんか503エラー(Service Unavailable:The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.)
で閲覧できないので、なんか向こうのサーバー側の不具合なのかなぁ。

まぁ一応練習用のところにはつなげるし、まだjson形式プロトコル対応とかAI部分の移植とかも全然できてないので、今のところはこの問題は放置することにします。


それで単純なツモ切りプレイヤーとして、サーバーと交互に文字列を送りあうところまでは行きました。

170208-01b.png
「hello」と「join」で最初に通信できるかを確認して「start_game」「start_kyoku」で試合開始・局開始。
その後「tsumo」と「dahai」を繰り返し(ツモ切りなので、自分の打牌順以外はただ「none」を送るだけ)、
最後に「hora」「end_kyoku」「end_game」で終わる、と。

一応、通信は成功している。

それで、今は次の段階に入っています。
前に作った四麻計算機・麻雀ゲームから変数や関数を移植したうえで、
json形式で受け取った文字列を各種内部変数(手牌とか捨て牌とか)に反映させるという作業です。

プロトコルのサンプルを見ながら各命令に対して文字列の読み取って変数に1個1個落とし込むという感じです。
今のところ「hello」「join」「start_game」「start_kyoku」「tsumo」「dahai」「reach」「reach_accepted」まで終わりました。
後は鳴きと和了と終了関係なので、今で半分くらいかなぁ。

そこまでいったら後は麻雀ゲームのときに作った関数を確認しながらコピー&ペーストして必要に応じて修正を加える感じなので、棒テン即リー全ツッパAIまでならそこまで時間はかからないかなー。

後はAIが完成するまでにサーバーが復活してくれるといいなぁ。


あんまり関係ない話だけど、最近、任意の場所でコードを折りたたんで非表示にする機能(アウトライン化)を発見しました。
自分のコード設計パターンとして、あまり関数を小分けにはせず、1個の関数内で細かい処理まで全部書いていく方式にしてるのが多いので、
1個の関数を表示するとそれが何百行と続いてスクロールするのがめんどいとかがあったので、
アウトライン機能でif文とかfor文とかの中身を細かく非表示にさせてまとめることで、コードの中身がちょっと見やすくなった…かもしれない。
floodgate for mahjong へ参戦その1・ツモ切りプレイヤーで接続
麻雀ゲームとして動くものができたので、
今回からはfloodgate for mahjongという麻雀AIどうしの対局場でAIを戦わせることを目指します。

GitHubから最新のものを落とす。
https://github.com/mahjong-server/mahjong-server
ふつうのコマンドプロンプトを使うと#pragma commentが認識できないらしく、なんかうまくいかない。
この前インストールしたCygwinでtsumogiriディレクトリでmakeコマンドを入れる。
コンパイルが通った!やった!(ほとんど自分は何もやってないけど。)

出てきた実行ファイルを動かす。
コマンドラインでそれっぽい文字が出てくる。
http://www.logos.t.u-tokyo.ac.jp/mjlog/mjlog/2017-02-05-154501.mjson
http://www.logos.t.u-tokyo.ac.jp/mjlog/mjlog/viewer.php?mjson=2017-02-05-154501.mjson
無事、対局終了までいった。(もちろん全部ツモ切りなのでラスだけど。)

これから実際に専用の書式に従ってAIを組んでいくのですが、
今までのプログラミングの環境がVB.NETでやっていたのに対し、
このfloodgate for mahjongに接続するためのプログラムはC++で書かれています。
接続部分をVB.NETで自作して作るのがいいのか、AI部分をC++で書くのがいいのか…。

接続部分がどういう仕組みになってるのかはまだ全く解析してなくて、何をやってるのかさっぱりわからんのに対し、
AI部分は今までの実績(麻雀ゲーム作り)から考えて、C++を全くやったことないことを差し引いても、
おそらく全く先が見えないということはない(たぶん2週間もあれば今の棒テン即リー全ツッパAIくらいならVB.NETからの変換はできそう。)ので、
C++の勉強も兼ねてAI部分をC++で作ってみようかなー、と思っています。

とりあえずよくわからんサーバーとの通信に関するところが解決できたので一安心である。
またC++の参考書を借りてこよう。
プロフィール

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

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

この人とブロともになる

QRコード
QRコード