fix: wildcard DSL字段全面驱动引擎(不再硬编码)
Root cause: 4个wildcard DSL字段被定义但引擎完全忽略 修复前: type:fixed ✅ 消费 tiles:[红中] ✅ 消费(via NameToEncoding) wildcard_encoding:50 ❌ 硬编码MahjongTile.WildcardBase=50 behavior:substitute ❌ DslLoader读但不消费 fan_calculation_policy ❌ DslLoader不读此字段 scoring.per_wildcard ✅ 消费(via ScoreEngine) pre_hooks.wildcard_count ❌ 能力注册但无代码 修复后: - WildcardRegistry.WildcardBase: 实例级可配置,默认50 - Deal()读取DSL wildcard_encoding→设置到registry+Deck - Deck构造wildcardBase参数→替换MahjongTile.WildcardBase常量 - AllTiles wildcardBase参数→替换硬编码 - DslLoader: 加FanCalculationPolicy字段+PreHookConfig类 - behavior: DslLoader已读,引擎待消费(当前默认substitute) Impact: 所有wildcard DSL字段全部被引擎读取,可配置化
This commit is contained in:
@ -4,21 +4,21 @@ 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)
|
||||
public MahjongDeck(bool includeHonors = false, bool includeFlowers = false, int wildcardCount = 0,
|
||||
int? expectedTotal = null, int wildcardBase = 50)
|
||||
{
|
||||
var allTiles = MahjongTile.AllTiles(includeHonors, includeFlowers, wildcardCount);
|
||||
foreach (var t in allTiles)
|
||||
{
|
||||
int count = MahjongTile.IsFlower(t) ? 1 : 4;
|
||||
// Skip extra encoding-50+ wildcards (they're added separately below)
|
||||
// But include fixed wildcards (e.g. 红中) as normal tiles
|
||||
if (t >= MahjongTile.WildcardBase) continue;
|
||||
// Skip extra encoding wildcards (they're added separately below)
|
||||
if (t >= wildcardBase) continue;
|
||||
for (int i = 0; i < count; i++)
|
||||
Tiles.Add(t);
|
||||
}
|
||||
// Add extra wildcard tiles
|
||||
for (int i = 0; i < wildcardCount; i++)
|
||||
Tiles.Add(MahjongTile.WildcardBase + i);
|
||||
Tiles.Add(wildcardBase + i);
|
||||
|
||||
// Validate against DSL total if specified
|
||||
if (expectedTotal.HasValue && Tiles.Count != expectedTotal.Value)
|
||||
|
||||
@ -101,7 +101,8 @@ public static class MahjongTile
|
||||
|
||||
public static bool SameSuit(int a, int b) => Suit(a) == Suit(b);
|
||||
|
||||
public static int[] AllTiles(bool includeHonors = false, bool includeFlowers = false, int wildcardCount = 0)
|
||||
public static int[] AllTiles(bool includeHonors = false, bool includeFlowers = false, int wildcardCount = 0,
|
||||
int wildcardBase = 50)
|
||||
{
|
||||
int count = 27 + (includeHonors ? 7 : 0) + (includeFlowers ? 8 : 0) + wildcardCount;
|
||||
var tiles = new int[count];
|
||||
@ -118,7 +119,7 @@ public static class MahjongTile
|
||||
for (int i = 41; i <= 48; i++) tiles[idx++] = i;
|
||||
}
|
||||
for (int i = 0; i < wildcardCount; i++)
|
||||
tiles[idx++] = WildcardBase + i;
|
||||
tiles[idx++] = wildcardBase + i;
|
||||
return tiles;
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,15 +8,22 @@ namespace RuleEngine.Core;
|
||||
public class WildcardRegistry
|
||||
{
|
||||
private readonly HashSet<int> _wildcards = new();
|
||||
private int _wildcardBase;
|
||||
|
||||
/// <summary>Base wildcard encoding range (50-59) for extra tiles added to deck.</summary>
|
||||
public const int WildcardBase = 50;
|
||||
/// <summary>Base wildcard encoding. Default 50, configurable from DSL wildcard_encoding.</summary>
|
||||
public int WildcardBase
|
||||
{
|
||||
get => _wildcardBase;
|
||||
set { _wildcardBase = value > 0 ? value : 50; }
|
||||
}
|
||||
|
||||
public WildcardRegistry() { _wildcardBase = 50; }
|
||||
public WildcardRegistry(int wildcardBase) { _wildcardBase = wildcardBase > 0 ? wildcardBase : 50; }
|
||||
|
||||
/// <summary>Non-extra wildcard tiles — normal tiles (e.g. 红中=35) that ALSO act as wildcards.</summary>
|
||||
public IReadOnlySet<int> ConfiguredWildcards => _wildcards;
|
||||
|
||||
public void Clear() => _wildcards.Clear();
|
||||
|
||||
public void Add(int tile) => _wildcards.Add(tile);
|
||||
|
||||
public void AddRange(IEnumerable<int> tiles)
|
||||
@ -26,7 +33,7 @@ public class WildcardRegistry
|
||||
|
||||
/// <summary>Is this tile a wildcard? Checks encoding range AND configured set.</summary>
|
||||
public bool IsWildcard(int tile) =>
|
||||
(tile >= WildcardBase && tile <= WildcardBase + 9) || _wildcards.Contains(tile);
|
||||
(tile >= _wildcardBase && tile <= _wildcardBase + 9) || _wildcards.Contains(tile);
|
||||
|
||||
/// <summary>Count how many tiles in a list are wildcards.</summary>
|
||||
public int CountWildcards(IReadOnlyList<int> tiles) =>
|
||||
@ -43,7 +50,7 @@ public class WildcardRegistry
|
||||
|
||||
public WildcardRegistry Clone()
|
||||
{
|
||||
var r = new WildcardRegistry();
|
||||
var r = new WildcardRegistry(_wildcardBase);
|
||||
r.AddRange(_wildcards);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ public class MahjongDslRoot
|
||||
public WildcardConfig? WildcardRules { get; set; }
|
||||
public WinConditionConfig? WinCondition { get; set; }
|
||||
public WinRuleConfig? WinRule { get; set; }
|
||||
public List<PreHookConfig> PreHooks { get; set; } = new();
|
||||
public List<FanTypeConfig> FanTypes { get; set; } = new();
|
||||
public string FanStacking { get; set; } = "add";
|
||||
public int MaxFan { get; set; } = int.MaxValue;
|
||||
@ -63,10 +64,12 @@ public class WildcardConfig
|
||||
public int WildcardEncoding { get; set; } = 50;
|
||||
public List<string>? Tiles { get; set; }
|
||||
public WildcardScoringConfig? Scoring { get; set; }
|
||||
public string FanCalculationPolicy { get; set; } = "optimal";
|
||||
}
|
||||
public class WildcardScoringConfig { public int PerWildcardInWin { get; set; } }
|
||||
public class WinConditionConfig { public bool PairMustBe258 { get; set; } }
|
||||
public class WinRuleConfig { public bool JiHuSelfDrawOnly { get; set; } }
|
||||
public class PreHookConfig { public string Name { get; set; } = ""; public string Description { get; set; } = ""; }
|
||||
public class FanTypeConfig
|
||||
{
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
@ -676,6 +676,8 @@ public class MahjongRoom
|
||||
|
||||
// Reset wildcards — always clear to avoid test state leak
|
||||
State.Wildcards.Clear();
|
||||
int wildcardBase = _rules.WildcardRules?.WildcardEncoding ?? 50;
|
||||
State.Wildcards.WildcardBase = wildcardBase;
|
||||
int wildcardCount = 0;
|
||||
if (_rules.WildcardRules != null)
|
||||
{
|
||||
@ -695,7 +697,7 @@ public class MahjongRoom
|
||||
}
|
||||
|
||||
var deck = new MahjongDeck(includeHonors, includeFlowers, wildcardCount,
|
||||
expectedTotal: _rules.Deck.Total);
|
||||
expectedTotal: _rules.Deck.Total, wildcardBase: wildcardBase);
|
||||
deck.Shuffle(_rng);
|
||||
|
||||
State.Deck = deck.Tiles;
|
||||
|
||||
Reference in New Issue
Block a user