Files
card-game-engine/docs/dsl-specification.md
xiaoou a338dac139 feat: --check 标志只验证DSL覆盖率不跑牌局
用法: dotnet run --project Demo -- --check --dsl new_variant
输出: 能力覆盖率 + 合规检查 + 仅验证模式退出

文档: README + dsl-specification 更新创建流程
2026-07-04 22:09:39 +08:00

12 KiB
Raw Blame History

麻将引擎 DSL 生成规范 (AI-readable)

概述

此文档描述 Card Game Engine 麻将规则引擎支持的所有 DSL 字段及其有效值。AI 代理在创建新玩法 YAML 文件时,只能使用本文档列出的字段和值。未列出的字段/值将被引擎拒绝或忽略,导致规则无法正确执行。

引擎通过双层检查确保合规:

  1. 能力注册检查 (CapabilityRegistry.Check)requires 列表中的每个能力必须已注册,否则拒绝加载
  2. 规则合规检查 (ValidateRuleCompliance):逐条检查番型名/条件/hook/模式是否被引擎支持,列出具体 gap

字段索引 (所有支持的 DSL 字段)

game (必需)

game:
  name: "玩法名称"        # string, 任意
  type: mahjong           # 固定值
  engine_type: mahjong    # 固定值
  players: { min: 4, max: 4 }  # 当前仅支持4人

requires (必需)

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 (必需)

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: falsetotal: 108
  • include_honors: true, include_flowers: falsetotal: 136
  • include_honors: true, include_flowers: truetotal: 144
  • wildcardCount>0 时额外加对应张数

deal (必需)

deal:
  cards_per_player: 13     # 固定值
  dealer_extra: 1          # 固定值

wildcard_rules (可选,有癞子时必填)

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 仅支持 substitutefan_calculation_policy 仅支持 optimal。其他值会在合规检查中警告。

win_condition (可选)

win_condition:
  pair_must_be_258: true   # bool, 是否要求将牌必须是2/5/8

fan_types (必需)

fan_types:
  - name: 清一色           # 番型名(见下方"引擎识别的番型名")
    base_fan: 8            # 基础番数
    level: 8               # 等级(用于add_max/max_level策略)
    excludes: [缺一门, 无字]  # 互斥番型(有此番型时不计算被排除的)
    conflicts: [暗七对]    # 冲突番型(取番数高的)
    condition: ""           # 条件(见下方"支持的条件字符串")

注意name 必须是引擎能识别的番型名见下方列表否则得分为0。condition 必须是引擎支持的条件字符串。

fan_stacking (必需)

fan_stacking: add          # "add"=所有番型相加, "max_level"=仅最高level, "add_max"=每level取最大后相加

max_fan (可选)

max_fan: 100               # 单局最高番数上限,默认无限

win_min_fan (可选)

win_min_fan: 8             # 最低胡牌番数要求(如国标8番起胡), 0=无限制

win_rule (可选)

win_rule:
  ji_hu_self_draw_only: true  # 鸡胡只能自摸(广东鸡平胡)

phases (必需)

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 (必需)

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 (可选)

pre_hooks:
  - name: wildcard_count    # 统计癞子数量
    description: "描述文字"

支持的 pre_hook 名wildcard_count, check_hua_zhu, check_ting

flower_rules (可选,有花牌时)

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 (可选,过水机制,仅血战到底)

# 在 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 -- --check --dsl your_file
    
    输出示例:
    ✅ DSL 适配率: 7/7 (100%) — 南昌麻将
    ✅ 仅验证模式 — 未运行牌局
    
    如果有合规警告会在适配率下方显示
  9. 验证玩法可运行 (100 局压测):
    dotnet run --project Demo -- --dsl your_file --auto --count 100
    

快速检查清单

  • deck.totalinclude_honors/include_flowers 匹配
  • fan_types 所有 name 在引擎识别列表中
  • fan_typescondition 在支持列表中(或为空)
  • 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
  • 加载时合规检查零警告