AIのための雀荘mjai.appにMjxのAgentを投稿する
前回の記事で、そこそこ戦えそうな麻雀AIのAgentを作ることができました。このAgentをAI雀荘に投稿してみようと思います。
実装
AI雀荘(mjai.app)はMjxのAgentに対応しており、以下のリポジトリにMjxのサンプルが公開されています。
https://github.com/smly/mjai.app/tree/main/examples/shantenbot
このbot.pyを参考に、MjxのShantenAgentを使っているところを書き換えればよさそうです。
以前の記事で実装していたMLPAgentを使い、↓のように編集しました。
import json
import sys
import random
import torch
from torch import nn, Tensor
import mjx
from mjx import Agent, Observation, Action
from mjx.agents import ShantenAgent
from gateway import MjxGateway, to_mjai_tile
class MLP(nn.Module):
def __init__(self, obs_size=544, n_actions=181, hidden_size=544):
super().__init__()
self.net = nn.Sequential(
nn.Linear(obs_size, hidden_size),
nn.ReLU(),
nn.Linear(hidden_size, n_actions),
)
def forward(self, x):
return self.net(x.float())
class MLPAgent(Agent):
def __init__(self, model) -> None:
super().__init__()
self.model = model
def act(self, observation: Observation) -> Action:
legal_actions = observation.legal_actions()
try:
if len(legal_actions) == 1:
return legal_actions[0]
# 予測
feature = observation.to_features(feature_name="mjx-small-v0")
with torch.no_grad():
action_logit = self.model(Tensor(feature.ravel()))
action_proba = torch.sigmoid(action_logit).numpy()
# アクション決定
mask = observation.action_mask()
action_idx = (mask * action_proba).argmax()
return mjx.Action.select_from(action_idx, legal_actions)
except:
return random.choice(legal_actions)
def main():
model = MLP()
model.load_state_dict(torch.load('./model.pth'))
agent = MLPAgent(model)
player_id = int(sys.argv[1])
assert player_id in range(4)
bot = MjxGateway(player_id, agent)
while True:
line = sys.stdin.readline().strip()
resp = bot.react(line)
sys.stdout.write(resp + "\n")
sys.stdout.flush()
if __name__ == "__main__":
main()
アップロード
編集したbot.pyと、モデルファイル、gateway.py(サンプルそのまま)を以下のように同じフォルダにおいてzipファイルを作ります。
親フォルダを圧縮するのではなく、解凍したら直下にこの3ファイルが見えている状態にする必要があります。Windowsの場合、3ファイルを同時に選択して右クリック→送る→圧縮(zip形式)フォルダーでOKです。
zipファイルを作成したら、https://mjai.app/でtwitter連携してサインアップし、アップロードを行います。
提出後、4つのテストケースが走り、これらに合格するとAccepted状態となりました。
【余談】エラー時のローカルでの動作確認方法
zipアップロード時にテストが通らないとき、ローカルで動作確認をする方法を書いておきます。
提出が通らない場合、このような表示になります。
test case1(対局開始時のケース)でエラーになったことがわかります。
(原因はzip作成時にフォルダごと圧縮してしまっていて、解凍するとフォルダができてしまうことだったのですが、備忘的に手順を残しておきます)
動作確認には、まずmjxの0.1.0を動かすためのLinux環境と、gateway.pyで使われている構文をサポートするpython3.10以上が必要です。
colabではpythonのバージョンが低く動きそうになかったので、Windows上にdocker環境を構築して動作確認しました。
まず Docker Desktop for Windows をインストールします。
次に、ここからDockerfileとrequrements.txtを取得します。
https://github.com/smly/mjai.app/tree/main/docker
この2ファイルをフォルダに配置し、そのフォルダでdocker buildコマンドを使いイメージを作成します。
docker build -t docker-whale .
このコマンドの中で、Dockerfileに書かれているPython-3.10.5のインストールやmjxのインストールが行われるため、1時間程度かかりました。
イメージができたらコンテナを起動し、提出したファイルを置きます。
Windows端末のPowerShellを使い、docker psでコンテナIDを調べ、docker cpでローカルのファイルをコンテナ上にコピーしました。
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a32da4ddc41d docker-whale:latest "bash" 34 minutes ago Up 34 minutes cool_feistel
> docker cp bot.py a32da4ddc41d:/workspace/
> docker cp gateway.py a32da4ddc41d:/workspace/
> docker cp model.pth a32da4ddc41d:/workspace/
次に、Docker DesktopからコンテナのCLIを起動し、環境の状態を確認します。
# bash
root@a32da4ddc41d:~# python -V
Python 3.10.5
root@a32da4ddc41d:~# ls /opt
mjx
pythonとmjxが入っていることが確認できました。
bot.pyを実行すると標準入力を待つ状態になるので、
https://github.com/smly/mjai.app/blob/main/verify_submission.py
に書いてある動作確認用の入力を入れてみます。
> python bot.py 0
[{"type":"start_game","id":0}]
{"type":"none"}
bot.pyに、プレイヤーIDとして0を与えて起動しています
標準入力に[{"type":"start_game","id":0}]
を与えると{"type":"none"}が出力されています(期待値通り)
さらにdo_test_2に書かれている文字列を入力すると、DEBUGログが出力された後に打牌の文字列が出力されています。
[{"type":"start_kyoku","bakaze":"E","kyoku":1,"honba":0,"kyotaku":0,"oya":0,"dora_marker":"7s","scores":[2500,2500,2500,2500],"tehais":[["3m","4m","3p","5pr","7p","9p","4s","4s","5sr","7s","7s","W","N"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?","?","?","?","?","?","?","?","?","?"]]},{"type":"tsumo","actor":0,"pai":"3m"}]
2022-10-09 16:40:27.278 | DEBUG | gateway:_get_mjx_obs:428 - [DEBUG] start_kyoku: [8, 12, 44, 52, 60, 68, 84, 85, 88, 97, 98, 116, 120]
2022-10-09 16:40:27.279 | DEBUG | gateway:_get_mjx_obs:461 - - apply tsumo event {'type': 'tsumo', 'actor': 0, 'pai': '3m'} (9)
2022-10-09 16:40:28.278 | DEBUG | gateway:_get_mjx_obs:637 - - action: {"tile":8}
2022-10-09 16:40:28.279 | DEBUG | gateway:_get_mjx_obs:637 - - action: {"type":"ACTION_TYPE_TSUMOGIRI","tile":9}
2022-10-09 16:40:28.279 | DEBUG | gateway:_get_mjx_obs:637 - - action: {"tile":12}
2022-10-09 16:40:28.279 | DEBUG | gateway:_get_mjx_obs:637 - - action: {"tile":44}
2022-10-09 16:40:28.280 | DEBUG | gateway:_get_mjx_obs:637 - - action: {"tile":52}
2022-10-09 16:40:28.280 | DEBUG | gateway:_get_mjx_obs:637 - - action: {"tile":60}
2022-10-09 16:40:28.280 | DEBUG | gateway:_get_mjx_obs:637 - - action: {"tile":68}
2022-10-09 16:40:28.281 | DEBUG | gateway:_get_mjx_obs:637 - - action: {"tile":84}
2022-10-09 16:40:28.281 | DEBUG | gateway:_get_mjx_obs:637 - - action: {"tile":88}
2022-10-09 16:40:28.281 | DEBUG | gateway:_get_mjx_obs:637 - - action: {"tile":97}
2022-10-09 16:40:28.282 | DEBUG | gateway:_get_mjx_obs:637 - - action: {"tile":116}
2022-10-09 16:40:28.282 | DEBUG | gateway:_get_mjx_obs:637 - - action: {"tile":120}
{"type":"dahai","actor":0,"pai":"N","tsumogiri":false}
このようにして、作成したbot.pyの動作確認を行うことができます。
この記事が気に入ったらサポートをしてみませんか?