namespace RuleEngine.Core; /// /// Per-game wildcard configuration — replaces the old static HashSet approach. /// Each MahjongGameState owns its own registry so different game variants /// don't leak wildcard state between tests or concurrent games. /// public class WildcardRegistry { private readonly HashSet _wildcards = new(); /// Base wildcard encoding range (50-59) for extra tiles added to deck. public const int WildcardBase = 50; /// Non-extra wildcard tiles — normal tiles (e.g. 红中=35) that ALSO act as wildcards. public IReadOnlySet ConfiguredWildcards => _wildcards; public void Clear() => _wildcards.Clear(); public void Add(int tile) => _wildcards.Add(tile); public void AddRange(IEnumerable tiles) { foreach (var t in tiles) _wildcards.Add(t); } /// Is this tile a wildcard? Checks encoding range AND configured set. public bool IsWildcard(int tile) => (tile >= WildcardBase && tile <= WildcardBase + 9) || _wildcards.Contains(tile); /// Count how many tiles in a list are wildcards. public int CountWildcards(IReadOnlyList tiles) => tiles.Count(t => IsWildcard(t)); /// Parse wildcard tile names from DSL into encodings. public static int? NameToEncoding(string name) => name switch { "红中" => 35, "发财" => 36, "白板" => 37, _ => null }; public WildcardRegistry Clone() { var r = new WildcardRegistry(); r.AddRange(_wildcards); return r; } }