feat: EndConditions映射 + 骰子翻牌定精(random wildcard)

EndConditions:
- BuildPhases 现在映射 PhaseConfig.Endconditions (之前 DslLoader 解析但 BuildPhases 忽略)
- 4处 _rules.Phases.Any(p => p.ParallelElimination) → _playPhase.ParallelElimination
- 新增 _playPhase 字段, 游戏不再依赖原始 DSL 做运行时判断

Random wildcard (骰子翻牌定精):
- WildcardRegistry.ComputeNextTiles(): 翻到 X → 正精=X+1, 副精=X+2 (9→1 循环, 字牌序循环)
- WildcardRegistry.RevealedTile 记录翻到的牌
- Deal() 发牌后翻一张牌确定精, 支持 random_count 控制精数量
- WildcardConfig 新增 RandomCount 字段 (默认2=正精+副精)
- ValidateRuleCompliance 接受 random 类型

南昌麻将 DSL:
- 新增 dsl-examples/nanchang.yaml (136张, random精, 无吃)
- 引擎加载 7/7, 1000局 0错误, 胡牌率 61%

58 tests pass, 5 种玩法全部 0 错误验证通过
This commit is contained in:
xiaoou
2026-07-04 21:06:16 +08:00
parent 5b60255f02
commit ae9c4c4142
5 changed files with 178 additions and 8 deletions

View File

@ -63,14 +63,17 @@ deal:
### wildcard_rules (可选,有癞子时必填)
```yaml
wildcard_rules:
type: fixed # "fixed"=指定牌当癞子 或 留空=编码50+的独立癞子牌
tiles: [红中] # 当type=fixed时,b必须使用这些名称
type: fixed # "fixed"=指定牌当癞子, "random"=骰子翻牌定精
tiles: [红中] # 当type=fixed时,必须使用这些名称
random_count: 2 # 当type=random时,正精+副精的数量(1=仅正精,2=正精+副精)
wildcard_encoding: 50 # 独立癞子牌的起始编码,默认50(范围50-59)
behavior: substitute # 当前仅支持"substitute"
fan_calculation_policy: optimal # 当前仅支持"optimal"
scoring:
per_wildcard_in_win: 1 # 每张癞子额外加番,0=不加
```
**精牌类型**`fixed` 指定固定牌(如红中)为癞子, `random` 发牌后翻一张牌确定精——翻到X则X+1为正精, X+2为副精(9→1循环, 字牌按序循环)。
**固定癞子牌名映射** (tiles 字段可用值)
| YAML 名称 | 引擎编码 |
|-----------|---------|