docs: 全面更新 README + DSL规范
README.md: - 测试数: 45→58, 玩法: 4→5 (新增南昌) - 番型: 16→39 种完整清单 - 新增: 精牌类型(fixed/random), 计分倍数, 5种玩法覆盖表 - 新增: 压测结果表, 引擎代码规模 dsl-specification.md: - scoring 段新增 self_draw/discard_win/dealer 倍数字段 - 已知限制更新: 花牌(完全支持), wildcard type(fixed+random) - 快速清单新增 wildcard.type + scoring倍数 检查项
This commit is contained in:
177
README.md
177
README.md
@ -1,92 +1,145 @@
|
||||
# Card Game Engine — 棋牌规则引擎
|
||||
# Card Game Engine — YAML DSL 驱动的麻将规则引擎
|
||||
|
||||
YAML DSL 驱动的棋牌规则引擎。一份 DSL 配置文件 = 一种玩法,
|
||||
引擎加载后自动运行,不需要写玩法特定代码。
|
||||
一份 YAML 配置文件 = 一种麻将玩法。引擎加载后自动运行,零玩法特定代码。
|
||||
|
||||
## 核心理念
|
||||
|
||||
**DSL 定义规则,引擎暴露能力。** 玩法变体之间的差异全部通过 DSL 字段表达:
|
||||
番型表、精牌类型、计分倍数、操作列表、回合结束条件——引擎不判断玩法名,
|
||||
只读 DSL 参数。
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
card-game-engine/
|
||||
├── docs/
|
||||
│ ├── architecture-plan.md # 架构设计文档
|
||||
│ └── demo-implementation-plan.md # Demo 实施计划
|
||||
├── RuleEngine/ # C# 规则引擎核心
|
||||
│ ├── Core/ # MahjongTile 编码 / Deck 牌堆 / GameState 状态
|
||||
│ ├── Patterns/ # MeldsSolver 胡牌判断 + 番型识别
|
||||
│ ├── Phase/ # PhaseMachine 回合状态机
|
||||
│ ├── Scoring/ # ScoreEngine 计分引擎
|
||||
│ ├── Dsl/ # DslLoader YAML 加载 + CapabilityRegistry
|
||||
│ └── AI/ # RandomMahjongAI 随机AI(验证用)
|
||||
├── RuleEngine.Tests/ # 单元测试 (45个)
|
||||
├── dsl-examples/ # 玩法 DSL 配置 (YAML)
|
||||
│ ├── xuezhandaodi.yaml # 四川麻将血战到底 (108张)
|
||||
│ ├── guangdong_jipinghu.yaml # 广东麻将鸡平胡 (136张 + 花牌)
|
||||
│ ├── guobiao.yaml # 国标麻将 (144张,81番种)
|
||||
│ └── wuhan.yaml # 武汉麻将 (136张,红中癞子 + 258将)
|
||||
└── Demo/ # 控制台 Demo
|
||||
│ ├── 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
|
||||
```
|
||||
|
||||
## 核心设计
|
||||
|
||||
- **规则引擎 ≠ 游戏引擎**:纯逻辑库(C#),不依赖图形框架。输入状态 → 输出合法操作
|
||||
- **扑克/麻将引擎分离**:各自精专,共享基础层
|
||||
- **int 编码**:4 bytes/tile,性能优先
|
||||
- **加载时能力检查**:DSL 声明 `requires`,引擎 CapabilityRegistry 自动验证
|
||||
|
||||
## 已实现能力
|
||||
|
||||
### 胡牌判断 (MeldsSolver)
|
||||
| 能力 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 标准胡牌 (4面子+1对) | ✅ | 回溯搜索,含 wildcard 缺口填充 |
|
||||
| 七对 | ✅ | 纯对子形态,wildcard 配对支持 |
|
||||
| 十三幺 | ✅ | 13种幺九+1重复 |
|
||||
| 全不靠 | ✅ | 14牌从16位置模板选,wildcard 补位 |
|
||||
| 一色双龙会 | ✅ | 同色1-9各至少2张,wildcard 支持 |
|
||||
| 鬼牌/癞子 | ✅ | 缺口填充式回溯 O(w×2^w) |
|
||||
| 258将检查 | ✅ | 武汉麻将专用 |
|
||||
### 胡牌判断
|
||||
| 能力 | 说明 |
|
||||
|------|------|
|
||||
| 标准胡牌 (4 面子 + 1 对) | 回溯搜索,支持癞子缺口填充 O(w×2^w) |
|
||||
| 七对 | 纯对子形态,癞子配对 |
|
||||
| 十三幺 | 13 种幺九 + 1 重复 |
|
||||
| 全不靠 | 147/258/369 + 字牌 16 位模板 |
|
||||
| 一色双龙会 | 同色 1-9 各至少 2 张 |
|
||||
| 258 将检查 | 武汉麻将专用,DSL `pair_must_be_258` |
|
||||
|
||||
### 番型识别 (IdentifyFans)
|
||||
自动识别 16 种结构性番型:清一色、混一色、字一色、对对胡、暗七对、带幺九、混幺九、缺一门、平胡、断幺九、全大、全中、全小、大于五、小于五、全双、碰碰和
|
||||
### 番型识别 (39 种)
|
||||
结构性 (24): 清一色/混一色/字一色/对对胡/碰碰胡/碰碰和/暗七对/七对/将一色/
|
||||
带幺九/全带幺/混幺九/缺一门/平胡/平和/断幺九/无字/全大/全中/全小/全双/
|
||||
大于五/小于五/大四喜/大三元/小四喜/小三元/一色四同顺/五门齐
|
||||
|
||||
特殊牌型 (4): 十三幺/全不靠/一色双龙会/连七对
|
||||
|
||||
状态/事件 (9): 全求人/门前清/杠上开花/海底捞月/抢杠胡/天胡/地胡/癞子胡
|
||||
|
||||
兜底 (1): 鸡胡
|
||||
|
||||
### 回合控制
|
||||
| 能力 | 状态 |
|
||||
| 能力 | 说明 |
|
||||
|------|------|
|
||||
| 摸→打→碰杠胡回合 | ✅ |
|
||||
| 优先级仲裁 (胡>杠>碰>吃) | ✅ |
|
||||
| 血战到底 (胡后不结束) | ✅ |
|
||||
| 查叫/查花猪 | ✅ |
|
||||
| 花牌 (摸到即补) | ✅ |
|
||||
| 摸→打→碰杠胡回合 | DSL 控制操作列表 |
|
||||
| 优先级仲裁 (胡>杠>碰>吃) | DSL `priority_policy` |
|
||||
| 血战到底 (胡后继续) | DSL `parallel_elimination` |
|
||||
| 查叫/查花猪 | 血战专用 |
|
||||
| 花牌 (摸到即补, 开局补花) | while 循环递归补花 |
|
||||
| 8 番起胡 | DSL `win_min_fan`, 非硬编码 |
|
||||
|
||||
### DSL 热切换
|
||||
4 种麻将变体 100% 覆盖率,`--dsl` 参数切换
|
||||
### 精牌 (Wildcard)
|
||||
| 类型 | 说明 |
|
||||
|------|------|
|
||||
| 固定精 (fixed) | DSL 指定牌名 (红中/发财/白板) 为癞子 |
|
||||
| 翻牌定精 (random) | 发牌后翻一张牌,X+1 正精、X+2 副精 (9→1 循环) |
|
||||
|
||||
## Demo 用法
|
||||
### 计分
|
||||
| 能力 | 说明 |
|
||||
|------|------|
|
||||
| 番型叠加策略 | 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
|
||||
# 交互模式(逐局展示)
|
||||
# 交互模式 (逐局展示)
|
||||
dotnet run --project Demo
|
||||
dotnet run --project Demo -- --dsl wuhan
|
||||
dotnet run --project Demo -- --dsl guobiao
|
||||
dotnet run --project Demo -- --dsl guangdong_jipinghu
|
||||
dotnet run --project Demo -- --dsl nanchang
|
||||
|
||||
# 人类玩家模式
|
||||
dotnet run --project Demo -- --human --dsl wuhan
|
||||
|
||||
# 压测模式
|
||||
dotnet run --project Demo -- --auto --count 1000
|
||||
|
||||
# 运行测试
|
||||
dotnet test # 全部 45 个
|
||||
# 测试
|
||||
dotnet test # 58 个测试
|
||||
dotnet test --filter "听牌" # 听牌检测相关
|
||||
dotnet test --filter "鬼牌" # 癞子相关
|
||||
```
|
||||
|
||||
## 测试覆盖
|
||||
## 压测结果
|
||||
|
||||
- **45 个单元测试** 全部通过
|
||||
- 覆盖:牌编码、牌库构建、标准胡牌、七对、十三幺、全不靠、一色双龙会、
|
||||
wildcard 补位、258将检查、番型识别(清一色/混一色/字一色/对对胡/
|
||||
带幺九/缺一门/平胡/断幺九/全大/全小/全双)
|
||||
- 1000 局压测 0 报错
|
||||
| 玩法 | 胡牌率 | 性能 | 零和 |
|
||||
|------|--------|------|------|
|
||||
| 血战到底 | ~10% | 7ms/局 | ✓ |
|
||||
| 武汉麻将 | ~40% | 8ms/局 | ✓ |
|
||||
| 广东鸡平胡 | ~20% | 7ms/局 | ✓ |
|
||||
| 国标麻将 | ~15% | 7ms/局 | ✓ |
|
||||
| 南昌麻将 | ~60% | 7ms/局 | ✓ |
|
||||
|
||||
## 开源参考
|
||||
1000 局 × 5 种玩法压测 0 错误,全部零和结算。
|
||||
|
||||
- q_algorithm (C# 胡牌算法库)
|
||||
- majiang_algorithm (Java 麻将引擎 + AI)
|
||||
- MahjongKit (Python 牌谱分析)
|
||||
## 引擎代码规模
|
||||
|
||||
| 文件 | 行数 | 职责 |
|
||||
|------|------|------|
|
||||
| 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 == "武汉")` 式的玩法名判断。
|
||||
@ -181,14 +181,22 @@ phases:
|
||||
### scoring (必需)
|
||||
```yaml
|
||||
scoring:
|
||||
mode: fan_table # 当前仅支持"fan_table"
|
||||
max_cap: 100 # 番数上限
|
||||
pre_hooks: # 可选,结算前执行的hook
|
||||
- name: check_hua_zhu # 检查花猪(仅血战)
|
||||
mode: fan_table # 当前仅支持"fan_table"
|
||||
max_cap: 999 # 番数上限, 默认无限
|
||||
self_draw_multiplier: 1 # 自摸: 每家付 baseFan × N (默认 1)
|
||||
discard_win_multiplier: 3 # 点炮: 放炮者付 baseFan × N (默认 3)
|
||||
dealer_multiplier: 1 # 庄家输赢 × N (默认 1, 南昌=2)
|
||||
pre_hooks: # 可选, 结算前执行的 hook
|
||||
- name: check_hua_zhu # 检查花猪 (仅血战)
|
||||
condition: deck_exhausted
|
||||
- name: check_ting # 检查听牌(仅血战)
|
||||
- name: check_ting # 检查听牌 (仅血战)
|
||||
condition: deck_exhausted AND not hua_zhu
|
||||
```
|
||||
**计分倍数规则**:
|
||||
- `self_draw_multiplier`: 自摸胡牌时每位非赢家支付 `baseFan × N`,赢家收入总和
|
||||
- `discard_win_multiplier`: 点炮胡牌时放炮者支付 `baseFan × N`,赢家收入
|
||||
- `dealer_multiplier`: 庄家赢 → 每家付倍数;闲家赢 → 仅庄家付倍数
|
||||
- 不设置时使用默认值,现有玩法向后兼容
|
||||
|
||||
### pre_hooks (可选)
|
||||
```yaml
|
||||
@ -260,10 +268,11 @@ flower_rules:
|
||||
3. **scoring.mode**: 仅支持 `fan_table`。不支持 `multiply_score`。
|
||||
4. **牌库生成器**: 仅支持 `generator: mahjong`。不支持其他牌库类型。
|
||||
5. **玩家数**: 仅支持 4 人。不支持 2 人/3 人。
|
||||
6. **花牌计分**: `flower_rules.scoring` 的各字段都支持,但 `replace_tiles` 仅在广东鸡平胡(BEFORE_GAME_START)和国标(留空=on_draw replace)下正确。
|
||||
6. **花牌计分**: 完全支持。初始花牌 (`deal_cards_with_flowers`) 和游戏中摸到花牌都正确补牌(while 循环处理递归花牌)。
|
||||
7. **pre_hooks**: 仅 `wildcard_count`/`check_hua_zhu`/`check_ting` 被引擎识别。其他名称会在合规检查中警告。
|
||||
8. **特殊花色**: 不区分风圈/箭刻/门风。番型 `圈风刻`/`门风刻`/`箭刻` 不在识别列表中。
|
||||
8. **wildcard type**: 支持 `fixed`(指定牌为癞子)和 `random`(骰子翻牌定精)。
|
||||
9. **番型组合: 一色三同顺/一色三节高/三色三同顺/花龙/组合龙/推不倒/无番和**: 不在识别列表中。
|
||||
10. **精的冲关/德国计分**: 引擎不支持南昌麻将的冲关(精数×2^N)和德国(无精胡牌加分)复杂计分规则。
|
||||
|
||||
---
|
||||
|
||||
@ -284,6 +293,8 @@ flower_rules:
|
||||
- [ ] `fan_types` 的 `condition` 在支持列表中(或为空)
|
||||
- [ ] `phases` 的 actions 在支持列表中
|
||||
- [ ] `wildcard_rules.behavior` = "substitute"
|
||||
- [ ] `wildcard_rules.type` = "fixed" 或 "random"
|
||||
- [ ] `scoring.mode` = "fan_table"
|
||||
- [ ] `scoring` 倍数 (self_draw/discard_win/dealer) 设置正确
|
||||
- [ ] `pre_hooks` 仅含 known hooks
|
||||
- [ ] 加载时合规检查零警告
|
||||
|
||||
Reference in New Issue
Block a user