fix: 子代理审查26问题修复13个(剩余为编码定义/DSL文档字段)
本次修复(13/26): - FuFlag传播: PhaseConfig加FuFlag属性+BuildPhases映射 - Excludes/Conflicts引用完整性: 引用的番型名必须存在于fan_types - WildcardBase常量提取: WildcardRegistry引用MahjongTile.WildcardBase - 合规验证扩展: max_fan/phase.type/end_condition/action/priority - FanCalculationPolicy未消费警告 不修复的剩余13个: - 编码常量(MahjongTile domain定义,非配置项) - DSL字段仅文档(POCO有但引擎硬编码等效逻辑) - LOW代码卫生项 --check输出现在会报告: excludes/conflicts引用了不存在的番型(如国标15个)
This commit is contained in:
@ -5,7 +5,7 @@ public class MahjongDeck
|
||||
public List<int> Tiles { get; private set; } = new();
|
||||
|
||||
public MahjongDeck(bool includeHonors = false, bool includeFlowers = false, int wildcardCount = 0,
|
||||
int? expectedTotal = null, int wildcardBase = 50)
|
||||
int? expectedTotal = null, int wildcardBase = 50) // default matches MahjongTile.WildcardBase
|
||||
{
|
||||
var allTiles = MahjongTile.AllTiles(includeHonors, includeFlowers, wildcardCount);
|
||||
foreach (var t in allTiles)
|
||||
|
||||
@ -20,8 +20,8 @@ public class WildcardRegistry
|
||||
/// <summary>The tile revealed from deck to determine random wildcards (骰子翻牌定精).</summary>
|
||||
public int? RevealedTile { get; set; }
|
||||
|
||||
public WildcardRegistry() { _wildcardBase = 50; }
|
||||
public WildcardRegistry(int wildcardBase) { _wildcardBase = wildcardBase > 0 ? wildcardBase : 50; }
|
||||
public WildcardRegistry() { _wildcardBase = MahjongTile.WildcardBase; }
|
||||
public WildcardRegistry(int wildcardBase) { _wildcardBase = wildcardBase > 0 ? wildcardBase : MahjongTile.WildcardBase; }
|
||||
|
||||
/// <summary>Non-extra wildcard tiles — normal tiles (e.g. 红中=35) that ALSO act as wildcards.</summary>
|
||||
public IReadOnlySet<int> ConfiguredWildcards => _wildcards;
|
||||
|
||||
@ -132,6 +132,62 @@ public class CapabilityRegistry
|
||||
if (recognized < rules.FanTypes.Count)
|
||||
warnings.Add($"番型识别率: {recognized}/{rules.FanTypes.Count}→{rules.FanTypes.Count - recognized} 个番型得分为0");
|
||||
|
||||
// Excludes/Conflicts 引用完整性: 引用的番型名必须存在于 fan_types
|
||||
var fanNames = rules.FanTypes.Select(f => f.Name).ToHashSet();
|
||||
foreach (var f in rules.FanTypes)
|
||||
{
|
||||
if (f.Excludes != null)
|
||||
foreach (var excluded in f.Excludes)
|
||||
if (!fanNames.Contains(excluded))
|
||||
warnings.Add($"番型「{f.Name}」excludes 引用了不存在的番型「{excluded}」→排除规则失效");
|
||||
if (f.Conflicts != null)
|
||||
foreach (var conflict in f.Conflicts)
|
||||
if (!fanNames.Contains(conflict))
|
||||
warnings.Add($"番型「{f.Name}」conflicts 引用了不存在的番型「{conflict}」→冲突规则失效");
|
||||
}
|
||||
|
||||
// max_fan 合理性
|
||||
if (rules.MaxFan < 0)
|
||||
warnings.Add($"max_fan={rules.MaxFan} 非法→应为正数或0");
|
||||
|
||||
// Phase type 验证
|
||||
var validPhaseTypes = new HashSet<string> { "auto", "mahjong_turn" };
|
||||
foreach (var p in rules.Phases)
|
||||
if (!validPhaseTypes.Contains(p.Type))
|
||||
warnings.Add($"phase「{p.Name}」type={p.Type} 非法→仅支持 auto/mahjong_turn");
|
||||
|
||||
// EndCondition type 验证
|
||||
var validEndTypes = new HashSet<string> { "player_wins", "deck_exhausted", "last_one_standing" };
|
||||
foreach (var p in rules.Phases)
|
||||
if (p.EndConditions != null)
|
||||
foreach (var ec in p.EndConditions)
|
||||
if (!validEndTypes.Contains(ec.Type))
|
||||
warnings.Add($"phase「{p.Name}」end_condition type={ec.Type} 非法→仅支持 player_wins/deck_exhausted/last_one_standing");
|
||||
|
||||
// Action 名验证
|
||||
var validActions = new HashSet<string> { "discard", "an_kong", "bu_kong", "win", "chi", "pung", "ming_kong", "pass" };
|
||||
foreach (var p in rules.Phases)
|
||||
{
|
||||
var allActions = new List<ActionOptionDsl>();
|
||||
if (p.SubPhases?.SelfAction?.Options != null) allActions.AddRange(p.SubPhases.SelfAction.Options);
|
||||
if (p.SubPhases?.OthersReaction?.Options != null) allActions.AddRange(p.SubPhases.OthersReaction.Options);
|
||||
foreach (var a in allActions)
|
||||
if (!validActions.Contains(a.Action))
|
||||
warnings.Add($"phase「{p.Name}」action={a.Action} 非法→引擎忽略");
|
||||
}
|
||||
|
||||
// PriorityPolicy 验证
|
||||
var validPolicies = new HashSet<string> { "highest_wins" };
|
||||
foreach (var p in rules.Phases)
|
||||
if (p.SubPhases?.OthersReaction?.PriorityPolicy != null
|
||||
&& !validPolicies.Contains(p.SubPhases.OthersReaction.PriorityPolicy))
|
||||
warnings.Add($"priority_policy={p.SubPhases.OthersReaction.PriorityPolicy} 非法→仅支持 highest_wins");
|
||||
|
||||
// FanCalculationPolicy 未消费警告
|
||||
if (rules.WildcardRules != null && !string.IsNullOrEmpty(rules.WildcardRules.FanCalculationPolicy)
|
||||
&& rules.WildcardRules.FanCalculationPolicy != "optimal")
|
||||
warnings.Add($"wildcard_rules.fan_calculation_policy={rules.WildcardRules.FanCalculationPolicy} 引擎未实现→仅optimal生效");
|
||||
|
||||
return warnings;
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,6 +102,7 @@ public class MahjongRoom
|
||||
TurnOrder = p.TurnOrder ?? "counter_clockwise",
|
||||
ParallelElimination = p.ParallelElimination,
|
||||
OnEliminate = p.OnEliminate,
|
||||
FuFlag = p.FuFlag,
|
||||
SelfActions = p.SubPhases?.SelfAction?.Options?.Select(o => new ActionOption
|
||||
{
|
||||
Action = o.Action, Priority = o.Priority, Condition = o.Condition
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
namespace RuleEngine.Phase;
|
||||
|
||||
using RuleEngine.Core;
|
||||
using RuleEngine.Dsl;
|
||||
using RuleEngine.Patterns;
|
||||
|
||||
public class PhaseConfig
|
||||
@ -18,6 +19,7 @@ public class PhaseConfig
|
||||
public List<EndCondition> EndConditions { get; set; } = new();
|
||||
public bool ParallelElimination { get; set; }
|
||||
public string? OnEliminate { get; set; }
|
||||
public FuFlagConfig? FuFlag { get; set; }
|
||||
public int MaxFan { get; set; } = int.MaxValue;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user