e6c63923d19be454bb7ab63dfb95462de168a5f7
只保留有hardcoded fallback值或event的番型在filter中 移除: 将一色/一色四同顺/一色三同顺/一般高/五门齐/全求人/门前清 → 这些只在DSL配置_fanConfig时才显示,否则被filter剔除 → allZeroValue→鸡胡兜底保证非零分 效果: 南昌显示'缺一门+鸡胡'(不再越权显示五门齐)
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_max,DSL 配置 |
| 番型互斥 | 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
Languages
C#
100%