# 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()` 双层检查确保零假阳性。 ## 用法 ```bash # 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 == "武汉")` 式的玩法名判断。