Skip to main content

Mahjong game simulator for evaluating submission files on mjai.app

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mjai-0.2.1.tar.gz (373.4 kB view details)

Uploaded Source

Built Distributions

mjai-0.2.1-cp312-none-win_amd64.whl (1.4 MB view details)

Uploaded CPython 3.12 Windows x86-64

mjai-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.7 MB view details)

Uploaded CPython 3.12 manylinux: glibc 2.17+ x86-64

mjai-0.2.1-cp312-cp312-macosx_11_0_arm64.whl (1.5 MB view details)

Uploaded CPython 3.12 macOS 11.0+ ARM64

mjai-0.2.1-cp311-none-win_amd64.whl (1.4 MB view details)

Uploaded CPython 3.11 Windows x86-64

mjai-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.7 MB view details)

Uploaded CPython 3.11 manylinux: glibc 2.17+ x86-64

mjai-0.2.1-cp311-cp311-macosx_11_0_arm64.whl (1.5 MB view details)

Uploaded CPython 3.11 macOS 11.0+ ARM64

mjai-0.2.1-cp310-none-win_amd64.whl (1.4 MB view details)

Uploaded CPython 3.10 Windows x86-64

mjai-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.7 MB view details)

Uploaded CPython 3.10 manylinux: glibc 2.17+ x86-64

mjai-0.2.1-cp310-cp310-macosx_11_0_arm64.whl (1.5 MB view details)

Uploaded CPython 3.10 macOS 11.0+ ARM64

File details

Details for the file mjai-0.2.1.tar.gz.

File metadata

  • Download URL: mjai-0.2.1.tar.gz
  • Upload date:
  • Size: 373.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.0 CPython/3.12.4

File hashes

Hashes for mjai-0.2.1.tar.gz
Algorithm Hash digest
SHA256 7678ef75265192e0bf475ffbea27129de1a4c4c496de4ce95783d9178d33fbbe
MD5 77b196add969ebdbd057f83ebfc38ffe
BLAKE2b-256 db771298b7b1307ee150fb5f341248de517efcc48124ff8e5370c968648e1db4

See more details on using hashes here.

File details

Details for the file mjai-0.2.1-cp312-none-win_amd64.whl.

File metadata

  • Download URL: mjai-0.2.1-cp312-none-win_amd64.whl
  • Upload date:
  • Size: 1.4 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.0 CPython/3.12.4

File hashes

Hashes for mjai-0.2.1-cp312-none-win_amd64.whl
Algorithm Hash digest
SHA256 327cdf462f20261b85be2f61bea1f1f713c10f20b037bde2e2b2e8d6e1597f12
MD5 f81cffe38f4738689e2f683980b9c0e3
BLAKE2b-256 7ede77e59de167c851588d9d6c7521331b0d498ebd4db9d344e3b15456a626ea

See more details on using hashes here.

File details

Details for the file mjai-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for mjai-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 9e261bef81938fa122b79811412b770e361d00775335b00e3d55bd90a9a4fc88
MD5 de73e14ecdcb5a323d82d33cdeebe2fb
BLAKE2b-256 41932a0b0c1a1f02aff5ed7156c28ea21607a6c85c53ac250c2ec22cc7646706

See more details on using hashes here.

File details

Details for the file mjai-0.2.1-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for mjai-0.2.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 14e8126a0370d74444a807ea46cb75f7b2f5e7f24124755e711c298285d82464
MD5 66577de64c98e4cc6f084f60020b99c4
BLAKE2b-256 e508a19b1cc890390ec45dee2e308efec753d9beffe007def3c00b730d586d6f

See more details on using hashes here.

File details

Details for the file mjai-0.2.1-cp311-none-win_amd64.whl.

File metadata

  • Download URL: mjai-0.2.1-cp311-none-win_amd64.whl
  • Upload date:
  • Size: 1.4 MB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.0 CPython/3.12.4

File hashes

Hashes for mjai-0.2.1-cp311-none-win_amd64.whl
Algorithm Hash digest
SHA256 80e5f908e0a3ea86ae278d4a7d320babc7a608421f9442719f2a01e5d484ee3a
MD5 65e60f849fcde2c05d3b2e27447b1e8a
BLAKE2b-256 73921dee7027fc3c44473769c888b24c9b3341e1cc4f5483f7c2e3322d513f9a

See more details on using hashes here.

File details

Details for the file mjai-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for mjai-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7e9bd89d0dfe8c00ecb1e157f889d4c2aa1098d21f3025dff5e434448e46667d
MD5 2261d06595b956833c0d2c757e3337e4
BLAKE2b-256 05d53c95799b5f802a9706bb57d36fef8df1fbd67dd3ca9735136ca6e3c26aea

See more details on using hashes here.

File details

Details for the file mjai-0.2.1-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for mjai-0.2.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 31a066f250df8538b8d7f4f833e7bc0f91baef23b58899f565e4dbc5ba8ed564
MD5 16c7889d0c0830e2e5c1839644f4914f
BLAKE2b-256 b2997b248dba856f85aaef3301536bacd6c5046fb370ab9012530cbf0f001fb3

See more details on using hashes here.

File details

Details for the file mjai-0.2.1-cp310-none-win_amd64.whl.

File metadata

  • Download URL: mjai-0.2.1-cp310-none-win_amd64.whl
  • Upload date:
  • Size: 1.4 MB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.0 CPython/3.12.4

File hashes

Hashes for mjai-0.2.1-cp310-none-win_amd64.whl
Algorithm Hash digest
SHA256 33853f451a7c5858c808f965d7180c20816a7302779f9f7331bc2f9d9e97dcd4
MD5 03c4f73c49c88a3fde399a229adca10e
BLAKE2b-256 66f17880c30bf840c1b610f599d955d8c2f908445d20b5a3be738065aa4acc28

See more details on using hashes here.

File details

Details for the file mjai-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for mjai-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 0eecddb86746eb48827f2d81f7dccd2fff28648ea9c388f8dd55ab4774e9e7f9
MD5 199a0a0b03d585a1f2dd45911702612e
BLAKE2b-256 30aa704ba4c2e4e4351834ffabde39a6d046f02e3f3a37cbb8936ad9cb7f4fbd

See more details on using hashes here.

File details

Details for the file mjai-0.2.1-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for mjai-0.2.1-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 f5ff6a389b76fd4a3bf4aff760c19c29475074cc483c95953bcf3df001d989c7
MD5 fbebe7a68cab234d66b5d9aa9972cef7
BLAKE2b-256 612b563ecdb4e9ce39de5f5933dd9e1b58e01670381102b389205d89e501b447

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page