From 237a6dc0adb4d4c7404fa5caacace89804b21189 Mon Sep 17 00:00:00 2001 From: xiaoou Date: Sat, 4 Jul 2026 20:38:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A7=84=E5=88=99=E7=BA=A7=E5=90=88?= =?UTF-8?q?=E8=A7=84=E6=A3=80=E6=9F=A5=E2=86=92=E9=80=82=E9=85=8D=E7=8E=87?= =?UTF-8?q?=E6=8A=A5=E5=91=8A=E4=B8=8D=E5=86=8D=E8=99=9A=E6=8A=A5100%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前: 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唯一支持) --- Demo/Program.cs | 10 ++++++++ RuleEngine/Dsl/DslLoader.cs | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/Demo/Program.cs b/Demo/Program.cs index 80dd766..3473e81 100644 --- a/Demo/Program.cs +++ b/Demo/Program.cs @@ -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)); diff --git a/RuleEngine/Dsl/DslLoader.cs b/RuleEngine/Dsl/DslLoader.cs index 50706bc..6dfbafa 100644 --- a/RuleEngine/Dsl/DslLoader.cs +++ b/RuleEngine/Dsl/DslLoader.cs @@ -52,6 +52,56 @@ public class CapabilityRegistry int m = required.Count(req => _capabilities.Contains(req)); return (r, m, m * 100 / r); } + + /// Comprehensive rule-level compliance check — finds DSL rules the engine can't fully honor. + public List ValidateRuleCompliance(MahjongDslRoot rules) + { + var warnings = new List(); + + // Fan types: check engine can recognize (structural patterns) + var enginePatterns = new HashSet + { + "清一色","混一色","字一色","对对胡","碰碰胡","碰碰和","暗七对","七对", + "带幺九","全带幺","混幺九","缺一门","平胡","断幺九", + "全大","全中","全小","全双","大于五","小于五", + "十三幺","全不靠","一色双龙会" // 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 { "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 ===