namespace RuleEngine.Core; public class MahjongDeck { public List Tiles { get; private set; } = new(); public MahjongDeck(bool includeHonors = false, bool includeFlowers = false, int wildcardCount = 0, int? expectedTotal = null, int wildcardBase = 50) // default matches MahjongTile.WildcardBase { 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; }