fix: PhaseMachine硬编码>=8番 → 读DSL win_min_fan

4处修复:
- PhaseMachine构造函数新增 _winMinFan 参数
- GetLegalActions 中 discard win check (line 139): >= 8 → >= _winMinFan
- GetLegalActions 中 self-draw win check (line 165): >= 8 → >= _winMinFan
- CheckCondition 中的 can_win*_and_fan_ge_8 (line 97-98): >= 8 → >= _winMinFan
- MahjongRoom 传参: winMinFan=_rules.WinMinFan

效果: 国标胡牌率 30%→13% (baseFan<8的低番手牌被正确拦截)
58 tests pass, 5 variants 0 errors
This commit is contained in:
xiaoou
2026-07-04 21:46:58 +08:00
parent 17a0d054ff
commit 4e9f06d5cd
2 changed files with 10 additions and 6 deletions

View File

@ -40,7 +40,8 @@ public class MahjongRoom
_phaseMachine = new MahjongPhaseMachine(phases, _solver, wildcardCount: wc, require258: r258, _phaseMachine = new MahjongPhaseMachine(phases, _solver, wildcardCount: wc, require258: r258,
fanConfig: fanConfig, fanConfig: fanConfig,
jiHuSelfDrawOnly: _rules.WinRule?.JiHuSelfDrawOnly ?? false, jiHuSelfDrawOnly: _rules.WinRule?.JiHuSelfDrawOnly ?? false,
wildcards: State.Wildcards); wildcards: State.Wildcards,
winMinFan: _rules.WinMinFan);
_scoreEngine = new MahjongScoreEngine(new ScoringConfig _scoreEngine = new MahjongScoreEngine(new ScoringConfig
{ {

View File

@ -44,13 +44,15 @@ public class MahjongPhaseMachine
private readonly bool _jiHuSelfDrawOnly; private readonly bool _jiHuSelfDrawOnly;
private readonly WildcardRegistry _wildcards; private readonly WildcardRegistry _wildcards;
private readonly bool _chiAllowed; private readonly bool _chiAllowed;
private readonly int _winMinFan;
public bool ChiAllowed => _chiAllowed; public bool ChiAllowed => _chiAllowed;
public MahjongPhaseMachine(List<PhaseConfig> phases, MeldsSolver solver, public MahjongPhaseMachine(List<PhaseConfig> phases, MeldsSolver solver,
int wildcardCount = 0, bool require258 = false, int wildcardCount = 0, bool require258 = false,
Dictionary<string, FanConfig>? fanConfig = null, Dictionary<string, FanConfig>? fanConfig = null,
bool jiHuSelfDrawOnly = false, bool jiHuSelfDrawOnly = false,
WildcardRegistry? wildcards = null) WildcardRegistry? wildcards = null,
int winMinFan = 0)
{ {
_phases = phases; _phases = phases;
_solver = solver; _solver = solver;
@ -59,6 +61,7 @@ public class MahjongPhaseMachine
_fanConfig = fanConfig ?? new(); _fanConfig = fanConfig ?? new();
_jiHuSelfDrawOnly = jiHuSelfDrawOnly; _jiHuSelfDrawOnly = jiHuSelfDrawOnly;
_wildcards = wildcards ?? new(); _wildcards = wildcards ?? new();
_winMinFan = winMinFan;
_chiAllowed = phases.Any(p => _chiAllowed = phases.Any(p =>
p.OthersReactions.Any(o => o.Action == "chi")); p.OthersReactions.Any(o => o.Action == "chi"));
} }
@ -94,8 +97,8 @@ public class MahjongPhaseMachine
"can_chi" => chiAllowed, "can_chi" => chiAllowed,
"can_win" => canSelfWin, "can_win" => canSelfWin,
"can_win_tumo" => canSelfWin, "can_win_tumo" => canSelfWin,
"can_win_tumo_and_fan_ge_8" => canSelfWin && fanSum >= 8, "can_win_tumo_and_fan_ge_8" => canSelfWin && fanSum >= _winMinFan,
"can_win_and_fan_ge_8" => canSelfWin && fanSum >= 8, "can_win_and_fan_ge_8" => canSelfWin && fanSum >= _winMinFan,
_ => true // unknown condition → allow (fail-open for safety) _ => true // unknown condition → allow (fail-open for safety)
}; };
} }
@ -136,7 +139,7 @@ public class MahjongPhaseMachine
{ {
// Don't add win action; 鸡胡 can only self-draw // Don't add win action; 鸡胡 can only self-draw
} }
else if (!requireWinFan || result.Fans.Sum(f => GetFanValue(f)) >= 8) else if (!requireWinFan || result.Fans.Sum(f => GetFanValue(f)) >= _winMinFan)
actions.Add(new ActionOption { Action = "win", Priority = 4 }); actions.Add(new ActionOption { Action = "win", Priority = 4 });
} }
} }
@ -162,7 +165,7 @@ public class MahjongPhaseMachine
wildcardCount: wildInSelf, require258Pair: _require258); wildcardCount: wildInSelf, require258Pair: _require258);
if (tumoResult != null && tumoResult.IsWin) if (tumoResult != null && tumoResult.IsWin)
{ {
if (!requireWinFan || tumoResult.Fans.Sum(f => GetFanValue(f)) >= 8) if (!requireWinFan || tumoResult.Fans.Sum(f => GetFanValue(f)) >= _winMinFan)
actions.Add(new ActionOption { Action = "win", Priority = 4 }); actions.Add(new ActionOption { Action = "win", Priority = 4 });
} }
} }