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倍数 检查项
301 lines
12 KiB
Markdown
301 lines
12 KiB
Markdown
# 麻将引擎 DSL 生成规范 (AI-readable)
|
||
|
||
## 概述
|
||
|
||
此文档描述 Card Game Engine 麻将规则引擎支持的所有 DSL 字段及其有效值。AI 代理在创建新玩法 YAML 文件时,**只能使用本文档列出的字段和值**。未列出的字段/值将被引擎拒绝或忽略,导致规则无法正确执行。
|
||
|
||
引擎通过双层检查确保合规:
|
||
1. **能力注册检查** (`CapabilityRegistry.Check`):`requires` 列表中的每个能力必须已注册,否则拒绝加载
|
||
2. **规则合规检查** (`ValidateRuleCompliance`):逐条检查番型名/条件/hook/模式是否被引擎支持,列出具体 gap
|
||
|
||
---
|
||
|
||
## 字段索引 (所有支持的 DSL 字段)
|
||
|
||
### game (必需)
|
||
```yaml
|
||
game:
|
||
name: "玩法名称" # string, 任意
|
||
type: mahjong # 固定值
|
||
engine_type: mahjong # 固定值
|
||
players: { min: 4, max: 4 } # 当前仅支持4人
|
||
```
|
||
|
||
### requires (必需)
|
||
```yaml
|
||
requires:
|
||
- deck.generator_mahjong
|
||
- meldsolver.standard_win
|
||
- meldsolver.seven_pairs
|
||
- meldsolver.thirteen_orphans # 可选,需要十三幺时加
|
||
- meldsolver.all_orphans # 可选,需要全不靠时加
|
||
- meldsolver.double_dragon # 可选,需要一色双龙会时加
|
||
- meldsolver.wildcard # 可选,有癞子时加
|
||
- deck.flower_cards # 可选,有花牌时加
|
||
- phase.mahjong_turn
|
||
- phase.parallel_elimination # 可选,血战模式时加
|
||
- phase.priority_arbitration
|
||
- scoring.fan_exclusion
|
||
- scoring.pre_hooks # 可选,需要结算前hook时加
|
||
```
|
||
|
||
### deck (必需)
|
||
```yaml
|
||
deck:
|
||
generator: mahjong # 固定值
|
||
include_honors: true # bool, 是否含字牌(东南西北中发白)
|
||
include_flowers: true # bool, 是否含花牌(春夏秋冬梅兰竹菊)
|
||
total: 136 # 牌库总张数。108=无字无花, 136=有字无花, 144=有字有花
|
||
```
|
||
**注意**:`total` 必须匹配 `include_honors` + `include_flowers` 组合,否则引擎加载时抛异常。
|
||
- `include_honors: false, include_flowers: false` → `total: 108`
|
||
- `include_honors: true, include_flowers: false` → `total: 136`
|
||
- `include_honors: true, include_flowers: true` → `total: 144`
|
||
- wildcardCount>0 时额外加对应张数
|
||
|
||
### deal (必需)
|
||
```yaml
|
||
deal:
|
||
cards_per_player: 13 # 固定值
|
||
dealer_extra: 1 # 固定值
|
||
```
|
||
|
||
### wildcard_rules (可选,有癞子时必填)
|
||
```yaml
|
||
wildcard_rules:
|
||
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 名称 | 引擎编码 |
|
||
|-----------|---------|
|
||
| 红中 | 35 |
|
||
| 发财 | 36 |
|
||
| 白板 | 37 |
|
||
|
||
**限制**:`behavior` 仅支持 `substitute`,`fan_calculation_policy` 仅支持 `optimal`。其他值会在合规检查中警告。
|
||
|
||
### win_condition (可选)
|
||
```yaml
|
||
win_condition:
|
||
pair_must_be_258: true # bool, 是否要求将牌必须是2/5/8
|
||
```
|
||
|
||
### fan_types (必需)
|
||
```yaml
|
||
fan_types:
|
||
- name: 清一色 # 番型名(见下方"引擎识别的番型名")
|
||
base_fan: 8 # 基础番数
|
||
level: 8 # 等级(用于add_max/max_level策略)
|
||
excludes: [缺一门, 无字] # 互斥番型(有此番型时不计算被排除的)
|
||
conflicts: [暗七对] # 冲突番型(取番数高的)
|
||
condition: "" # 条件(见下方"支持的条件字符串")
|
||
```
|
||
**注意**:`name` 必须是引擎能识别的番型名(见下方列表),否则得分为0。`condition` 必须是引擎支持的条件字符串。
|
||
|
||
### fan_stacking (必需)
|
||
```yaml
|
||
fan_stacking: add # "add"=所有番型相加, "max_level"=仅最高level, "add_max"=每level取最大后相加
|
||
```
|
||
|
||
### max_fan (可选)
|
||
```yaml
|
||
max_fan: 100 # 单局最高番数上限,默认无限
|
||
```
|
||
|
||
### win_min_fan (可选)
|
||
```yaml
|
||
win_min_fan: 8 # 最低胡牌番数要求(如国标8番起胡), 0=无限制
|
||
```
|
||
|
||
### win_rule (可选)
|
||
```yaml
|
||
win_rule:
|
||
ji_hu_self_draw_only: true # 鸡胡只能自摸(广东鸡平胡)
|
||
```
|
||
|
||
### phases (必需)
|
||
```yaml
|
||
phases:
|
||
- name: deal
|
||
type: auto
|
||
action: deal_cards # 标准发牌。有花牌+开局换花用 deal_cards_with_flowers
|
||
next: play
|
||
|
||
- name: play
|
||
type: mahjong_turn
|
||
turn_order: counter_clockwise
|
||
parallel_elimination: false # true=血战到底模式
|
||
sub_phases:
|
||
draw:
|
||
type: auto
|
||
action: draw_card
|
||
on_draw_flower: replace # 摸到花牌时补牌(有花牌的变体)
|
||
on_empty_deck: exhausted # 牌墙耗尽行为
|
||
self_action:
|
||
options:
|
||
- { action: discard }
|
||
- { action: an_kong } # 可选,有暗杠时加
|
||
- { action: bu_kong, condition: has_punged_pair } # 可选,有加杠时加
|
||
- { action: win, condition: can_win_tumo } # 可选,可自摸时加
|
||
others_reaction:
|
||
options:
|
||
- { action: chi, priority: 1, condition: can_chi } # 可选,有吃时加
|
||
- { action: pung, priority: 2, condition: has_two_same }
|
||
- { action: ming_kong, priority: 3, condition: has_three_same }
|
||
- { action: win, priority: 4, condition: can_win }
|
||
- { action: pass, priority: 0 }
|
||
priority_policy: highest_wins
|
||
on_win: game_over
|
||
end_conditions:
|
||
- { type: player_wins, action: settle }
|
||
- { type: deck_exhausted, action: draw_game }
|
||
|
||
- name: settle
|
||
type: auto
|
||
action: calculate_scores
|
||
next: null
|
||
```
|
||
**action 选项**:可用的 self_action:discard/an_kong/bu_kong/win。可用的 others_reaction:chi/pung/ming_kong/win/pass。**不列出的 action 将不可用**。
|
||
|
||
**支持的条件字符串**:
|
||
| 条件 | 含义 |
|
||
|------|------|
|
||
| `can_win_tumo` | 可以自摸胡 |
|
||
| `can_win_tumo_and_fan_ge_8` | 自摸+番数≥8 |
|
||
| `can_win` | 可以吃胡 |
|
||
| `can_win_and_fan_ge_8` | 吃胡+番数≥8 |
|
||
| `has_punged_pair` | 有碰过的对子(加杠) |
|
||
| `has_two_same` | 手中有2张相同 |
|
||
| `has_three_same` | 手中有3张相同 |
|
||
| `can_chi` | 可以吃 |
|
||
|
||
### scoring (必需)
|
||
```yaml
|
||
scoring:
|
||
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 # 检查听牌 (仅血战)
|
||
condition: deck_exhausted AND not hua_zhu
|
||
```
|
||
**计分倍数规则**:
|
||
- `self_draw_multiplier`: 自摸胡牌时每位非赢家支付 `baseFan × N`,赢家收入总和
|
||
- `discard_win_multiplier`: 点炮胡牌时放炮者支付 `baseFan × N`,赢家收入
|
||
- `dealer_multiplier`: 庄家赢 → 每家付倍数;闲家赢 → 仅庄家付倍数
|
||
- 不设置时使用默认值,现有玩法向后兼容
|
||
|
||
### pre_hooks (可选)
|
||
```yaml
|
||
pre_hooks:
|
||
- name: wildcard_count # 统计癞子数量
|
||
description: "描述文字"
|
||
```
|
||
**支持的 pre_hook 名**:`wildcard_count`, `check_hua_zhu`, `check_ting`
|
||
|
||
### flower_rules (可选,有花牌时)
|
||
```yaml
|
||
flower_rules:
|
||
on_draw: replace_and_draw # 摸花牌时补牌
|
||
replace_tiles: BEFORE_GAME_START # 开局前换花牌(广东) 或留空=游戏中换花牌(国标)
|
||
scoring:
|
||
normal: 1 # 每张花牌基础分
|
||
matching:
|
||
value: 1 # 配对花牌额外分
|
||
mapping:
|
||
1: [春, 梅]
|
||
2: [夏, 兰]
|
||
3: [秋, 竹]
|
||
4: [冬, 菊]
|
||
```
|
||
|
||
### fu_flag (可选,过水机制,仅血战到底)
|
||
```yaml
|
||
# 在 phases.play 下:
|
||
fu_flag:
|
||
type: dirty_flag
|
||
set_on: can_win_but_pass
|
||
clear_on: next_discard_self
|
||
effect: block_win_on_current_tile
|
||
```
|
||
|
||
---
|
||
|
||
## 引擎识别的番型名 (39种)
|
||
|
||
以下番型名可以被引擎的结构识别器检测到。**使用这些名称时引擎会正确识别并计分,使用其他名称则得分为0。**
|
||
|
||
### 结构性番型 (始终可识别)
|
||
清一色, 混一色, 字一色, 对对胡, 碰碰胡, 碰碰和, 暗七对, 七对,
|
||
将一色, 带幺九, 全带幺, 混幺九, 缺一门, 无字, 平胡, 平和, 断幺九,
|
||
全大, 全中, 全小, 全双, 大于五, 小于五,
|
||
大四喜, 大三元, 小四喜, 小三元, 一色四同顺, 五门齐
|
||
|
||
### 特殊牌型 (在 CheckWin 特定路径中识别)
|
||
十三幺, 全不靠, 一色双龙会, 连七对
|
||
|
||
### 状态相关 (需要游戏上下文)
|
||
全求人, 门前清
|
||
|
||
### 事件番型 (需要引擎设置对应标志位)
|
||
杠上开花, 海底捞月, 抢杠胡, 天胡, 地胡
|
||
|
||
### 条件番型
|
||
癞子胡 (条件: hand_contains_wildcard)
|
||
|
||
### 兜底番型
|
||
鸡胡 (当没有其他番型被识别时自动添加)
|
||
|
||
---
|
||
|
||
## 已知限制 (不会在合规检查中警告,但确实不可用)
|
||
|
||
1. **behavior**: 仅支持 `substitute`。不支持 `universal`/`multiplier` 等其他行为。
|
||
2. **fan_calculation_policy**: 仅支持 `optimal`(单次回溯找可行解)。不支持 `average`/`fixed`。
|
||
3. **scoring.mode**: 仅支持 `fan_table`。不支持 `multiply_score`。
|
||
4. **牌库生成器**: 仅支持 `generator: mahjong`。不支持其他牌库类型。
|
||
5. **玩家数**: 仅支持 4 人。不支持 2 人/3 人。
|
||
6. **花牌计分**: 完全支持。初始花牌 (`deal_cards_with_flowers`) 和游戏中摸到花牌都正确补牌(while 循环处理递归花牌)。
|
||
7. **pre_hooks**: 仅 `wildcard_count`/`check_hua_zhu`/`check_ting` 被引擎识别。其他名称会在合规检查中警告。
|
||
8. **wildcard type**: 支持 `fixed`(指定牌为癞子)和 `random`(骰子翻牌定精)。
|
||
9. **番型组合: 一色三同顺/一色三节高/三色三同顺/花龙/组合龙/推不倒/无番和**: 不在识别列表中。
|
||
10. **精的冲关/德国计分**: 引擎不支持南昌麻将的冲关(精数×2^N)和德国(无精胡牌加分)复杂计分规则。
|
||
|
||
---
|
||
|
||
## 创建新 DSL 的完整流程
|
||
|
||
1. 从现有示例复制模板(推荐 `dsl-examples/wuhan.yaml`)
|
||
2. 修改 `game.name`
|
||
3. 选择 `deck` (牌数) 和 `requires` (能力)
|
||
4. 选择 `fan_types` (仅使用上表格中的番型名,其他名称得分为0)
|
||
5. 选择 `fan_stacking` 策略
|
||
6. 选择 `phases` 中的 actions (不列出=不可用)
|
||
7. 如果需要 wildcard/花牌/fu_flag,添加对应段
|
||
8. 运行 `dotnet run --project Demo -- --dsl your_file --count 5` 验证
|
||
|
||
### 快速检查清单
|
||
- [ ] `deck.total` 与 `include_honors/include_flowers` 匹配
|
||
- [ ] `fan_types` 所有 `name` 在引擎识别列表中
|
||
- [ ] `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
|
||
- [ ] 加载时合规检查零警告
|