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:
xiaoou
2026-07-04 20:38:35 +08:00
parent b038d5e2da
commit 237a6dc0ad
2 changed files with 60 additions and 0 deletions

View File

@ -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));

View File

@ -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 ===