Mjai 麻雀AI対戦サーバ
をテンプレートにして作成
開始行:
麻雀AI同士を対戦させることができるサーバです。
* 試してみる [#ubc82c22]
- Rubyをインストール。
- RubyGemsをインストール。
$ sudo gem install json
$ wget https://raw.github.com/gimite/mjai/master/samples/tsumogiri_player.rb
$ ruby tsumogiri_player.rb mjsonp://gimite.net:11600/manue-1kyoku
ここで指定したサーバでは、Manueという開発中のAI(現状あまり強くない) 3人を相手に1局戦ができます。
テストにご自由にお使いください。途中で切っても大丈夫です。 http://gimite.net/mjai/log/ にログが出力されます。サーバが落ちていたらGimiteに教えて下さい。
* プロトコル [#l062a700]
クライアント(プレーヤ)4人がTCPでサーバに接続します。その後、サーバ、クライアントが交互にJSONを1行ずつ送り合います。1ゲーム終わるとTCPの接続を切断します。
** 牌の表記 [#id97a227]
- 数牌: 1m, 2m, ..., 9m, 1p, 2p, ..., 9p, 1s, 2s, ..., 9s
- 字牌: E(東), S(南), W(西), N(北), P(白), F(發), C(中)
- 赤牌: 5mr, 5pr, 5sr
** 通信内容のサンプル [#kf490f44]
サンプルとしてこのゲーム(牌譜ビューア)での各プレーヤの通信内容がそれぞれ0、1、2、3となっています。
以下適当に抜粋。"<-"がサーバからのメッセージ、"->"がクライアントからのメッセージです。
ゲーム開始、配牌、その後1巡の摸打:
<- {"type":"hello","protocol":"mjsonp","protocol_version":1}
-> {"type":"join","name":"shanten","room":"default"}
<- {"type":"start_game","id":1,"names":["shanten","shanten","shanten","shanten"]}
# ここでのid属性が、このゲーム中での自分のIDになります。
-> {"type":"none"}
<- {"type":"start_kyoku","bakaze":"E","kyoku":1,"honba":0,"kyotaku":0,"oya":0,"dora_marker":"7s","tehais":[["?","?","?","?","?","?","?","?","?","?","?","?","?"],["3m","4m","3p","5pr","7p","9p","4s","4s","5sr","7s","7s","W","N"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?","?","?","?","?","?","?","?","?","?"]]}
# ここでのtehaisなど、配列になっているものはID 0, 1, 2, 3のものが順に入ります。親から順ではないので注意。
-> {"type":"none"}
<- {"type":"tsumo","actor":0,"pai":"?"}
-> {"type":"none"}
<- {"type":"dahai","actor":0,"pai":"6s","tsumogiri":false}
-> {"type":"none"}
<- {"type":"tsumo","actor":1,"pai":"3m"}
-> {"type":"dahai","actor":1,"pai":"7s","tsumogiri":false}
<- {"type":"dahai","actor":1,"pai":"7s","tsumogiri":false}
-> {"type":"none"}
<- {"type":"tsumo","actor":2,"pai":"?"}
-> {"type":"none"}
<- {"type":"dahai","actor":2,"pai":"S","tsumogiri":false}
-> {"type":"none"}
<- {"type":"tsumo","actor":3,"pai":"?"}
-> {"type":"none"}
<- {"type":"dahai","actor":3,"pai":"F","tsumogiri":false}
-> {"type":"none"}
ポン:
<- {"type":"dahai","actor":1,"pai":"5sr","tsumogiri":false}
-> {"type":"pon","actor":0,"target":1,"pai":"5sr","consumed":["5s","5s"]}
<- {"type":"pon","actor":0,"target":1,"pai":"5sr","consumed":["5s","5s"]}
-> {"type":"dahai","actor":0,"pai":"9p","tsumogiri":false}
<- {"type":"dahai","actor":0,"pai":"9p","tsumogiri":false}
-> {"type":"none"}
チー:
<- {"type":"dahai","actor":3,"pai":"4p","tsumogiri":true}
-> {"type":"chi","actor":0,"target":3,"pai":"4p","consumed":["5p","6p"]}
<- {"type":"chi","actor":0,"target":3,"pai":"4p","consumed":["5p","6p"]}
-> {"type":"dahai","actor":0,"pai":"9m","tsumogiri":false}
<- {"type":"dahai","actor":0,"pai":"9m","tsumogiri":false}
-> {"type":"none"}
加槓:
<- {"type":"tsumo","actor":0,"pai":"6m"}
-> {"type":"kakan","actor":0,"pai":"6m","consumed":["6m","6m","6m"]}
<- {"type":"kakan","actor":0,"pai":"6m","consumed":["6m","6m","6m"]}
-> {"type":"none"}
<- {"type":"tsumo","actor":0,"pai":"1p"}
-> {"type":"dahai","actor":0,"pai":"2p","tsumogiri":false}
<- {"type":"dora","dora_marker":"3s"}
-> {"type":"none"}
<- {"type":"dahai","actor":0,"pai":"2p","tsumogiri":false}
-> {"type":"none"}
大明槓:
<- {"type":"dahai","actor":1,"pai":"5m","tsumogiri":false}
-> {"type":"daiminkan","actor":3,"target":1,"pai":"5m","consumed":["5m","5m","5mr"]}
<- {"type":"daiminkan","actor":3,"target":1,"pai":"5m","consumed":["5m","5m","5mr"]}
-> {"type":"none"}
<- {"type":"tsumo","actor":3,"pai":"S"}
-> {"type":"dahai","actor":3,"pai":"S","tsumogiri":true}
<- {"type":"dora","dora_marker":"8p"}
-> {"type":"none"}
<- {"type":"dahai","actor":3,"pai":"S","tsumogiri":true}
-> {"type":"none"}
暗槓:
<- {"type":"tsumo","actor":1,"pai":"N"}
-> {"type":"ankan","actor":1,"consumed":["N","N","N","N"]}
<- {"type":"ankan","actor":1,"consumed":["N","N","N","N"]}
-> {"type":"none"}
<- {"type":"dora","dora_marker":"4s"}
-> {"type":"none"}
<- {"type":"tsumo","actor":1,"pai":"3p"}
-> {"type":"dahai","actor":1,"pai":"6s","tsumogiri":false}
<- {"type":"dahai","actor":1,"pai":"6s","tsumogiri":false}
-> {"type":"none"}
リーチ:
<- {"type":"tsumo","actor":1,"pai":"2m"}
-> {"type":"reach","actor":1}
<- {"type":"reach","actor":1}
-> {"type":"dahai","actor":1,"pai":"7s","tsumogiri":false}
<- {"type":"dahai","actor":1,"pai":"7s","tsumogiri":false}
-> {"type":"none"}
<- {"type":"reach_accepted","actor":1,"deltas":[0,-1000,0,0],"scores":[28000,23000,24000,24000]}
-> {"type":"none"}
<- {"type":"tsumo","actor":2,"pai":"?"}
-> {"type":"none"}
ツモあがり:
<- {"type":"tsumo","actor":2,"pai":"2m"}
-> {"type":"hora","actor":2,"target":2,"pai":"2m"}
<- {"type":"hora","actor":2,"target":2,"pai":"2m","uradora_markers":["8p"],"hora_tehais":["1m","3m","5m","6m","7m","1p","2p","3p","4p","5pr","6p","W","W","2m"],"yakus":[["akadora",1],["reach",1],["menzenchin_tsumoho",1]],"fu":30,"fan":3,"hora_points":4000,"deltas":[-2100,-1100,6300,-1100],"scores":[25900,21900,29300,22900]}
-> {"type":"none"}
<- {"type":"end_kyoku"}
-> {"type":"none"}
ロンあがり:
<- {"type":"dahai","actor":0,"pai":"7s","tsumogiri":true}
-> {"type":"hora","actor":1,"target":0,"pai":"7s"}
<- {"type":"hora","actor":1,"target":0,"pai":"7s","uradora_markers":["3s"],"hora_tehais":["2m","3m","4m","4p","5pr","6p","6p","7p","8p","6s","8s","N","N"],"yakus":[["akadora",1],["reach",1]],"fu":40,"fan":2,"hora_points":2600,"deltas":[-4400,8400,0,0],"scores":[27500,22300,24300,25900]}
-> {"type":"none"}
<- {"type":"end_kyoku"}
-> {"type":"none"}
流局:
<- {"type":"ryukyoku","reason":"fanpai","tehais":[["5m","5m","5mr","3s","3s","N","N"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?"]],"tenpais":[true,false,false,false],"deltas":[3000,-1000,-1000,-1000],"scores":[28000,24000,24000,24000]}
-> {"type":"none"}
<- {"type":"end_kyoku"}
-> {"type":"none"}
不明な点は@gimiteを捕まえて聞いてください。
* 自分でサーバを動かすには [#ja5f02a2]
** インストール [#tb349d5b]
- Ruby 1.9以上をインストール。Debian/Ubuntuならたぶん:
$ sudo apt-get install ruby1.9.1-dev
- mjaiをインストール。
$ sudo gem1.9.1 install mjai
** 開発用にサーバを動かす [#a9ba1ab6]
$ mjai server --port=11600 --game_type=one_kyoku --room=default --log_dir=./log mjai-shanten mjai-shanten mjai-shanten
自前のAIをポート11600に接続して {"type":"join","name":"xxxx","room":"default"} を送ると、mjai-shanten(単純なAI)3人相手の1局戦が開始します。
** 評価用にサーバを動かす [#qe314d2d]
$ mjai server --port=11600 --game_type=tonnan --games=100 --room=default --log_dir=./log \
./my-ai.sh ./my-ai.sh mjai-shanten mjai-shanten
とすると内部で
$ ./my-ai.sh mjsonp://localhost:11600/default
を繰り返し実行し、mjai-shantenとの東南戦を100戦行います。
* ルール [#kf5b3165]
天鳳の喰い断あり赤ありルール互換になる…予定です。現状色々未実装なルールがあります。
* 牌譜ビューア [#j9cea9bf]
$ mjai convert 2012-04-30-215246.mjson 2012-04-30-215246.html
などとすることで、ログをHTML形式の牌譜ビューアに変換できます。今のところたぶんChromeでしか動きません。
* 安定度について [#jc79db35]
プロトコルはあまりいじらないつもりですが、問題を見つけたら大きく変わる可能性もあります。RubyでAIを書く場合はmjai内のクラスを使うと便利かもしれませんが、Rubyのクラス構成やインタフェースはガンガン変わります。
* 天鳳のログの解析 [#ie6f4f1b]
[[天鳳の強い人2人の全牌譜:http://blog.tenhou.net/article/46797689.html]]が公開されています。天鳳のログ(.mjlog)はmjai convertで.mjsonに変換できます。また、Mjai::Archiveクラスで読めます。
* Mjai上で動作するAI [#o50b5fcb]
- [[mjai-manue:https://github.com/gimite/mjai-manue]] (by Gimite)
- [[mjai-silica:https://github.com/wistery-k/mjai-silica]] (by wistery-kさん)
- [[mjai-shanten:http://d.hatena.ne.jp/wistery_k/20121102/1351845850]] (mjai gem付属)
-- シャンテン数が最小になる打牌の中からランダムで選ぶ。
- mjai-tsumogiri (mjai gem付属)
-- ツモ切りするだけ。
* 関連ツール [#o1d1fafd]
- mjai用GUIクライアント (by wistery-kさん)
* ソースコード [#p7409501]
- [[Github - gimite/mjai:https://github.com/gimite/mjai]]
* 関連記事 [#e55f2c33]
[[統計による麻雀危険牌分析]]
終了行:
麻雀AI同士を対戦させることができるサーバです。
* 試してみる [#ubc82c22]
- Rubyをインストール。
- RubyGemsをインストール。
$ sudo gem install json
$ wget https://raw.github.com/gimite/mjai/master/samples/tsumogiri_player.rb
$ ruby tsumogiri_player.rb mjsonp://gimite.net:11600/manue-1kyoku
ここで指定したサーバでは、Manueという開発中のAI(現状あまり強くない) 3人を相手に1局戦ができます。
テストにご自由にお使いください。途中で切っても大丈夫です。 http://gimite.net/mjai/log/ にログが出力されます。サーバが落ちていたらGimiteに教えて下さい。
* プロトコル [#l062a700]
クライアント(プレーヤ)4人がTCPでサーバに接続します。その後、サーバ、クライアントが交互にJSONを1行ずつ送り合います。1ゲーム終わるとTCPの接続を切断します。
** 牌の表記 [#id97a227]
- 数牌: 1m, 2m, ..., 9m, 1p, 2p, ..., 9p, 1s, 2s, ..., 9s
- 字牌: E(東), S(南), W(西), N(北), P(白), F(發), C(中)
- 赤牌: 5mr, 5pr, 5sr
** 通信内容のサンプル [#kf490f44]
サンプルとしてこのゲーム(牌譜ビューア)での各プレーヤの通信内容がそれぞれ0、1、2、3となっています。
以下適当に抜粋。"<-"がサーバからのメッセージ、"->"がクライアントからのメッセージです。
ゲーム開始、配牌、その後1巡の摸打:
<- {"type":"hello","protocol":"mjsonp","protocol_version":1}
-> {"type":"join","name":"shanten","room":"default"}
<- {"type":"start_game","id":1,"names":["shanten","shanten","shanten","shanten"]}
# ここでのid属性が、このゲーム中での自分のIDになります。
-> {"type":"none"}
<- {"type":"start_kyoku","bakaze":"E","kyoku":1,"honba":0,"kyotaku":0,"oya":0,"dora_marker":"7s","tehais":[["?","?","?","?","?","?","?","?","?","?","?","?","?"],["3m","4m","3p","5pr","7p","9p","4s","4s","5sr","7s","7s","W","N"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?","?","?","?","?","?","?","?","?","?"]]}
# ここでのtehaisなど、配列になっているものはID 0, 1, 2, 3のものが順に入ります。親から順ではないので注意。
-> {"type":"none"}
<- {"type":"tsumo","actor":0,"pai":"?"}
-> {"type":"none"}
<- {"type":"dahai","actor":0,"pai":"6s","tsumogiri":false}
-> {"type":"none"}
<- {"type":"tsumo","actor":1,"pai":"3m"}
-> {"type":"dahai","actor":1,"pai":"7s","tsumogiri":false}
<- {"type":"dahai","actor":1,"pai":"7s","tsumogiri":false}
-> {"type":"none"}
<- {"type":"tsumo","actor":2,"pai":"?"}
-> {"type":"none"}
<- {"type":"dahai","actor":2,"pai":"S","tsumogiri":false}
-> {"type":"none"}
<- {"type":"tsumo","actor":3,"pai":"?"}
-> {"type":"none"}
<- {"type":"dahai","actor":3,"pai":"F","tsumogiri":false}
-> {"type":"none"}
ポン:
<- {"type":"dahai","actor":1,"pai":"5sr","tsumogiri":false}
-> {"type":"pon","actor":0,"target":1,"pai":"5sr","consumed":["5s","5s"]}
<- {"type":"pon","actor":0,"target":1,"pai":"5sr","consumed":["5s","5s"]}
-> {"type":"dahai","actor":0,"pai":"9p","tsumogiri":false}
<- {"type":"dahai","actor":0,"pai":"9p","tsumogiri":false}
-> {"type":"none"}
チー:
<- {"type":"dahai","actor":3,"pai":"4p","tsumogiri":true}
-> {"type":"chi","actor":0,"target":3,"pai":"4p","consumed":["5p","6p"]}
<- {"type":"chi","actor":0,"target":3,"pai":"4p","consumed":["5p","6p"]}
-> {"type":"dahai","actor":0,"pai":"9m","tsumogiri":false}
<- {"type":"dahai","actor":0,"pai":"9m","tsumogiri":false}
-> {"type":"none"}
加槓:
<- {"type":"tsumo","actor":0,"pai":"6m"}
-> {"type":"kakan","actor":0,"pai":"6m","consumed":["6m","6m","6m"]}
<- {"type":"kakan","actor":0,"pai":"6m","consumed":["6m","6m","6m"]}
-> {"type":"none"}
<- {"type":"tsumo","actor":0,"pai":"1p"}
-> {"type":"dahai","actor":0,"pai":"2p","tsumogiri":false}
<- {"type":"dora","dora_marker":"3s"}
-> {"type":"none"}
<- {"type":"dahai","actor":0,"pai":"2p","tsumogiri":false}
-> {"type":"none"}
大明槓:
<- {"type":"dahai","actor":1,"pai":"5m","tsumogiri":false}
-> {"type":"daiminkan","actor":3,"target":1,"pai":"5m","consumed":["5m","5m","5mr"]}
<- {"type":"daiminkan","actor":3,"target":1,"pai":"5m","consumed":["5m","5m","5mr"]}
-> {"type":"none"}
<- {"type":"tsumo","actor":3,"pai":"S"}
-> {"type":"dahai","actor":3,"pai":"S","tsumogiri":true}
<- {"type":"dora","dora_marker":"8p"}
-> {"type":"none"}
<- {"type":"dahai","actor":3,"pai":"S","tsumogiri":true}
-> {"type":"none"}
暗槓:
<- {"type":"tsumo","actor":1,"pai":"N"}
-> {"type":"ankan","actor":1,"consumed":["N","N","N","N"]}
<- {"type":"ankan","actor":1,"consumed":["N","N","N","N"]}
-> {"type":"none"}
<- {"type":"dora","dora_marker":"4s"}
-> {"type":"none"}
<- {"type":"tsumo","actor":1,"pai":"3p"}
-> {"type":"dahai","actor":1,"pai":"6s","tsumogiri":false}
<- {"type":"dahai","actor":1,"pai":"6s","tsumogiri":false}
-> {"type":"none"}
リーチ:
<- {"type":"tsumo","actor":1,"pai":"2m"}
-> {"type":"reach","actor":1}
<- {"type":"reach","actor":1}
-> {"type":"dahai","actor":1,"pai":"7s","tsumogiri":false}
<- {"type":"dahai","actor":1,"pai":"7s","tsumogiri":false}
-> {"type":"none"}
<- {"type":"reach_accepted","actor":1,"deltas":[0,-1000,0,0],"scores":[28000,23000,24000,24000]}
-> {"type":"none"}
<- {"type":"tsumo","actor":2,"pai":"?"}
-> {"type":"none"}
ツモあがり:
<- {"type":"tsumo","actor":2,"pai":"2m"}
-> {"type":"hora","actor":2,"target":2,"pai":"2m"}
<- {"type":"hora","actor":2,"target":2,"pai":"2m","uradora_markers":["8p"],"hora_tehais":["1m","3m","5m","6m","7m","1p","2p","3p","4p","5pr","6p","W","W","2m"],"yakus":[["akadora",1],["reach",1],["menzenchin_tsumoho",1]],"fu":30,"fan":3,"hora_points":4000,"deltas":[-2100,-1100,6300,-1100],"scores":[25900,21900,29300,22900]}
-> {"type":"none"}
<- {"type":"end_kyoku"}
-> {"type":"none"}
ロンあがり:
<- {"type":"dahai","actor":0,"pai":"7s","tsumogiri":true}
-> {"type":"hora","actor":1,"target":0,"pai":"7s"}
<- {"type":"hora","actor":1,"target":0,"pai":"7s","uradora_markers":["3s"],"hora_tehais":["2m","3m","4m","4p","5pr","6p","6p","7p","8p","6s","8s","N","N"],"yakus":[["akadora",1],["reach",1]],"fu":40,"fan":2,"hora_points":2600,"deltas":[-4400,8400,0,0],"scores":[27500,22300,24300,25900]}
-> {"type":"none"}
<- {"type":"end_kyoku"}
-> {"type":"none"}
流局:
<- {"type":"ryukyoku","reason":"fanpai","tehais":[["5m","5m","5mr","3s","3s","N","N"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?"]],"tenpais":[true,false,false,false],"deltas":[3000,-1000,-1000,-1000],"scores":[28000,24000,24000,24000]}
-> {"type":"none"}
<- {"type":"end_kyoku"}
-> {"type":"none"}
不明な点は@gimiteを捕まえて聞いてください。
* 自分でサーバを動かすには [#ja5f02a2]
** インストール [#tb349d5b]
- Ruby 1.9以上をインストール。Debian/Ubuntuならたぶん:
$ sudo apt-get install ruby1.9.1-dev
- mjaiをインストール。
$ sudo gem1.9.1 install mjai
** 開発用にサーバを動かす [#a9ba1ab6]
$ mjai server --port=11600 --game_type=one_kyoku --room=default --log_dir=./log mjai-shanten mjai-shanten mjai-shanten
自前のAIをポート11600に接続して {"type":"join","name":"xxxx","room":"default"} を送ると、mjai-shanten(単純なAI)3人相手の1局戦が開始します。
** 評価用にサーバを動かす [#qe314d2d]
$ mjai server --port=11600 --game_type=tonnan --games=100 --room=default --log_dir=./log \
./my-ai.sh ./my-ai.sh mjai-shanten mjai-shanten
とすると内部で
$ ./my-ai.sh mjsonp://localhost:11600/default
を繰り返し実行し、mjai-shantenとの東南戦を100戦行います。
* ルール [#kf5b3165]
天鳳の喰い断あり赤ありルール互換になる…予定です。現状色々未実装なルールがあります。
* 牌譜ビューア [#j9cea9bf]
$ mjai convert 2012-04-30-215246.mjson 2012-04-30-215246.html
などとすることで、ログをHTML形式の牌譜ビューアに変換できます。今のところたぶんChromeでしか動きません。
* 安定度について [#jc79db35]
プロトコルはあまりいじらないつもりですが、問題を見つけたら大きく変わる可能性もあります。RubyでAIを書く場合はmjai内のクラスを使うと便利かもしれませんが、Rubyのクラス構成やインタフェースはガンガン変わります。
* 天鳳のログの解析 [#ie6f4f1b]
[[天鳳の強い人2人の全牌譜:http://blog.tenhou.net/article/46797689.html]]が公開されています。天鳳のログ(.mjlog)はmjai convertで.mjsonに変換できます。また、Mjai::Archiveクラスで読めます。
* Mjai上で動作するAI [#o50b5fcb]
- [[mjai-manue:https://github.com/gimite/mjai-manue]] (by Gimite)
- [[mjai-silica:https://github.com/wistery-k/mjai-silica]] (by wistery-kさん)
- [[mjai-shanten:http://d.hatena.ne.jp/wistery_k/20121102/1351845850]] (mjai gem付属)
-- シャンテン数が最小になる打牌の中からランダムで選ぶ。
- mjai-tsumogiri (mjai gem付属)
-- ツモ切りするだけ。
* 関連ツール [#o1d1fafd]
- mjai用GUIクライアント (by wistery-kさん)
* ソースコード [#p7409501]
- [[Github - gimite/mjai:https://github.com/gimite/mjai]]
* 関連記事 [#e55f2c33]
[[統計による麻雀危険牌分析]]
ページ名: