Expand description
§Riichi Mahjong Game Engine
This crate implements a game engine of standard Japanese Riichi Mahjong in the form of a library, building upon the foundation of riichi-elements and riichi-decomp.
§Table of Contents
model— Data structures for the entire game:- State (including
StateCore), Action, Reaction RoundBegin,RoundEnd, …AgariResult,Scoring, …RoundHistory,RoundHistoryLite, …
- State (including
engine— The game Engine.rules— Configurable Ruleset for the engine.yaku— All known Yaku’s and utils.interop— Working with data models from other implementations of the Japanese Riichi Mahjong game.
§Quick Example
See docs on engine::Engine.
use riichi::prelude::*; // includes `Engine` and `riichi_elements::prelude::*`
let mut engine = Engine::new();
engine.begin_round(RoundBegin {
ruleset: Default::default(),
round_id: RoundId { kyoku: 0, honba: 0 }, // east 1 kyoku, 0 honba (first round in game)
wall: wall::make_sorted_wall([1, 1, 1]), // 1111m2222m3333m4444m0555m...
pot: 0,
points: [25000, 25000, 25000, 25000],
});
assert_eq!(engine.state().core.seq, 0);
assert_eq!(engine.state().core.actor, P0);
engine.register_action(Action::Discard(Discard {
tile: t!("1m"), ..Discard::default()}))?;
// use `engine.register_reaction` for Chii/Pon/Daiminkan/Ron
let step = engine.step();
assert_eq!(step.action_result, ActionResult::Pass);
assert_eq!(engine.state().core.seq, 1);
assert_eq!(engine.state().core.actor, P1);
/* ... */
In a more realistic setting:
round_id,pot, andpointsmay be either their begin-of-game values or derived from the previous round’s results.wallshould be shuffled, e.g. using therandcrate.- The State of the engine should be observed by the players at each step.
- Actions and Reactions should be from players’ inputs.
§How We Model the Game
§Game Setup
Each game (Hanchan, Tonpuu, …) is played by 4 players and consists of at least 1 round (Kyoku). The 3-player variant is currently not supported.
Each round starts with an initial state:
- The “Ba-Kyoku-Honba” triplet, i.e. “East 1 Kyoku, 0 Honba”, represented as
model::RoundId. - How many points each player has at the beginning of the round.
- How many “riichi sticks” currently remains on the table.
- The complete shuffled wall (34 x 4 = 136 tiles) to be used in this round (see
riichi_elements::wall).
§State Machine of a Round
The game flow within a round can be modeled as the following state machine:
┌──────┐
│ Deal │
└─┬────┘
│
│ ┌────────────────────────────────────────────────────────────────┐
│ │ │
▼ ▼ #1 #2 │
┌────────┐ Draw=Y ┌────────────┐ ┌─────────────┐ Nothing │
│DrawHead├──────────►│ │ │ ├───────────┤
└────────┘ Meld=N │ │ Discard │ │ │
#4 │ ├──────────►│ │ #3 ▼
│ │ Riichi │ │ ┌─────────────────┐
│ In-turn │ │ Resolved │ │ Forced abortion │
│ player's │ │ declaration │ └─────────────────┘
┌────────┐ Draw=Y │ decision │ │ from │ ▲
┌─►│DrawTail├──────────►│ │ │ out-of-turn │ │
│ └────────┘ Meld=Y │ (Action) │ │ players │ Daiminkan │
│ #4 │ │ │ ├───────────┤
│ │ │ │ (Reaction) │ │
│ │ │ Kakan │ │ │
│ ┌────────┐ Draw=N │ ├──────────►│ │ Chii │
│ │Chii/Pon├──────────►│ │ Ankan │ ├─────────┐ │
│ └────────┘ Meld=Y └──┬───────┬─┘ └──────┬──────┘ Pon │ │
│ #4 ▲ │ │ │ │ │
│ │ NineKinds│ │Tsumo │Ron │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌──────────┐ ┌─────┐ ┌─────┐ │ │
│ │ #3│ Abortion │ │ Win │#3 │ Win │#3 │ │
│ │ └──────────┘ └─────┘ └─────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘There are multiple states within one logical turn of a round:
-
The player in turn is ready to take an action (
model::Action), after incoming draw and/or meld. This action might be terminal (abortion by nine kinds, or win by self draw). -
Each other player may independently declare an reaction (
model::Reaction): Chii, Pon, Daiminkan, or Ron. The resolved reaction type determines the next state. -
After reaction resolution, we need to check for any involuntary round-ending conditions.
-
All done, then the next player gains draw and/or meld depending on what has happened so far, marking the beginning of the next turn.
Not all actions are valid at all times; the validity often depends on state variables not illustrated in the state machine diagram.
§One-state-per-turn Simplification
It is possible to simplify by only explicitly modeling one state (per turn), namely the one before the in-turn
player makes a decision (after taking a draw or a Chii/Pon). This is basically #1 in the state machine diagram,
represented by model::State.
All other states in the diagram can be derived from this:
- The state marked
#2is basically the pre-action state (#1) + the action taken. - The states marked
#3are terminal (abortion / win). They can be handled separately outside the normal game flow. - The states marked
#4are internal transitory states skipped over by the engine without any player input.
This key simplification enables a regular representation of the normal game flow of a round as a sequence of triplets: State + Action + Reaction (optional).
§Optional features
§serde (Default: enabled)
Defines a JSON-centric serialization format for most of the common data structures, including those in the riichi-elements crate.
This simplifies interop with external programs (bots, client-server, analysis, data processing), persistence of game states, etc..
See each individual type’s docs for the detailed format.
§tenhou-log-json (Default: enabled)
Defines an intermediate de/serialization data model for Tenhou’s JSON-formatted logs, and reconstruction of each round’s preconditions, action-reactions, and end conditions into our own data model.
See interop::tenhou_log_json mod-level docs for details.
§static-lut (Default: disabled)
Enables the corresponding feature in the riichi-decomp crate, which builds the lookup tables required by its hand
analysis algorithms statically. If disabled, the lookup tables will be generated upon first instantiation of
riichi_decomp::Decomposer.
§References
Modules§
- engine
- Driver of the main game logic.
- interop
- Interoperability between this crate and other programs / data formats.
- model
- State-Action-Reaction representation of a round of game.
- prelude
- Convenient re-exports of commonly imported items.
- rules
- Configurable rules and interpretations of rules for a game, bundled as
Ruleset. - yaku
- An non-exhaustive list of recognized
Yaku’s (役) and utils for working with them.
Constants§
- VERSION_
STR - Version of this crate (as a string).
Statics§
- VERSION
- Version of this crate (parsed).