The mjai-manue-go engine utilizes a clear distinction between Events, which represent state changes that have occurred in the game (often received from the server), and Actions, which represent decisions made by a player that are submitted to the server. These hierarchies drive the state transitions within the round state machine.
An Event is an interface defined in internal/domain/game/event/event.go3-5 It is used to update the internal game state. Events are typically generated by the Mjai adapter after decoding inbound JSON messages.
| Event Type | Description | Key Fields |
|---|---|---|
Draw | A player draws a tile from the wall. | actor, drawnTile |
Discard | A player discards a tile. | actor, tile, tsumogiri |
Chii / Pon | A player calls a sequence or triplet. | actor, target, taken, consumed |
Dora | A new dora indicator is revealed. | tile |
ConcealedKan | A player declares a closed Kan. | actor, consumed |
CalledKan | A player declares an open Kan (Daiminkan). | actor, target, taken, consumed |
PromotedKan | A player adds a tile to a Pon (Kakan). | actor, added, consumed |
Win | A player wins (Tsumo/Ron). | actor, target, winningTile, winningPoints, deltas, scores |
StartRound | Initialization of a new round. | (Hand, Dealer, Dora, etc.) |
EndRound | Signal that the current round has concluded. | - |
DrawRound | A round ends in a draw (e.g., Ryukyoku). | - |
drawnTile and the actor (seat) internal/domain/game/event/draw.go8-11tsumogiri boolean to indicate if the tile was the one just drawn internal/domain/game/event/discard.go8-12ConcealedKan internal/domain/game/event/concealed_kan.go8-11 CalledKan internal/domain/game/event/called_kan.go8-13 and PromotedKan internal/domain/game/event/promoted_kan.go8-12 to correctly handle state transitions like Robbing a Kan.The following diagram maps the logical events to their implementation entities.
Diagram: Event Entity Mapping
Sources: internal/domain/game/event/event.go3-5 internal/domain/game/event/draw.go13-15 internal/domain/game/event/discard.go14-20 internal/domain/game/event/end_round.go6-8 internal/domain/game/event/draw_round.go6-8 internal/domain/game/event/called_kan.go15-20
An Action represents a player's intent. Unlike Events, Actions are validated against the current state to ensure they are legal before being converted into outbound Mjai messages.
| Action Type | Description | Implementation |
|---|---|---|
Discard | Discarding a tile from hand. | internal/domain/game/action/discard.go8-12 |
Chii | Calling a sequence. | internal/domain/game/action/chii.go10-15 |
Pon | Calling a triplet. | internal/domain/game/action/pon.go10-15 |
ConcealedKan | Declaring Ankan. | internal/domain/game/action/concealed_kan.go10-13 |
CalledKan | Declaring Daiminkan. | internal/domain/game/action/called_kan.go10-15 |
PromotedKan | Declaring Kakan. | internal/domain/game/action/promoted_kan.go10-14 |
Riichi | Declaring Riichi. | internal/domain/game/action/riichi.go8-10 |
Win | Declaring Tsumo or Ron. | internal/domain/game/action/win.go8-11 |
Pass | Opting not to react to a discard. | - |
Kyushukyuhai | Abortive draw (9 terminals/honors). | - |
Action constructors perform validation to ensure no "Unknown" tiles are passed into the domain logic. For example, NewChii returns an error if the taken tile or any consumed tiles are unknown internal/domain/game/action/chii.go17-25 Similar checks exist for Pon internal/domain/game/action/pon.go17-24 ConcealedKan internal/domain/game/action/concealed_kan.go15-20 and PromotedKan internal/domain/game/action/promoted_kan.go16-24
The AI generates these actions, which are then serialized by the adapter layer.
Diagram: Action Lifecycle and Flow
Sources: internal/domain/game/action/discard.go14-20 internal/domain/game/action/chii.go17-25 internal/domain/game/action/concealed_kan.go15-20
While many types (like Discard or Pon) exist in both hierarchies, they serve different purposes:
Actor Authority:
event.Discard reflects what the server said happened. It includes the actor and tsumogiri status as fact internal/domain/game/event/discard.go24-34action.Discard is a request. It requires validation that the tile is not unknown internal/domain/game/action/discard.go14-20 and unit tests ensure that unknown tiles cause errors internal/domain/game/action/discard_test.go49-52Meld Structure:
Chii and Pon explicitly track the taken tile (from the target) and the consumed tiles (from the actor's hand) to ensure the move is valid internal/domain/game/action/chii.go10-15 internal/domain/game/action/pon.go10-15Kan Complexity:
Concealed, Called, Promoted) because they have different impacts on the round state, such as when a PromotedKan can be intercepted by a Win action (Robbing the Kan) internal/domain/game/action/promoted_kan.go10-14Testability:
TestNewConcealedKan verifies that the actor and consumed tiles are correctly assigned internal/domain/game/action/concealed_kan_test.go11-31 while TestNewCalledKan checks the target and taken tile assignments internal/domain/game/action/called_kan_test.go11-38Sources: internal/domain/game/event/discard.go internal/domain/game/action/discard.go internal/domain/game/action/chii.go internal/domain/game/action/promoted_kan.go internal/domain/game/action/discard_test.go internal/domain/game/action/concealed_kan_test.go internal/domain/game/action/called_kan_test.go