fix: CheckWin缺少wildcard/258参数导致武汉麻将0%胡牌 + 庄家第一轮跳过摸牌

Root cause 1: 所有CheckWin调用未传wildcardCount/require258Pair
- StepTurn自摸: 5处遗漏 → 癞子在手牌中被当废牌
- HandleDiscardReactions: 2处遗漏
- PhaseMachine: 2处遗漏
→ 武汉麻将永远不能胡牌

Root cause 2: 庄家开局14张+摸牌=15张, CheckWin守卫≠14拒绝
→ 庄家永远不能自摸 → 修复: 庄家第一轮跳过摸牌

额外修复:
- RandomMahjongAI: 永远不出癞子(预留wildcard)
- ScoreEngine.FanValue: 从DSL _fanConfig读取番值
- HandleDiscardReactions: 同优先级座位顺序
- 新增GetWinParams/CountWildcardsInHand辅助方法

验证: 47/47测试, Wuhan 1.6%胡牌(500局), Sichuan 0.4%
This commit is contained in:
xiaoou
2026-07-04 11:39:00 +08:00
parent 55447d6b05
commit bb43ed7c89
3 changed files with 50 additions and 14 deletions

View File

@ -38,11 +38,16 @@ public class MahjongPhaseMachine
{
private readonly List<PhaseConfig> _phases;
private readonly MeldsSolver _solver;
private readonly int _wildcardCount;
private readonly bool _require258;
public MahjongPhaseMachine(List<PhaseConfig> phases, MeldsSolver solver)
public MahjongPhaseMachine(List<PhaseConfig> phases, MeldsSolver solver,
int wildcardCount = 0, bool require258 = false)
{
_phases = phases;
_solver = solver;
_wildcardCount = wildcardCount;
_require258 = require258;
}
public PhaseConfig? GetPhase(string name) => _phases.FirstOrDefault(p => p.Name == name);
@ -65,7 +70,10 @@ public class MahjongPhaseMachine
actions.Add(new ActionOption { Action = "ming_kong", Priority = 3 });
var handWithTile = new List<int>(state.Hands[playerId]) { state.LastDiscard.Value };
var result = _solver.CheckWin(handWithTile);
int wildInHand = state.Hands[playerId].Count(Core.MahjongTile.IsWildcard)
+ (Core.MahjongTile.IsWildcard(state.LastDiscard.Value) ? 1 : 0);
var result = _solver.CheckWin(handWithTile,
wildcardCount: wildInHand, require258Pair: _require258);
if (result != null && result.IsWin)
{
if (!requireWinFan || result.Fans.Sum(f => GetFanValue(f)) >= 8)
@ -79,7 +87,9 @@ public class MahjongPhaseMachine
if (CanBuKong(state, playerId))
actions.Add(new ActionOption { Action = "bu_kong", Priority = 1 });
var tumoResult = _solver.CheckWin(state.Hands[playerId]);
int wildInSelf = state.Hands[playerId].Count(Core.MahjongTile.IsWildcard);
var tumoResult = _solver.CheckWin(state.Hands[playerId],
wildcardCount: wildInSelf, require258Pair: _require258);
if (tumoResult != null && tumoResult.IsWin)
{
if (!requireWinFan || tumoResult.Fans.Sum(f => GetFanValue(f)) >= 8)