Files
card-game-engine/RuleEngine/Core/WildcardRegistry.cs
xiaoou c2b5136085 refactor: _wildcards static→实例级 WildcardRegistry
Root cause: static HashSet跨测试/跨变体污染+硬编码名→编码映射

Changes:
- WildcardRegistry: 实例级wildcard管理(IsWildcard/CountWildcards/Clone)
- GameState: 新增Wildcards属性(含Clone)
- MahjongTile: 移除static_wildcards,IsWildcard仅查编码50-59
  +ToString(tile,wildcards)重载用于游戏上下文显示
- MeldsSolver: _wildcards字段→构造函数注入(WildcardRegistry?)
- PhaseMachine: _wildcards字段+构造注入
- Deal(): State.Wildcards.Clear()+AddRange替代static ConfigureWildcards
  +NameToEncoding查表替代switch硬编码
- RandomMahjongAI/HumanMahjongPlayer: state.Wildcards替代static调用
- HumanMahjongPlayer.CheckTing: 补wildcardCount参数(之前遗漏)
- ExecuteWinClaim: IsFinished=true→Settle()(之前Phase漏设)

Test: CoreTests清理ConfigureWildcards()→WildcardRegistry测试
Impact: 零static state→不同变体/测试完全隔离,未来支持随机宝牌
2026-07-04 20:05:26 +08:00

51 lines
1.6 KiB
C#

namespace RuleEngine.Core;
/// <summary>
/// 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.
/// </summary>
public class WildcardRegistry
{
private readonly HashSet<int> _wildcards = new();
/// <summary>Base wildcard encoding range (50-59) for extra tiles added to deck.</summary>
public const int 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)
{
foreach (var t in tiles) _wildcards.Add(t);
}
/// <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);
/// <summary>Count how many tiles in a list are wildcards.</summary>
public int CountWildcards(IReadOnlyList<int> tiles) =>
tiles.Count(t => IsWildcard(t));
/// <summary>Parse wildcard tile names from DSL into encodings.</summary>
public static int? NameToEncoding(string name) => name switch
{
"红中" => 35,
"发财" => 36,
"白板" => 37,
_ => null
};
public WildcardRegistry Clone()
{
var r = new WildcardRegistry();
r.AddRange(_wildcards);
return r;
}
}