Skip to content

Mjai Protocol for 4-Player

API Request Body Formats

For /mjai/start, it is the first request required before sending any Mjai events to initialize the bot. In the request body, id is the current player ID, an integer between 0 and 3 inclusive (which will be used to distinguish the current player in the subsequent queries to /mjai/act and /mjai/batch). bound is a non-negative integer that will be used later unless being 0. model is the optional selected model for the bot; if not provided, a default model will be used, and the specific model used will be shown in the API response.

For /mjai/act and /mjai/batch, the request body formats are based on the Mjai protocol, originally by Gimite. Specifically, for each /mjai/act request, the JSON object has two fields: seq and data. seq is a monotonically increasing number (optionally taking modulus by bound used at /mjai/act to start the bot), and data is a Mjai event. The request body of /mjai/batch should be an array of JSON objects with the same format of that of /mjai/act, allowing clients to send all events since the last action at once.

Rules of bound and seq

To ensure synchronization between the server and the clients, every Mjai event has an accompanying seq, a monotonically increasing sequence number up to bound. The range of seq is from 0 to 65535 inclusive, so the bound provided at the bot start can be useful to allow wrap around of seq by taking modulus of bound.

If bound is positive, the first event after starting the bot will have seq as 0, the next will have seq as 1, etc. The events can be sent out of order, no matter between requests or within a batch of events in a request to /mjai/batch. In this mode, you cannot resend the same request, because the server cannot distinguish such events from those with wrapped around sequence numbers.

If bound is 0, the first event will have seq as 1, and the seq of following events should be monotonically increasing without taking any modulus. The events can be sent out of order as well. However, since the maximum value of seq is 65535, the client must request /mjai/start again before the sequence number reaching this limit. On the other hand, clients can resend the same events any number of times, and the last cached action will be returned in the response if the latest sequence numbers match. Notice that if clients send different events with the same sequence number, the behavior is undefined, which means that the server may or may not use the updated events, whether errors are returned or not.

Mjai Events in Requests

For Mjai events used in data attribute inside request bodies, only some events are useful and others are simply ignored. The useful events include start_kyoku, tsumo, dahai, chi, pon, daiminkan, kakan, ankan, dora, reach, and reach_accepted. The useless ones include none, start_game, hora, ryukyoku, end_kyoku, end_game. The server will return errors on all other invalid event types. Notice that hora and ryukyoku can be valid actions returned in the API response, but these are ignored in the request body as events.

start_game is not needed because the player ID is specified in the request to /mjai/start. It also means that the player states are reset for every start_kyoku event, so it is unnecessary to send any events before the current kyoku or include more than one start_kyoku event in the batch of a request.

start_kyoku

{"type":"start_kyoku","bakaze":"E","dora_marker":"9p","kyoku":1,"honba":0,"kyotaku":0,"oya":0,"scores":[25000,25000,25000,25000],"tehais":[["3m","4m","5m","5mr","8m","9m","2p","5p","8p","2s","5s","7s","N"],["1m","1m","3m","5m","7m","8m","9m","6s","8s","F","F","C","C"],["1m","2m","7m","7p","2s","7s","7s","8s","E","S","N","N","P"],["6m","9m","9m","1p","6p","6p","7p","9p","4s","5s","6s","S","P"]]}

In real games, other players in the tehais field can have "?" as unknown tiles. Even if you provide known tehais for other players, the AI models will ignore and pretend not to know these. Notice that kyoku starts with 1 instead of 0.

tsumo

{"type":"tsumo","actor":0,"pai":"9s"}

In real games, the drawn tiles of other players can be replaced by "?".

dahai

{"type":"dahai","actor":0,"pai":"N","tsumogiri":false}

chi

{"type":"chi","actor":0,"target":3,"pai":"6s","consumed":["5s","7s"]}

pon

{"type":"pon","actor":0,"target":2,"pai":"1m","consumed":["1m","1m"]}

daiminkan

{"type":"daiminkan","actor":0,"target":1,"pai":"5m","consumed":["5m","5m","5mr"]}

kakan

{"type":"kakan","actor":0,"pai":"5mr","consumed":["5m","5m","5m"]}

ankan

{"type":"ankan","actor":0,"consumed":["1m","1m","1m","1m"]}

dora

{"type":"dora","dora_marker":"1m"}

reach

{"type":"reach","actor":0}

reach_accepted

{"type":"reach_accepted","actor":0}

Mjai Actions in Responses

In responses of /mjai/act and /mjai/batch, the bot actions will be returned in act in the Mjai format, accompanied by a seq that is the same as the corresponding event.

For some models, the actions returned will contain a meta field, which has q_values and mask_bits that are compatible with the Mortal format. These fields can be used to rank all possible actions according to the AI model's preferences. The q_values can be used to derive probabilities or weights of actions (filtered by mask_bits) according to the following formula: $$ P_{t}\left(a\right)=\frac{e^{q_{t}\left(a\right)/\tau}}{\sum_{i=1}^{n}e^{q_{t}\left(i\right)/\tau}} $$ Notice that for models that do not use the Mortal architecture, these fields can be filled with artificially constructed values where the difference in q_values may not be meaningful at all.

Below are the formats of hora and ryukyoku actions. (Other Mjai actions have the same formats as Mjai events.)

hora

{"type":"hora","actor":0,"target":0}

ryukyoku

{"type":"ryukyoku"}