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字段全部被引擎读取,可配置化
55 lines
1.8 KiB
C#
55 lines
1.8 KiB
C#
namespace RuleEngine.Core;
|
|
|
|
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)
|
|
{
|
|
var allTiles = MahjongTile.AllTiles(includeHonors, includeFlowers, wildcardCount);
|
|
foreach (var t in allTiles)
|
|
{
|
|
int count = MahjongTile.IsFlower(t) ? 1 : 4;
|
|
// 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(wildcardBase + i);
|
|
|
|
// Validate against DSL total if specified
|
|
if (expectedTotal.HasValue && Tiles.Count != expectedTotal.Value)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"牌库大小不匹配: 引擎生成 {Tiles.Count} 张, DSL 声明 {expectedTotal.Value} 张。" +
|
|
$" includeHonors={includeHonors} includeFlowers={includeFlowers} wildcardCount={wildcardCount}." +
|
|
$" 请检查 DSL deck.total 字段与 include_honors/include_flowers 的一致性。");
|
|
}
|
|
}
|
|
|
|
public void Shuffle(Random? rng = null)
|
|
{
|
|
rng ??= Random.Shared;
|
|
int n = Tiles.Count;
|
|
while (n > 1)
|
|
{
|
|
int k = rng.Next(n--);
|
|
(Tiles[n], Tiles[k]) = (Tiles[k], Tiles[n]);
|
|
}
|
|
}
|
|
|
|
public int Draw()
|
|
{
|
|
if (Tiles.Count == 0)
|
|
throw new InvalidOperationException("Deck is empty");
|
|
int last = Tiles[^1];
|
|
Tiles.RemoveAt(Tiles.Count - 1);
|
|
return last;
|
|
}
|
|
|
|
public int Count => Tiles.Count;
|
|
}
|