跳转到主要内容

mjai.app上评估提交文件的麻将游戏模拟器

项目描述

mjai-simulator

mjai.app 是一个麻将人工智能竞赛平台。此存储库包含在mjai.app上评估提交文件的麻将游戏模拟器实现。

安装

mjai 软件包可在Python包索引(PyPI)中找到。您可以使用 pip 命令安装它。

$ pip install mjai

使用

您可以通过指定以下代码中的提交文件来模拟麻将游戏。模拟器内部运行 Docker。必须安装并可用以用户权限运行 docker 命令。

import mjai

submissions = [
    "examples/shanten.zip",
    "examples/tsumogiri.zip",
    "examples/tsumogiri.zip",
    "examples/invalidbot2.zip",
]
mjai.Simulator(submissions, logs_dir="./logs").run()

此软件包还提供了一个用于开发通过mjai协议通信的机器人的基类。

from mjai import Bot


class RulebaseBot(Bot):
    def think(self) -> str:
        if self.can_tsumo_agari:
            return self.action_tsumo_agari()
        elif self.can_ron_agari:
            return self.action_ron_agari()
        elif self.can_riichi:
            return self.action_riichi()

        ...

if __name__ == "__main__":
    RulebaseBot(player_id=int(sys.argv[1])).start()

Docker镜像

提交文件部署在Docker容器中,并以Docker容器的形式运行。

此存储库包含用于创建Docker容器的Dockerfile和其他资源。Docker镜像已推送到Docker Hub( docker.io/smly/mjai-client:v3)。

提交格式

请准备一个程序,当从标准输入接收mjai协议格式的输入时,将其输出为适当的 mjai协议消息 到标准输出。将此程序命名为 "bot.py" 并将其打包到zip文件中。zip文件应包含位于根目录下的bot.py。

bot.py 必须是 Python 脚本,但也可能包含可执行的预编译库。程序将在 linux/amd64 环境中执行。提交文件大小必须小于或等于 1000 MB。bot.py 的第一个参数是玩家 ID。玩家 ID 必须是 0、1、2 或 3。玩家 ID 0 代表起家,后续数字代表下家、对面或上家。有关详细信息,请参阅 示例代码

超时

mjai.Simulator 实例在 docker 中创建运行提交文件的执行环境时,它指定了 --platform linux/amd64 选项。如果您想在不同的架构上运行,您将不得不模拟并运行容器,这将非常慢。如果您在除 linux/amd64 以外的架构上调试,您可以通过放宽超时限制来避免超时错误。以下是如何指定 timeout 参数。默认情况下,timeout 设置为 2.0。

Simulator(submissions, logs_dir="./logs", timeout=10.0).run()

如果模拟被中断,docker 容器可能无法成功终止。如果以前的 docker 容器仍然存在,新的 docker 容器可能由于重复的 HTTP 端口而无法启动。您可以通过以下方式批量删除具有 smly/mjai-client 映像作为祖先的所有 docker 容器

% for cid in `docker ps -a --filter ancestor=smly/mjai-client:v3 --format "{{.ID}}"`; do docker rm -f $cid; done

Mjai 协议

我们的 Mjai 协议基本上基于Gimite 的原始 Mjai 协议,但基于Mortal 的 Mjai 引擎实现进行了定制。以下是一些定制点

  • 消息通过标准输入和标准输出发送和接收。
  • 游戏服务器发送消息,直到玩家可以采取行动的事件。
  • 当玩家没有发送适当的事件消息时,它被视为 chombo,并支付 mangan 大小的罚款。
  • 如果玩家在 2 秒内没有发送消息,它被视为 chombo,并支付 mangan 大小的罚款。
  • 第一轮(局)不一定是东 1。

第一轮不一定是东 1

如果任何一位玩家出现错误,则游戏被视为中止和局(流局)。在这种情况下,所有四位玩家的容器都会终止。玩家的容器将重新启动,并从发生错误的下一轮开始继续游戏。因此,玩家收到的第一轮可能不是东 1。

以下是从玩家 0 观察到的发送和接收的消息。 <- 表示玩家的事件。 -> 表示来自玩家的事件。

# Game resumed from S1-1
0 <- [{"type":"start_game","names":["0","1","2","3"],"id":0}]
0 -> {"type":"none"}

# NOTE: No ryukyoku events are sent from the game server.
0 <- [{"type":"end_kyoku"}]
0 -> {"type":"none"}

# S2-2 first tsumo tile
0 <- [{"type":"start_kyoku","bakaze":"S","dora_marker":"1p","kyoku":2,"honba":2,"kyotaku":0,"oya":1,"scores":[800,61100,11300,26800],"tehais":[["4p","4s","P","3p","1p","5s","2m","F","1m","7s","9m","6m","9s"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?","?","?","?","?","?","?","?","?","?"]]},{"type":"tsumo","actor":1,"pai":"?"},{"type":"dahai","actor":1,"pai":"F","tsumogiri":false},{"type":"tsumo","actor":2,"pai":"?"},{"type":"dahai","actor":2,"pai":"3m","tsumogiri":true},{"type":"tsumo","actor":3,"pai":"?"},{"type":"dahai","actor":3,"pai":"1m","tsumogiri":true},{"type":"tsumo","actor":0,"pai":"3s"}]
0 -> {"type":"dahai","pai":"3s","actor":0,"tsumogiri":true}

案例研究:振听(振聴)

当玩家已经做出十番,但手牌处于振听状态。由于玩家不能隆,即使对手弃掉等待的牌,也无法采取行动。例如,假设你有 2333678m 678s 678p。等待的牌是 14m1m 已经被弃掉。由于手牌处于振听状态,即使其他玩家弃掉 1m,玩家也不能隆。

3 <- [{"type":"dahai","actor":3,"pai":"P","tsumogiri":true},{"type":"tsumo","actor":0,"pai":"?"},{"type":"dahai","actor":0,"pai":"2p","tsumogiri":true},{"type":"tsumo","actor":1,"pai":"?"},{"type":"dahai","actor":1,"pai":"4m","tsumogiri":true},{"type":"tsumo","actor":2,"pai":"?"},{"type":"dahai","actor":2,"pai":"6m","tsumogiri":true}]

在这种情况下,在演员 2 弃掉 6m 后,立即为演员 3 输入。演员 3 需要决定是否对 6m 进行 chi 叫牌。

案例研究:暗杠(暗槓)

在暗杠的情况下,先出现 dora 事件,然后是摸牌事件。

3 -> {"type": "ankan", "actor": 3, "consumed": ["6s", "6s", "6s", "6s"]}
3 <- [{"type":"ankan","actor":3,"consumed":["6s","6s","6s","6s"]},{"type":"dora","dora_marker":"6p"},{"type":"tsumo","actor":3,"pai":"7p"}]
3 -> {"type":dahai","actor":3,"pai":"7p","tsumogiri":true}

对于开发者

使用交互式壳调试

Simulator 执行的进程可以逐一检查和调试,如下所示

# pull latest docker image
% docker pull docker.io/smly/mjai-client:v3

# launch
% CONTAINER_ID=`docker run -d --rm -p 28080:3000 --mount "type=bind,src=/Users/smly/gitws/mjai.app/examples/rulebase.zip,dst=/bot.zip,readonly" smly/mjai-client:v3 sleep infinity`

# install bot program
% docker exec ${CONTAINER_ID} unzip -q /bot.zip

# debug
% docker exec -it ${CONTAINER_ID} /workspace/.pyenv/shims/python -u bot.py 0
[{"type":"start_game","id":0}]  <-- Input
{"type":"none"}  <-- Output
[{"type":"start_kyoku","bakaze":"E","dora_marker":"2s","kyoku":1,"honba":0,"kyotaku":0,"oya":0,"scores":[25000,25000,25000,25000],"tehais":[["E","6p","9m","8m","C","2s","7m","S","6m","1m","S","3s","8m"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?","?","?","?","?","?","?","?","?","?"]]},{"type":"tsumo","actor":0,"pai":"1m"}]  <-- Input
{"type": "dahai", "actor": 0, "pai": "C", "tsumogiri": false}  <-- Output
[{"type":"dahai","actor":0,"pai":"C","tsumogiri":false},{"type":"tsumo","actor":1,"pai":"?"},{"type":"dahai","actor":1,"pai":"3m","tsumogiri":false},{"type":"tsumo","actor":2,"pai":"?"},{"type":"dahai","actor":2,"pai":"1m","tsumogiri":false}]  <-- Input
{"type": "none"}  <-- Output
[{"type":"tsumo","actor":3,"pai":"?"},{"type":"dahai","actor":3,"pai":"1m","tsumogiri":false}]  <-- Input
{"type": "none"}  <-- Output
[{"type":"tsumo","actor":0,"pai":"C"}]  <-- Input
{"type": "dahai", "actor": 0, "pai": "C", "tsumogiri": true}  <-- Output

使用 http 服务器调试

# install http server interface of mjai protocol
% docker cp python/mjai/http_server/server.py ${CONTAINER_ID}:/workspace/00__server__.py

# http server mode. `0` is the player index.
% docker exec -it ${CONTAINER_ID} /workspace/.pyenv/shims/python 00__server__.py 0
% curl http://localhost:28080/
OK

% curl -X POST -d '[{"type":"start_game","id":0}]' http://localhost:28080/
{"type":"none"}

% cat > request.json
[{"type":"start_kyoku","bakaze":"E","dora_marker":"2s","kyoku":1,"honba":0,"kyotaku":0,"oya":0,"scores":[25000,25000,25000,25000],"tehais":[["E","6p","9m","8m","C","2s","7m","S","6m","1m","S","3s","8m"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?","?","?","?","?","?","?","?","?","?"],["?","?","?","?","?","?","?","?","?","?","?","?","?"]]},{"type":"tsumo","actor":0,"pai":"1m"}]

% curl -X POST -d '@request.json' http://localhost:28080/
{"type":"dahai","actor":0,"pai":"6p","tsumogiri":false}

开发

已确认与 rustc 1.75.0 (82e1608df 2023-12-21) 一起工作。

$ pip install maturin  # install build tool
$ maturin build --release --locked --target aarch64-apple-darwin --out dist

特别感谢

./src 目录中的代码是 Mortal 的 libriichi,仅进行了少量更新。Mortal 根据 AGPL-3.0 发布,并受 Equim 版权保护。

项目详细信息


下载文件

下载适用于您平台的应用程序。如果您不确定选择哪个,请了解有关 安装包 的更多信息。

源分发

mjai-0.2.1.tar.gz (373.4 kB 查看哈希值)

上传时间

构建分发

mjai-0.2.1-cp312-none-win_amd64.whl (1.4 MB 查看哈希值)

上传时间 CPython 3.12 Windows x86-64

mjai-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.7 MB 查看哈希值)

上传时间 CPython 3.12 manylinux: glibc 2.17+ x86-64

mjai-0.2.1-cp312-cp312-macosx_11_0_arm64.whl (1.5 MB 查看哈希值)

上传时间 CPython 3.12 macOS 11.0+ ARM64

mjai-0.2.1-cp311-none-win_amd64.whl (1.4 MB 查看哈希值)

上传时间 CPython 3.11 Windows x86-64

mjai-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.7 MB 查看哈希值)

上传时间 CPython 3.11 manylinux: glibc 2.17+ x86-64

mjai-0.2.1-cp311-cp311-macosx_11_0_arm64.whl (1.5 MB 查看哈希值)

上传时间 CPython 3.11 macOS 11.0+ ARM64

mjai-0.2.1-cp310-none-win_amd64.whl (1.4 MB 查看哈希值)

上传时间 CPython 3.10 Windows x86-64

mjai-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.7 MB 查看哈希值)

上传时间 CPython 3.10 manylinux: glibc 2.17+ x86-64

mjai-0.2.1-cp310-cp310-macosx_11_0_arm64.whl (1.5 MB 查看哈希值)

上传于 CPython 3.10 macOS 11.0+ ARM64

支持者:

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面