feat: 规则级合规检查→适配率报告不再虚报100%
之前: CapabilityRegistry仅检查coarse-grained能力名(requires列表) → 所有DSL报100%即使有27条规则未实现 现在: 二层报告 - 能力适配率: 7/7(100%) ← 之前唯一指标 - 规则合规检查: ⚠ 9条规则引擎无法完全执行 ← 新增 • 番型「将一色」(16)引擎无识别逻辑→0分 • 番型「杠上开花/海底捞月/抢杠胡」事件番型无识别逻辑 • fu_flag过水机制未实现 检查维度: - 番型识别(21种已知pattern vs DSL fan_types) - 番型条件字符串(hand_contains_wildcard等) - wildcard behavior(substitute唯一支持) - fu_flag过水机制 - pre_hooks(已知3种) - scoring.mode(fan_table唯一支持)
This commit is contained in:
@ -40,6 +40,16 @@ try
|
||||
var (r, m, pct) = caps.ComputeCoverage(rules.Requires);
|
||||
string status = pct == 100 ? "✅" : "⚠";
|
||||
Console.WriteLine($"{status} DSL 适配率: {m}/{r} ({pct}%) — {rules.Game.Name}");
|
||||
|
||||
// Rule-level compliance check (finer granularity than capability check)
|
||||
var warnings = caps.ValidateRuleCompliance(rules);
|
||||
if (warnings.Count > 0)
|
||||
{
|
||||
Console.WriteLine($" ⚠ {warnings.Count} 条规则引擎无法完全执行:");
|
||||
foreach (var w in warnings)
|
||||
Console.WriteLine($" • {w}");
|
||||
}
|
||||
|
||||
if (pct < 100)
|
||||
{
|
||||
var missing = rules.Requires.Where(req => !caps.Has(req));
|
||||
|
||||
@ -52,6 +52,56 @@ public class CapabilityRegistry
|
||||
int m = required.Count(req => _capabilities.Contains(req));
|
||||
return (r, m, m * 100 / r);
|
||||
}
|
||||
|
||||
/// <summary>Comprehensive rule-level compliance check — finds DSL rules the engine can't fully honor.</summary>
|
||||
public List<string> ValidateRuleCompliance(MahjongDslRoot rules)
|
||||
{
|
||||
var warnings = new List<string>();
|
||||
|
||||
// Fan types: check engine can recognize (structural patterns)
|
||||
var enginePatterns = new HashSet<string>
|
||||
{
|
||||
"清一色","混一色","字一色","对对胡","碰碰胡","碰碰和","暗七对","七对",
|
||||
"带幺九","全带幺","混幺九","缺一门","平胡","断幺九",
|
||||
"全大","全中","全小","全双","大于五","小于五",
|
||||
"十三幺","全不靠","一色双龙会" // special patterns from CheckWin
|
||||
};
|
||||
foreach (var f in rules.FanTypes)
|
||||
{
|
||||
if (!enginePatterns.Contains(f.Name) && !string.IsNullOrEmpty(f.Name))
|
||||
warnings.Add($"番型「{f.Name}」(base_fan:{f.BaseFan})引擎无识别逻辑→得分始终为0");
|
||||
}
|
||||
|
||||
// Fan condition strings
|
||||
foreach (var f in rules.FanTypes.Where(f => !string.IsNullOrEmpty(f.Condition)))
|
||||
{
|
||||
if (f.Condition != "hand_contains_wildcard")
|
||||
warnings.Add($"番型「{f.Name}」条件「{f.Condition}」引擎无法评估→番型永不触发");
|
||||
}
|
||||
|
||||
// Wildcard behavior
|
||||
if (rules.WildcardRules != null && !string.IsNullOrEmpty(rules.WildcardRules.Behavior)
|
||||
&& rules.WildcardRules.Behavior != "substitute")
|
||||
warnings.Add($"wildcard_rules.behavior={rules.WildcardRules.Behavior} 引擎仅支持substitute→忽略");
|
||||
|
||||
// Fu flag (过水)
|
||||
if (rules.Phases.Any(p => p.FuFlag != null))
|
||||
warnings.Add("过水(fu_flag)机制未实现→吃碰杠后pass仍可胡同一张牌");
|
||||
|
||||
// Pre-hooks
|
||||
var knownHooks = new HashSet<string> { "wildcard_count", "check_hua_zhu", "check_ting" };
|
||||
foreach (var h in rules.PreHooks.Concat(rules.Scoring.PreHooks))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(h.Name) && !knownHooks.Contains(h.Name))
|
||||
warnings.Add($"pre_hook「{h.Name}」引擎未实现→{h.Description}");
|
||||
}
|
||||
|
||||
// Scoring mode
|
||||
if (rules.Scoring.Mode != "fan_table")
|
||||
warnings.Add($"scoring.mode={rules.Scoring.Mode} 引擎仅支持fan_table→忽略");
|
||||
|
||||
return warnings;
|
||||
}
|
||||
}
|
||||
|
||||
// === DSL POCO types ===
|
||||
|
||||
Reference in New Issue
Block a user