docs: AI-readable DSL规范+番型识别器清单

dsl-specification.md: 新玩法生成指南
- 全部支持的DSL字段+有效值+约束
- 39种引擎识别番型名
- 支持的条件字符串/action/pre_hook
- 已知限制清单
- 创建新DSL的完整流程+检查清单

fan-recognizer-inventory.md: 番型识别器清单
- 每种番型的检测条件
- 事件番型的标志位映射
- 未实现的番型列表(使用将得分为0)

验证: AI按规范生成的南昌麻将(fixed wildcard发财)
→ 100%合规零警告,正常运行
This commit is contained in:
xiaoou
2026-07-04 20:53:40 +08:00
parent 721333390a
commit 5b60255f02
2 changed files with 368 additions and 0 deletions

286
docs/dsl-specification.md Normal file
View File

@ -0,0 +1,286 @@
# 麻将引擎 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"=指定牌当癞子 或 留空=编码50+的独立癞子牌
tiles: [红中] # 当type=fixed时,b必须使用这些名称
wildcard_encoding: 50 # 独立癞子牌的起始编码,默认50(范围50-59)
behavior: substitute # 当前仅支持"substitute"
fan_calculation_policy: optimal # 当前仅支持"optimal"
scoring:
per_wildcard_in_win: 1 # 每张癞子额外加番,0=不加
```
**固定癞子牌名映射** (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_actiondiscard/an_kong/bu_kong/win。可用的 others_reactionchi/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: 100 # 番数上限
pre_hooks: # 可选,结算前执行的hook
- name: check_hua_zhu # 检查花猪(仅血战)
condition: deck_exhausted
- name: check_ting # 检查听牌(仅血战)
condition: deck_exhausted AND not hua_zhu
```
### 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. **花牌计分**: `flower_rules.scoring` 的各字段都支持,但 `replace_tiles` 仅在广东鸡平胡(BEFORE_GAME_START)和国标(留空=on_draw replace)下正确。
7. **pre_hooks**: 仅 `wildcard_count`/`check_hua_zhu`/`check_ting` 被引擎识别。其他名称会在合规检查中警告。
8. **特殊花色**: 不区分风圈/箭刻/门风。番型 `圈风刻`/`门风刻`/`箭刻` 不在识别列表中。
9. **番型组合: 一色三同顺/一色三节高/三色三同顺/花龙/组合龙/推不倒/无番和**: 不在识别列表中。
---
## 创建新 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"
- [ ] `scoring.mode` = "fan_table"
- [ ] `pre_hooks` 仅含 known hooks
- [ ] 加载时合规检查零警告

View File

@ -0,0 +1,82 @@
# 番型识别器清单 (39种)
此文档列出引擎 `IdentifyFans(result, state?)` 能识别的全部番型名及其检测条件。
## 结构性番型 (基于手牌 melds/pair 结构检测)
| 番型名 | 检测条件 | 别名 |
|--------|---------|------|
| 清一色 | 全部数牌同花色,无字牌 | - |
| 混一色 | 全部数牌同花色+含字牌 | - |
| 字一色 | 全部为字牌 | - |
| 对对胡 | 全部面子为刻子 | 碰碰胡, 碰碰和 |
| 暗七对 | 全部面子为对子(来自七对路径) | 七对 |
| 将一色 | 全部为2/5/8数牌 | - |
| 带幺九 | 全部面子含幺九牌,将对为幺九 | 全带幺 |
| 混幺九 | 全部为幺九或字牌,且两者都有 | - |
| 缺一门 | 数牌最多2种花色 | - |
| 无字 | 全部为数牌,无字牌 | - |
| 平胡 | 全部面子为顺子 | 平和 |
| 断幺九 | 全部为中张数牌(2-8,无幺九无字) | - |
| 全大 | 全部数牌rank≥7 | - |
| 全中 | 全部数牌rank 4-6 | - |
| 全小 | 全部数牌rank≤3 | - |
| 全双 | 全部数牌rank为偶数 | - |
| 大于五 | 全部数牌rank>5 | - |
| 小于五 | 全部数牌rank<5 | - |
| 大四喜 | 4个风刻(东南西北) | - |
| 大三元 | 3个箭刻(中发白) | - |
| 小四喜 | 3个风刻+风将 | - |
| 小三元 | 2个箭刻+箭将 | - |
| 一色四同顺 | 4个完全相同的顺子 | - |
| 五门齐 | 3种数牌+字牌共计4类 | - |
## 特殊牌型 (在 CheckWin 特定路径中,不经过标准面子提取)
| 番型名 | 检测路径 | 说明 |
|--------|---------|------|
| 十三幺 | TryThirteenOrphans | 13种幺九+1对 |
| 全不靠 | TryAllOrphans | 147/258/369+字牌 |
| 一色双龙会 | TryDoubleDragon | 同花色1-9各2张 |
| 连七对 | TrySevenPairs(同色连7对) | 同花色的连续7对(未单独实现,由七对路径检测后通过DSL名匹配) |
## 状态相关番型 (需要传入 state 参数)
| 番型名 | 检测条件 |
|--------|---------|
| 全求人 | melds=0 allTiles2 (即全部副露) |
| 门前清 | state.Exposed 全部为空(无碰//) |
## 事件番型 (需要 state 中的事件标志位)
| 番型名 | 标志位 | 设置时机 |
|--------|--------|---------|
| 杠上开花 | state.IsKongDraw | 杠后摸牌时 |
| 海底捞月 | state.IsLastTile | 摸牌前deck只剩1张 |
| 抢杠胡 | state.IsRobbedKong | 加杠时 |
| 天胡 | state.IsFirstTurn && state.DealerOnFirstTurn | 庄家首轮 |
| 地胡 | state.IsFirstTurn && !state.DealerOnFirstTurn | 非庄家首轮 |
## 条件番型
| 番型名 | 条件 | 检测方式 |
|--------|------|---------|
| 癞子胡 | hand_contains_wildcard | state.Wildcards.CountWildcards(allTiles) > 0 |
## 兜底番型
| 番型名 | 触发条件 |
|--------|---------|
| 鸡胡 | 上述所有番型都未识别时自动添加 |
---
## 未实现的番型 (使用这些名称将得分为0)
以下番型引擎无法识别,在合规检查中会列出警告:
**结构类**: 绿一色, 九莲宝灯, 四杠子, 一色三同顺, 一色三节高, 三色三同顺, 三色三节高, 一色三步高, 三色三步高, 花龙, 组合龙, 推不倒, 无番和, 清龙, 三色双龙会, 全带五, 一色三高, 一色四高, 三风刻, 三杠, 四归一, 双暗刻, 双同刻, 双暗杠, 双明杠, 老少副, 连六, 喜相逢, 一般高, 幺九刻
**事件类**: 圈风刻, 门风刻, 箭刻, 不求人, 自摸(作为独立番型)
**条件类**: 边张, 坎张, 单钓将