xiaoou c1bcdc42a2 fix: ComputeNextTiles 正精=翻牌本身(非+1)
南昌麻将规则: 翻到X, 正精=X(翻牌本身), 副精=X+1(9→1循环)
之前错误实现: 正精=X+1, 副精=X+2
修复: 循环从i=0开始(含翻牌本身)

验证: 翻5万→正精5万/副精6万, 翻9筒→正精9筒/副精1筒
2026-07-04 23:45:36 +08:00

Card Game Engine — YAML DSL 驱动的麻将规则引擎

一份 YAML 配置文件 = 一种麻将玩法。引擎加载后自动运行,零玩法特定代码。

核心理念

DSL 定义规则,引擎暴露能力。 玩法变体之间的差异全部通过 DSL 字段表达: 番型表、精牌类型、计分倍数、操作列表、回合结束条件——引擎不判断玩法名, 只读 DSL 参数。

项目结构

card-game-engine/
├── docs/
│   ├── dsl-specification.md          # DSL 完整字段参考 (AI 可读)
│   ├── fan-recognizer-inventory.md   # 39 种番型识别器清单
│   ├── macos-build-deploy.md         # macOS 构建指南
│   └── architecture-plan.md          # 架构设计历史
├── RuleEngine/                       # C# 引擎核心 (~3000 行)
│   ├── Core/                         # 牌编码 / 牌堆 / 状态 / 癞子注册表
│   ├── Patterns/                     # 胡牌判断 (MeldsSolver) + 番型识别
│   ├── Phase/                        # 回合状态机 + 合法操作
│   ├── Scoring/                      # 计分引擎 + 花牌/癞子 bonus
│   ├── Dsl/                          # YAML 加载 + 双层合规检查
│   └── AI/                           # 随机 AI + 人类玩家 CLI
├── RuleEngine.Tests/                 # 58 个单元测试
├── dsl-examples/                     # 5 种麻将玩法 DSL
│   ├── xuezhandaodi.yaml             # 四川血战到底 (108 张)
│   ├── wuhan.yaml                    # 武汉麻将 (136 张, 红中癞子, 258 将)
│   ├── guangdong_jipinghu.yaml       # 广东鸡平胡 (144 张, 花牌)
│   ├── guobiao.yaml                  # 国标麻将 (144 张, 81 番种, 8 番起胡)
│   └── nanchang.yaml                 # 南昌麻将 (136 张, 骰子翻牌定精, 庄×2)
└── Demo/                             # 控制台 Demo

已实现能力

胡牌判断

能力 说明
标准胡牌 (4 面子 + 1 对) 回溯搜索,支持癞子缺口填充 O(w×2^w)
七对 纯对子形态,癞子配对
十三幺 13 种幺九 + 1 重复
全不靠 147/258/369 + 字牌 16 位模板
一色双龙会 同色 1-9 各至少 2 张
258 将检查 武汉麻将专用DSL pair_must_be_258

番型识别 (39 种)

结构性 (24): 清一色/混一色/字一色/对对胡/碰碰胡/碰碰和/暗七对/七对/将一色/ 带幺九/全带幺/混幺九/缺一门/平胡/平和/断幺九/无字/全大/全中/全小/全双/ 大于五/小于五/大四喜/大三元/小四喜/小三元/一色四同顺/五门齐

特殊牌型 (4): 十三幺/全不靠/一色双龙会/连七对

状态/事件 (9): 全求人/门前清/杠上开花/海底捞月/抢杠胡/天胡/地胡/癞子胡

兜底 (1): 鸡胡

回合控制

能力 说明
摸→打→碰杠胡回合 DSL 控制操作列表
优先级仲裁 (胡>杠>碰>吃) DSL priority_policy
血战到底 (胡后继续) DSL parallel_elimination
查叫/查花猪 血战专用
花牌 (摸到即补, 开局补花) while 循环递归补花
8 番起胡 DSL win_min_fan, 非硬编码

精牌 (Wildcard)

类型 说明
固定精 (fixed) DSL 指定牌名 (红中/发财/白板) 为癞子
翻牌定精 (random) 发牌后翻一张牌X+1 正精、X+2 副精 (9→1 循环)

计分

能力 说明
番型叠加策略 add / max_level / add_maxDSL 配置
番型互斥 excludes / conflicts 图,引擎自动应用
计分倍数 self_draw / discard_win / dealer 倍数DSL 可配
花牌计分 每张 +1 番 + 座位配对额外分
癞子 bonus 胡牌手牌中每张癞子加分

5 种玩法 DSL 覆盖率

                      血战  武汉  广东  国标  南昌
声明式 DSL              9    3    9    11    9
内置算法                7    7    7     6    9
引擎能力 (capability)   7    7    7    10    7  
番型识别               14   14   14    22   14
─────────────────────────────────────────────
覆盖完整性              ✓    ✓    ✓     ✓    ✓

覆盖完整性 = 所有 DSL 配置的番型/条件/hook 均被引擎消费。 ValidateRuleCompliance() 双层检查确保零假阳性。

用法

# DSL 覆盖验证 (只加载不跑牌局)
dotnet run --project Demo -- --check --dsl your_variant
dotnet run --project Demo -- --check                   # 默认血战

# 交互模式 (逐局展示)
dotnet run --project Demo
dotnet run --project Demo -- --dsl wuhan
dotnet run --project Demo -- --dsl nanchang

# 人类玩家模式
dotnet run --project Demo -- --human --dsl wuhan

# 压测模式
dotnet run --project Demo -- --auto --count 1000

# 测试
dotnet test                    # 58 个测试
dotnet test --filter "听牌"     # 听牌检测相关
dotnet test --filter "鬼牌"     # 癞子相关

压测结果

玩法 胡牌率 性能 零和
血战到底 ~10% 7ms/局
武汉麻将 ~40% 8ms/局
广东鸡平胡 ~20% 7ms/局
国标麻将 ~15% 7ms/局
南昌麻将 ~60% 7ms/局

1000 局 × 5 种玩法压测 0 错误,全部零和结算。

引擎代码规模

文件 行数 职责
MahjongRoom.cs 866 牌局编排
MeldsSolver.cs 808 胡牌 + 番型
DslLoader.cs 299 DSL 加载
PhaseMachine.cs 260 回合状态机
ScoreEngine.cs 174 计分引擎
其余 7 个文件 ~581 牌编码 / 状态 / AI
总计 2988

引擎 95% 通用逻辑5% 变体特殊逻辑(已通过 DSL 参数化)。 零 if (gameName == "武汉") 式的玩法名判断。

Description
YAML DSL 驱动的棋牌规则引擎 — 麻将 + 扑克,玩法即配置
Readme 1 MiB
Languages
C# 100%