Skip to main content

mjai-simulator

mjai.app is a platform for mahjong AI competition. This repository contains the implementation of mahjong game simulator for evaluating submission files on mjai.app.

Installation

mjai package is available at the Python Package Index (PyPI). You can install it with pip command.

$ pip install mjai

Usage

You can simulate a mahjong game by specifying submission files as shown in the code below. The Simulator runs Docker internally. The docker command must be installed and be available to run with user privileges.

import mjai

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

This package also provides a base class for developing bots that communicate via the mjai protocol.

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 Image

Submission files are deployed in a Docker container and run as a Docker container.

This repository contains Dockerfile and other resources that will be used to create the Docker container. The docker image is pushed to Docker Hub (docker.io/smly/mjai-client:v3).

Submission format

Please prepare a program that outputs the appropriate mjai protocol message to the standard output when given input in the mjai protocol format from standard input. Name this program "bot.py" and pack it into a zip file. The zip file should contain bot.py directly under the root directory.

bot.py must be a Python script, but it is also possible to include precompiled libraries if they are executable. The program will be executed in a linux/amd64 environment. The submission file must be 1000 MB or less. bot.py takes a player ID as its first argument. Player ID must be 0, 1, 2, or 3. Player ID 0 represents the chicha 起家, and subsequent numbers represent the shimocha 下家, toimen 対面 or kamicha 上家 of the chicha 起家. See example code for details.

Timeout

When the mjai.Simulator instance creates an environment to run the submission file in docker, it specifies the --platform linux/amd64 option. If you want to run on a different architecture, you will have to emulate and run the container, which will be much slower. If you are debugging on an architecture other than linux/amd64, you can avoid timeout errors by relaxing the timeout limit. Specify the timeout argument as follows. The timeout is set to 2.0 by default.

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

If the simulation is interrupted, the docker container may not terminate successfully. If past docker containers remain, the new docker container may fail to launch due to duplicate HTTP ports. You can remove all docker containers that have the smly/mjai-client image as an ancestor in a batch as follows:

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

Mjai Protocol

Our Mjai protocol is basically based on the Gimite's original Mjai Protocol, but customized based on Mortal's Mjai Engine implementation. The following points are customized:

  • Messages are sent and received by standard input and standard output.
  • The game server sends messages collectively up to the event that the player can act on.
  • When the player does not send the appropriate event message, it is treated as a chombo and a mangan-sized penalty is paid.
  • If the player does not send a message within 2 seconds, it is treated as a chombo and a mangan-sized penalty is paid.
  • First round (kyoku) is not necessarily East 1.

First round is not necessarily East 1

If an error occurs in any one of the 4 players, the game is treated as a abortive draw (ryukyoku; 流局). At this time, all 4 players' containers are terminated. The players' containers are restarted and the game is resumed from the next round in which an error occurs. Therefore, the first round received by the player may not be East 1.

The following are messages sent and received as seen from player 0. <- denotes events for the player. -> denotes events from the player.

# 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}

Case Study: Furiten (振聴)

When the player has already made a tenpai but the hand is furiten. Since the player cannot Ron, even if the waiting tile is discarded by the opponent, no action is possible. For example, let's say you have 2333678m 678s 678p. The waiting tile is 14m and 1m has already been discarded. Since the hand is a Furiten, even if the other player discards 1m, the player cannot Ron.

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}]

In this case, immediately after actor 2 discards 6m, input is given to actor 3. actor 3 needs to decide whether to call chi on 6m.

Case Study: Ankan (暗槓)

In the case of an ankan, the dora event comes first, followed by the tsumo event.

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}

For Developers

Debug with interactive shell

The procedures executed by Simulator can be checked and debugged one by one as follows:

# 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

Debug with http sever

# 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}

Development

Confirmed working with rustc 1.75.0 (82e1608df 2023-12-21).

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

Special Thanks

The code in ./src directory is Mortal's libriichi with minor updates. Mortal is distributed under the AGPL-3.0 and is copyrighted by Equim.

Metadata

Release files for mjai 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mjai 0.2.1
File Size Uploaded
mjai-0.2.1.tar.gz 373.4 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for mjai 0.2.1
File
mjai-0.2.1-cp312-none-win_amd64.whl CPython 3.12 none Windows x86-64 Details
mjai-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.17+ x86-64 Details
mjai-0.2.1-cp312-cp312-macosx_11_0_arm64.whl CPython 3.12 CPython 3.12 macOS 11.0+ ARM64 Details
mjai-0.2.1-cp311-none-win_amd64.whl CPython 3.11 none Windows x86-64 Details
mjai-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.17+ x86-64 Details
mjai-0.2.1-cp311-cp311-macosx_11_0_arm64.whl CPython 3.11 CPython 3.11 macOS 11.0+ ARM64 Details
mjai-0.2.1-cp310-none-win_amd64.whl CPython 3.10 none Windows x86-64 Details
mjai-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.17+ x86-64 Details
mjai-0.2.1-cp310-cp310-macosx_11_0_arm64.whl CPython 3.10 CPython 3.10 macOS 11.0+ ARM64 Details

Total release size: 13.9 MB

Release files / mjai-0.2.1.tar.gz

Download URL mjai-0.2.1.tar.gz
Size 373.4 kB
Tags Source
SHA-256 checksum
How to use checksums
7678ef75265192e0bf475ffbea27129de1a4c4c496de4ce95783d9178d33fbbe
BLAKE2b-256 checksum
How to use checksums
db771298b7b1307ee150fb5f341248de517efcc48124ff8e5370c968648e1db4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Release files / mjai-0.2.1-cp312-none-win_amd64.whl

Download URL mjai-0.2.1-cp312-none-win_amd64.whl
Size 1.4 MB
Tags CPython 3.12 Windows x86-64
SHA-256 checksum
How to use checksums
327cdf462f20261b85be2f61bea1f1f713c10f20b037bde2e2b2e8d6e1597f12
BLAKE2b-256 checksum
How to use checksums
7ede77e59de167c851588d9d6c7521331b0d498ebd4db9d344e3b15456a626ea
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Release files / mjai-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL mjai-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 1.7 MB
Tags CPython 3.12 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
9e261bef81938fa122b79811412b770e361d00775335b00e3d55bd90a9a4fc88
BLAKE2b-256 checksum
How to use checksums
41932a0b0c1a1f02aff5ed7156c28ea21607a6c85c53ac250c2ec22cc7646706
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Release files / mjai-0.2.1-cp312-cp312-macosx_11_0_arm64.whl

Download URL mjai-0.2.1-cp312-cp312-macosx_11_0_arm64.whl
Size 1.5 MB
Tags CPython 3.12 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
14e8126a0370d74444a807ea46cb75f7b2f5e7f24124755e711c298285d82464
BLAKE2b-256 checksum
How to use checksums
e508a19b1cc890390ec45dee2e308efec753d9beffe007def3c00b730d586d6f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Release files / mjai-0.2.1-cp311-none-win_amd64.whl

Download URL mjai-0.2.1-cp311-none-win_amd64.whl
Size 1.4 MB
Tags CPython 3.11 Windows x86-64
SHA-256 checksum
How to use checksums
80e5f908e0a3ea86ae278d4a7d320babc7a608421f9442719f2a01e5d484ee3a
BLAKE2b-256 checksum
How to use checksums
73921dee7027fc3c44473769c888b24c9b3341e1cc4f5483f7c2e3322d513f9a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Release files / mjai-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL mjai-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 1.7 MB
Tags CPython 3.11 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
7e9bd89d0dfe8c00ecb1e157f889d4c2aa1098d21f3025dff5e434448e46667d
BLAKE2b-256 checksum
How to use checksums
05d53c95799b5f802a9706bb57d36fef8df1fbd67dd3ca9735136ca6e3c26aea
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Release files / mjai-0.2.1-cp311-cp311-macosx_11_0_arm64.whl

Download URL mjai-0.2.1-cp311-cp311-macosx_11_0_arm64.whl
Size 1.5 MB
Tags CPython 3.11 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
31a066f250df8538b8d7f4f833e7bc0f91baef23b58899f565e4dbc5ba8ed564
BLAKE2b-256 checksum
How to use checksums
b2997b248dba856f85aaef3301536bacd6c5046fb370ab9012530cbf0f001fb3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Release files / mjai-0.2.1-cp310-none-win_amd64.whl

Download URL mjai-0.2.1-cp310-none-win_amd64.whl
Size 1.4 MB
Tags CPython 3.10 Windows x86-64
SHA-256 checksum
How to use checksums
33853f451a7c5858c808f965d7180c20816a7302779f9f7331bc2f9d9e97dcd4
BLAKE2b-256 checksum
How to use checksums
66f17880c30bf840c1b610f599d955d8c2f908445d20b5a3be738065aa4acc28
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Release files / mjai-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL mjai-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 1.7 MB
Tags CPython 3.10 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
0eecddb86746eb48827f2d81f7dccd2fff28648ea9c388f8dd55ab4774e9e7f9
BLAKE2b-256 checksum
How to use checksums
30aa704ba4c2e4e4351834ffabde39a6d046f02e3f3a37cbb8936ad9cb7f4fbd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Release files / mjai-0.2.1-cp310-cp310-macosx_11_0_arm64.whl

Download URL mjai-0.2.1-cp310-cp310-macosx_11_0_arm64.whl
Size 1.5 MB
Tags CPython 3.10 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
f5ff6a389b76fd4a3bf4aff760c19c29475074cc483c95953bcf3df001d989c7
BLAKE2b-256 checksum
How to use checksums
612b563ecdb4e9ce39de5f5933dd9e1b58e01670381102b389205d89e501b447
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.12.4

Release history Release notifications | RSS feed

This release

0.2.1 This release

10 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page