mjai-manue-go

Go port of mjai-manue — a Mahjong AI for the Mjai Mahjong AI match server
[!NOTE]
The original project includes an older version written in Ruby and a newer version written in CoffeeScript. This project ports only the new version.
About mjai-manue
mjai-manue is a Mahjong AI client published together with Mjai, a Mahjong AI match server by Hiroshi Ichikawa.[^mjai-release] It communicates with the server using the Mjai protocol.
The Mjai protocol is supported by several Japanese Mahjong AI clients and simulators.[^mjai-usage]
mjai-manue is a client by the author of Mjai. It has also been used as a baseline opponent in Mahjong AI research.[^akochan-paper]
[^mjai-release]: Hiroshi Ichikawa, " [mahjong] 麻雀AIと麻雀AI対戦サーバを公開 - monthly gimite", 2012.
[^mjai-usage]: Examples include clients such as Akochan and Mortal, and simulators such as Mjx and RiichiEnv.
[^akochan-paper]: Moyuru Kurita and Kunihito Hoki, "Method for Constructing Artificial Intelligence Player with Abstraction to Markov Decision Processes in Multiplayer Game of Mahjong", 2019.
Name
mjai-manue is pronounced "MJAI Manue" (エムジェイエーアイ・マヌエ).
According to the original author, the name may also be written in kanji as 麻鵺, roughly meaning "an unidentified thing that plays Mahjong".
Source: mixi2 post by Hiroshi Ichikawa
Demo
Sample self-match log generated by the Mjai server log viewer.
Differences from Original
stdio Mode Support
This project adds standard input/output support for JSON Lines streams, following the same style as Akochan and Mortal. In stdio mode, the bot reads input line by line and emits an action only when the current state requires a decision.
No possible_actions Dependency
In Mjai protocol messages, possible_actions may be attached to events such as tsumo and dahai to tell the bot which responses are currently legal. Unlike the original project, this project does not require that field to be present. Instead, it updates the game state from the event stream and derives available decisions from that state.
This makes the bot usable with inputs that contain the game events but omit server-provided action candidates, including mjson game logs and environments that do not provide possible_actions, such as RiichiEnv.
Embedded Configuration
Unlike the original project, this project embeds configuration files at build time. The installed binary can run on its own without depending on files in the repository checkout.
Bug Fixes
- Fixed an incorrect shanten number calculation when a hand contains four identical tiles.
- In shanten calculation, kept nearest winning-hand goals that require drawing two currently absent tiles for a pair, which the original excludes.
- Fixed win-value estimation for candidate discards so a discarded red five is not counted as a red dora in the future winning hand.
- Fixed random other-player win score-change estimation so the self-draw probability is assigned to the winning player, not always to this bot.
Other Differences
- Ported the AI logic from the original implementation while reimplementing the rest in Go.
- Treats malformed or unexpected input more strictly than the original implementation.
- Logs more detailed game-state information using Mjai's board-state format.
- Uses deterministic random numbers, but does not reproduce the original implementation's random sequence exactly.
Prerequisites
This project (including all tools under tools/) requires:
- Go 1.26 or later
- Environment variable
GOEXPERIMENT=jsonv2 enabled when building, installing or running with go run
Installation
go install github.com/Apricot-S/mjai-manue-go/cmd/mjai-manue@latest
[!TIP]
To customize the AI's strategic behavior, replace the following configuration files before building mjai-manue:
configs/danger_tree.all.json
configs/game_stats.json
configs/light_game_stats.json
See tools/ for instructions on how to generate these files.
Usage
mjai-manue [--name <PLAYER_NAME>] [--id <ID>] [--seed <INT>] [<URL>]
See cmd/ for more information.
How It Works
[!NOTE]
The original README's "仕組み" section was based on an older CoffeeScript implementation and does not match the final implementation being ported. This section has been rewritten based on the final implementation.
For each legal candidate, Manue estimates the following values. These values are included in the bot's trace output, including the response log field and stderr logs.
myHoraProb / Win probability
- The probability that the bot wins the current round after choosing the candidate.
- Estimated by Monte Carlo simulation. The bot first computes the tiles needed to complete each candidate hand, then shuffles the unseen wall and checks whether the randomly drawn tiles can satisfy one of those winning hands.
- Seven Pairs and Thirteen Orphans are not considered.
- Runs 1000 trials per decision.
avgHoraPt / Average win points
- The average point value when the bot wins.
- Estimated together with
myHoraProb, using the hand value of the winning hands reached in the Monte Carlo trials.
hojuProb / Deal-in probability
- The probability that the candidate immediately deals into another player's ron.
- Estimated per opponent from that opponent's tenpai probability and a decision-tree danger model trained from tile-safety features such as honors, suji, dora, and discard patterns.
ryukyokuProb / Exhaustive draw probability
- The probability that the round ends in an exhaustive draw if the bot does not win.
- Estimated from statistical round-end data and the current turn.
ryukyokuAvgPt / Average exhaustive draw points
- The bot's average score delta when the round ends in an exhaustive draw.
- Estimated from current tenpai probabilities and exhaustive-draw tenpai statistics.
otherHoraProb / Other players' win probability
- The probability mass left after the bot's win and exhaustive draw probabilities.
- It is distributed across the other players' possible wins.
expPt / Expected points
- The bot's expected score delta for the round.
- Computed by combining immediate deal-in, the bot's own win, exhaustive draw, and other-player win score-change distributions.
avgRank / Average rank
- The bot's expected final rank after applying the candidate's score-change distribution.
- Computed from current scores, seat order, the next round, and statistical pairwise rank probabilities.
The candidate with the smallest avgRank is selected. If multiple candidates have the same avgRank, Manue chooses the one with the larger expPt.
Candidate Selection
- Self-turn decisions are represented as discard candidates and, when legal, riichi-plus-discard candidates.
- Reactions to another player's discard are represented as pass candidates and call candidates.
- For chii and pon, Manue also evaluates the possible discard after the call.
- These decisions are selected by the same
avgRank method.
- Winning actions are always taken when legal, including Seven Pairs and Thirteen Orphans wins.
- Concealed kan, promoted kan, and kyushukyuhai are not selected.
Credits
This project is a Go port of mjai-manue, created by Hiroshi Ichikawa.
Some parts of the code are ported from Mjai, created by Hiroshi Ichikawa.
Licenses
This project is licensed under the New BSD License (3-Clause BSD License).
This project also contains configuration files copied from the original project:
configs/danger_tree.all.json
configs/game_stats.json
configs/light_game_stats.json
These files are copyright Hiroshi Ichikawa and distributed under the New BSD License.