The mjai-manue-go project follows a Domain-Driven Design (DDD) approach combined with a Hexagonal (Ports and Adapters) architecture. This structure ensures that the core Mahjong logic remains pure and decoupled from external protocols like MJAI or specific I/O transports like TCP and stdio.
The codebase is organized into four distinct layers with strict dependency rules: dependencies always point inward. The domain layer never depends on any other layer, while the cmd layer depends on everything below it to bootstrap the application.
| Layer | Package | Responsibility |
|---|---|---|
| Command | cmd/ | CLI entry points, flag parsing, and runtime initialization <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/AGENTS.md?plain=1#L26-L26" min=26 file-path="AGENTS.md">Hii</FileRef>. |
| Adapter | internal/adapter/ | External protocol translation (MJAI JSON) and I/O drivers (TCP/Stdio) <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/AGENTS.md?plain=1#L25-L25" min=25 file-path="AGENTS.md">Hii</FileRef>. |
| Application | internal/application/ | Use-case orchestration (The "Bot" logic), managing game sessions <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/AGENTS.md?plain=1#L24-L24" min=24 file-path="AGENTS.md">Hii</FileRef>. |
| Domain | internal/domain/ | Core Mahjong rules, state transitions, and AI decision logic <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/AGENTS.md?plain=1#L23-L23" min=23 file-path="AGENTS.md">Hii</FileRef>. |
<FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L57-L58" min=57 max=58 file-path=".agents/design.md">Hii</FileRef>.<FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L59-L60" min=59 max=60 file-path=".agents/design.md">Hii</FileRef>.<FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L58-L59" min=58 max=59 file-path=".agents/design.md">Hii</FileRef>.Sources: <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L53-L61" min=53 max=61 file-path=".agents/design.md">Hii</FileRef>, <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/AGENTS.md?plain=1#L21-L25" min=21 max=25 file-path="AGENTS.md">Hii</FileRef>
To prevent the specific quirks of the MJAI protocol from leaking into the Mahjong engine, the system employs an Anti-Corruption Layer (ACL). This is primarily implemented in the internal/adapter/mjai/ package.
The following diagram illustrates how an external MJAI message travels through the layers, transforming from a raw string to a typed Domain Event, and eventually resulting in a Domain Action.
Diagram: Data Transformation Flow
Sources: <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L66-L91" min=66 max=91 file-path=".agents/design.md">Hii</FileRef>, <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L103-L104" min=103 max=104 file-path=".agents/design.md">Hii</FileRef>
cmd/)The entry points (e.g., cmd/mjai-manue/main.go) are responsible for flag analysis, Agent selection, and runtime startup <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/AGENTS.md?plain=1#L26-L26" min=26 file-path="AGENTS.md">Hii</FileRef>. They load configuration files (stats and danger trees) and initialize the ai.Agent implementation with appropriate dependencies <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L107-L107" min=107 file-path=".agents/design.md">Hii</FileRef>.
internal/adapter/)Driver handles the dispatch of messages to the Bot <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L103-L103" min=103 file-path=".agents/design.md">Hii</FileRef>.possible_actions from MJAI is ignored; the engine derives legal actions internally from the round.State <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/README.md?plain=1#L41-L45" min=41 max=45 file-path="README.md">Hii</FileRef>.Discard or Pass into MJAI messages. Pass is explicitly handled to support the protocol's none response while preserving AI logic logs <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L124-L125" min=124 max=125 file-path=".agents/design.md">Hii</FileRef>.internal/application/)The Bot struct is the central coordinator. It maintains the game.State (long-term scores) and the round.State (current hand/table state) <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L104-L104" min=104 file-path=".agents/design.md">Hii</FileRef>.
Bot distinguishes between NoReaction (just updating state) and Action (a decision made by the AI) <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L119-L122" min=119 max=122 file-path=".agents/design.md">Hii</FileRef>. This is critical for the MJAI protocol, where some messages require a synchronous {"type":"none"} response while others do not <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L124-L125" min=124 max=125 file-path=".agents/design.md">Hii</FileRef>.Bot uses a Reporter interface to output board states and AI traces to stderr without polluting the protocol stdout <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L47-L47" min=47 file-path=".agents/design.md">Hii</FileRef>.internal/domain/)round.State manages the state machine of a single hand, handling transitions for discards, calls, and wins <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L98-L98" min=98 file-path=".agents/design.md">Hii</FileRef>. It implements Apply to advance the game based on domain events <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L98-L98" min=98 file-path=".agents/design.md">Hii</FileRef>.ai.Agent interface defines the decision-making contract, taking an ai.Request which contains the current round.ActionStateViewer (observation + legal actions) <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/AGENTS.md?plain=1#L34-L34" min=34 file-path="AGENTS.md">Hii</FileRef>, <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L104-L104" min=104 file-path=".agents/design.md">Hii</FileRef>.AnalyzeShanten calculate hand progress without maintaining state <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/internal/domain/game/round/service/shanten.go#L91-L91" min=91 file-path="internal/domain/game/round/service/shanten.go">Hii</FileRef>.Sources: <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L97-L108" min=97 max=108 file-path=".agents/design.md">Hii</FileRef>, <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/AGENTS.md?plain=1#L20-L37" min=20 max=37 file-path="AGENTS.md">Hii</FileRef>, <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/internal/domain/game/round/service/shanten.go#L91-L140" min=91 max=140 file-path="internal/domain/game/round/service/shanten.go">Hii</FileRef>
This diagram bridges the gap between high-level concepts and specific code entities during a single turn (e.g., a tsumo event).
Diagram: Code Entity Interaction
Sources: <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L115-L135" min=115 max=135 file-path=".agents/design.md">Hii</FileRef>, <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/AGENTS.md?plain=1#L34-L34" min=34 file-path="AGENTS.md">Hii</FileRef>
--seed is provided <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/README.md?plain=1#L63-L63" min=63 file-path="README.md">Hii</FileRef>, <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L48-L48" min=48 file-path=".agents/design.md">Hii</FileRef>.round.State can render a text-based board for debugging via RenderBoard(), but it returns a string rather than writing directly to stdout <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L99-L99" min=99 file-path=".agents/design.md">Hii</FileRef>.<FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/AGENTS.md?plain=1#L31-L31" min=31 file-path="AGENTS.md">Hii</FileRef>, <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L46-L46" min=46 file-path=".agents/design.md">Hii</FileRef>.stdout is reserved strictly for MJAI protocol messages, while all logs and diagnostic traces are directed to stderr <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/AGENTS.md?plain=1#L30-L30" min=30 file-path="AGENTS.md">Hii</FileRef>, <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L47-L47" min=47 file-path=".agents/design.md">Hii</FileRef>.Sources: <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/.agents/design.md?plain=1#L44-L51" min=44 max=51 file-path=".agents/design.md">Hii</FileRef>, <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/AGENTS.md?plain=1#L20-L37" min=20 max=37 file-path="AGENTS.md">Hii</FileRef>, <FileRef file-url="https://github.com/Apricot-S/mjai-manue-go/blob/1ead8427/README.md?plain=1#L37-L40" min=37 max=40 file-path="README.md">Hii</FileRef>
Refresh this wiki