The ManueAgent is the central intelligence of the system, responsible for processing the current game state and selecting the optimal action. The lifecycle follows a structured pipeline: identifying immediate wins, handling forced moves (like post-Riichi discards), generating action candidates, evaluating those candidates through simulation and statistical models, and finally selecting the best candidate based on an average-rank minimization policy.
The core entry point for the agent is the Decide() method internal/domain/ai/manue_agent.go48-67 This method orchestrates the transition from raw legal actions to a final Decision object.
The following diagram illustrates the high-level logic within ManueAgent.Decide().
Title: ManueAgent Decision Flow
Sources: internal/domain/ai/manue_agent.go48-122
ManueAgent maintains internal state primarily for its Random Number Generator (RNG).
The agent requires ManueAgentDeps, which includes Stats (for historical win/turn probabilities) and a Danger estimator (for deal-in risk analysis) internal/domain/ai/manue_agent.go14-36 The ManueStats implementation is validated upon agent creation to ensure structural invariants are met internal/domain/ai/manue_agent.go21-29
To ensure reproducible behavior (crucial for golden-file testing), the agent uses a Reset() method internal/domain/ai/manue_agent.go38-46
seed internal/domain/ai/manue_agent.go31Reset() (typically called at the start_game event) recreates the evaluator with a fresh rand.NewPCG source using that seed internal/domain/ai/manue_agent.go44-45Sources: internal/domain/ai/manue_agent.go14-46 internal/domain/ai/agent_internal_test.go98-130
Once candidates are built (see Section 4.2), they are passed to the candidateEvaluator. This component synthesizes multiple data points into a candidateScore.
The evaluation for each candidate involves:
winEstimateGoalCounts tracking successful simulations internal/domain/ai/trace.go33-39hojuProb (deal-in probability) for the discard associated with the candidate internal/domain/ai/trace.go73ryukyokuProb and ryukyokuAvgPt (expected payments at draw) internal/domain/ai/trace.go75-78avgRank (average rank) internal/domain/ai/trace.go71The candidateScore results are formatted into the decision log:
| Metric | Description |
|---|---|
avgRank | The primary metric; the expected final rank (1.0 to 4.0) internal/domain/ai/trace.go71 |
expPt | Secondary metric; the expected net point change internal/domain/ai/trace.go72 |
hojuProb | Probability of dealing into another player's win internal/domain/ai/trace.go73 |
myHoraProb | Probability of the agent winning the round internal/domain/ai/trace.go74 |
Sources: internal/domain/ai/trace.go62-96 internal/domain/ai/trace_test.go25-55
The final selection is performed by chooseBestCandidate within the buildCandidateDecision pipeline internal/domain/ai/manue_agent.go138-152
The agent follows a strict hierarchy for comparing candidates:
averageRank: The candidate with the lowest expected rank is prioritized internal/domain/ai/trace.go71expectedPoints: If ranks are equal, higher expected points are chosen internal/domain/ai/trace.go72If the agent's RiichiState is RiichiAccepted, it bypasses candidate evaluation and calls tsumogiriDiscard internal/domain/ai/manue_agent.go82-89 This enforces the rule that a player in Riichi must discard their drawn tile internal/domain/ai/manue_agent.go164-172
Sources: internal/domain/ai/manue_agent.go82-172 internal/domain/ai/agent_internal_test.go58-66
The agent attaches detailed reasoning to every decision via the Log and Trace fields of the Decision struct internal/domain/ai/manue_agent.go147-151
Every candidate is identified by a traceKey. For discards, the key format is [riichi_prefix].[tile_code].
0.5m indicates a Riichi discard of 5-man internal/domain/ai/trace.go16-20-1.5m indicates a normal discard of 5-man internal/domain/ai/trace.go14-19The formatCandidateTrace function generates a Markdown-style table containing the metrics for all evaluated candidates, including avgRank, hojuProb, and shanten internal/domain/ai/trace.go62-96 If a candidate has infinite shanten, it is represented as "Inf" internal/domain/ai/trace.go98-103
Title: Trace Data Generation
Sources: internal/domain/ai/trace.go1-123 internal/domain/ai/trace_test.go1-141
The ManueAgent is consumed by the application.Bot internal/application/bot.go15 The Bot manages the round.State and provides the agent with a Request containing a StateViewer internal/application/bot.go78-82
Title: Bot and Agent Interaction
Sources: internal/application/bot.go59-89 internal/domain/ai/manue_agent.go48-136
Refresh this wiki