[verified] feat: 麻将规则引擎 Demo 完整实现
RuleEngine 核心类: - MahjongTile.cs — int编码 (1-29万条筒, 31-37字, 41-48花, 50-59宝牌) - MeldsSolver.cs — 标准回溯 + wildcard缺口填充 + 七对/十三幺/全不靠 - PhaseMachine.cs — 回合机(摸打碰杠胡 + 优先级仲裁) - ScoreEngine.cs — 番型计分 + 互斥图 - DslLoader.cs — YAML DSL加载 + 能力检查 4个DSL: 四川血战/广东鸡平胡/国标麻将/武汉麻将 控制台Demo: 交互模式 + 自动模式(--auto) 测试: 33个测试用例, 32个通过
This commit is contained in:
103
dsl-examples/guangdong_jipinghu.yaml
Normal file
103
dsl-examples/guangdong_jipinghu.yaml
Normal file
@ -0,0 +1,103 @@
|
||||
game:
|
||||
name: 广东麻将鸡平胡
|
||||
type: mahjong
|
||||
engine_type: mahjong
|
||||
players: { min: 4, max: 4 }
|
||||
|
||||
requires:
|
||||
- meldsolver.standard_win
|
||||
- meldsolver.seven_pairs
|
||||
- meldsolver.thirteen_orphans
|
||||
- deck.flower_cards
|
||||
- phase.mahjong_turn
|
||||
- phase.priority_arbitration
|
||||
- scoring.fan_exclusion
|
||||
|
||||
deck:
|
||||
generator: mahjong
|
||||
includeHonors: true
|
||||
includeFlowers: true
|
||||
total: 136
|
||||
|
||||
deal:
|
||||
cards_per_player: 13
|
||||
dealer_extra: 1
|
||||
|
||||
flower_rules:
|
||||
on_draw: replace_and_draw
|
||||
replace_tiles: BEFORE_GAME_START
|
||||
scoring:
|
||||
normal: 1
|
||||
matching:
|
||||
value: 1
|
||||
mapping:
|
||||
1: [春, 梅]
|
||||
2: [夏, 兰]
|
||||
3: [秋, 竹]
|
||||
4: [冬, 菊]
|
||||
|
||||
fan_types:
|
||||
# 鸡胡(1番,只能自摸)
|
||||
- { name: 鸡胡, base_fan: 1, level: 1 }
|
||||
# 平胡(4番)
|
||||
- { name: 清一色, base_fan: 8, level: 3, excludes: [缺一门, 无字] }
|
||||
- { name: 对对胡, base_fan: 4, level: 2, conflicts: [暗七对] }
|
||||
- { name: 暗七对, base_fan: 8, level: 3, conflicts: [对对胡] }
|
||||
- { name: 混一色, base_fan: 4, level: 2, excludes: [缺一门] }
|
||||
- { name: 带幺九, base_fan: 4, level: 2, excludes: [缺一门] }
|
||||
# 爆胡(8番+)
|
||||
- { name: 十三幺, base_fan: 32, level: 3, excludes: [五门齐, 门前清] }
|
||||
- { name: 大四喜, base_fan: 32, level: 3 }
|
||||
- { name: 大三元, base_fan: 32, level: 3 }
|
||||
- { name: 杠上开花, base_fan: 1, level: 1, excludes: [海底捞月] }
|
||||
- { name: 海底捞月, base_fan: 1, level: 1 }
|
||||
- { name: 抢杠胡, base_fan: 1, level: 1 }
|
||||
|
||||
fan_stacking: max_level
|
||||
max_fan: 999
|
||||
|
||||
win_rule:
|
||||
ji_hu_self_draw_only: true # 鸡胡只能自摸
|
||||
|
||||
phases:
|
||||
- name: deal
|
||||
type: auto
|
||||
action: deal_cards_with_flowers
|
||||
next: play
|
||||
|
||||
- name: play
|
||||
type: mahjong_turn
|
||||
turn_order: counter_clockwise
|
||||
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
|
||||
|
||||
scoring:
|
||||
mode: fan_table
|
||||
max_cap: 999
|
||||
103
dsl-examples/guobiao.yaml
Normal file
103
dsl-examples/guobiao.yaml
Normal file
@ -0,0 +1,103 @@
|
||||
game:
|
||||
name: 国标麻将
|
||||
type: mahjong
|
||||
engine_type: mahjong
|
||||
players: { min: 4, max: 4 }
|
||||
|
||||
requires:
|
||||
- deck.generator_mahjong
|
||||
- deck.flower_cards
|
||||
- meldsolver.standard_win
|
||||
- meldsolver.seven_pairs
|
||||
- meldsolver.thirteen_orphans
|
||||
- meldsolver.all_orphans
|
||||
- meldsolver.double_dragon
|
||||
- phase.mahjong_turn
|
||||
- phase.priority_arbitration
|
||||
- scoring.fan_exclusion
|
||||
|
||||
deck:
|
||||
generator: mahjong
|
||||
includeHonors: true
|
||||
includeFlowers: true
|
||||
total: 144
|
||||
|
||||
deal:
|
||||
cards_per_player: 13
|
||||
dealer_extra: 1
|
||||
|
||||
flower_rules:
|
||||
on_draw: replace_and_draw
|
||||
scoring:
|
||||
normal: 1
|
||||
matching:
|
||||
value: 1
|
||||
mapping:
|
||||
1: [春, 梅]
|
||||
2: [夏, 兰]
|
||||
3: [秋, 竹]
|
||||
4: [冬, 菊]
|
||||
|
||||
win_min_fan: 8
|
||||
fan_stacking: add_max
|
||||
|
||||
fan_types:
|
||||
# 88番
|
||||
- { name: 大四喜, base_fan: 88, level: 12, excludes: [圈风, 门风, 三风] }
|
||||
- { name: 大三元, base_fan: 88, level: 12, excludes: [双箭刻] }
|
||||
- { name: 十三幺, base_fan: 88, level: 12, excludes: [五门齐, 门前清, 单钓将, 混幺九] }
|
||||
- { name: 连七对, base_fan: 88, level: 12, excludes: [七对, 门前清, 单钓将, 清一色, 无字] }
|
||||
# 64番
|
||||
- { name: 小四喜, base_fan: 64, level: 11, excludes: [三风] }
|
||||
- { name: 小三元, base_fan: 64, level: 11, excludes: [双箭刻] }
|
||||
- { name: 字一色, base_fan: 64, level: 11, excludes: [碰碰和, 全带幺, 混幺九, 缺一门] }
|
||||
# 48番
|
||||
- { name: 一色四同顺, base_fan: 48, level: 10, excludes: [一色三同顺, 四归一, 一般高] }
|
||||
# 24番
|
||||
- { name: 清一色, base_fan: 24, level: 8, excludes: [无字, 缺一门] }
|
||||
- { name: 七对, base_fan: 24, level: 8, excludes: [门前清, 单钓将] }
|
||||
- { name: 全双, base_fan: 24, level: 8 }
|
||||
# ... Demo阶段只列关键番种
|
||||
|
||||
phases:
|
||||
- name: deal
|
||||
type: auto
|
||||
action: deal_cards_with_flowers
|
||||
next: play
|
||||
|
||||
- name: play
|
||||
type: mahjong_turn
|
||||
turn_order: counter_clockwise
|
||||
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_and_fan_ge_8 }
|
||||
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_and_fan_ge_8 }
|
||||
- { 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
|
||||
|
||||
scoring:
|
||||
mode: fan_table
|
||||
max_cap: 999
|
||||
101
dsl-examples/wuhan.yaml
Normal file
101
dsl-examples/wuhan.yaml
Normal file
@ -0,0 +1,101 @@
|
||||
game:
|
||||
name: 武汉麻将
|
||||
type: mahjong
|
||||
engine_type: mahjong
|
||||
players: { min: 4, max: 4 }
|
||||
|
||||
requires:
|
||||
- deck.generator_mahjong
|
||||
- meldsolver.standard_win
|
||||
- meldsolver.seven_pairs
|
||||
- meldsolver.wildcard
|
||||
- phase.mahjong_turn
|
||||
- phase.priority_arbitration
|
||||
- scoring.fan_exclusion
|
||||
|
||||
deck:
|
||||
generator: mahjong
|
||||
includeHonors: true
|
||||
includeFlowers: false
|
||||
wildcardTile: 红中
|
||||
wildcardCount: 4
|
||||
total: 136
|
||||
|
||||
deal:
|
||||
cards_per_player: 13
|
||||
dealer_extra: 1
|
||||
|
||||
wildcard_rules:
|
||||
type: fixed
|
||||
tiles: [红中]
|
||||
wildcard_encoding: 50
|
||||
behavior: substitute
|
||||
fan_calculation_policy: optimal
|
||||
scoring:
|
||||
per_wildcard_in_win: 1
|
||||
|
||||
win_condition:
|
||||
pair_must_be_258: true
|
||||
|
||||
fan_types:
|
||||
- { name: 碰碰胡, base_fan: 2 }
|
||||
- { name: 清一色, base_fan: 8, excludes: [缺一门, 无字] }
|
||||
- { name: 七对, base_fan: 8, excludes: [门前清, 单钓将] }
|
||||
- { name: 将一色, base_fan: 16, excludes: [碰碰胡, 缺一门] }
|
||||
- { name: 全求人, base_fan: 4 }
|
||||
- { name: 杠上开花, base_fan: 1, excludes: [海底捞月] }
|
||||
- { name: 海底捞月, base_fan: 1 }
|
||||
- { name: 抢杠胡, base_fan: 1 }
|
||||
- { name: 天胡, base_fan: 32 }
|
||||
- { name: 地胡, base_fan: 16 }
|
||||
- { name: 癞子胡, base_fan: 1, condition: hand_contains_wildcard }
|
||||
- { name: 将, base_fan: 0 }
|
||||
|
||||
fan_stacking: add
|
||||
max_fan: 100
|
||||
|
||||
phases:
|
||||
- name: deal
|
||||
type: auto
|
||||
action: deal_cards
|
||||
next: play
|
||||
|
||||
- name: play
|
||||
type: mahjong_turn
|
||||
turn_order: counter_clockwise
|
||||
sub_phases:
|
||||
draw:
|
||||
type: auto
|
||||
action: draw_card
|
||||
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
|
||||
|
||||
scoring:
|
||||
mode: fan_table
|
||||
max_cap: 100
|
||||
|
||||
pre_hooks:
|
||||
- name: wildcard_count
|
||||
description: 统计胡牌手牌中癞子数量,每张癞子+1番
|
||||
92
dsl-examples/xuezhandaodi.yaml
Normal file
92
dsl-examples/xuezhandaodi.yaml
Normal file
@ -0,0 +1,92 @@
|
||||
game:
|
||||
name: 四川麻将血战到底
|
||||
type: mahjong
|
||||
engine_type: mahjong
|
||||
players: { min: 4, max: 4 }
|
||||
|
||||
requires:
|
||||
- meldsolver.standard_win
|
||||
- meldsolver.seven_pairs
|
||||
- phase.mahjong_turn
|
||||
- phase.parallel_elimination
|
||||
- phase.priority_arbitration
|
||||
- scoring.fan_exclusion
|
||||
- scoring.pre_hooks
|
||||
|
||||
deck:
|
||||
generator: mahjong
|
||||
includeHonors: false
|
||||
includeFlowers: false
|
||||
total: 108
|
||||
|
||||
deal:
|
||||
cards_per_player: 13
|
||||
dealer_extra: 1
|
||||
|
||||
# 番型
|
||||
fan_types:
|
||||
- { name: 清一色, base_fan: 4, excludes: [缺一门, 无字] }
|
||||
- { name: 对对胡, base_fan: 2, conflicts: [暗七对] }
|
||||
- { name: 暗七对, base_fan: 4, conflicts: [对对胡] }
|
||||
- { name: 带幺九, base_fan: 2, excludes: [缺一门] }
|
||||
- { name: 杠上开花, base_fan: 1, excludes: [海底捞月] }
|
||||
- { name: 海底捞月, base_fan: 1 }
|
||||
- { name: 抢杠胡, base_fan: 1 }
|
||||
- { name: 缺一门, base_fan: 0 }
|
||||
|
||||
fan_stacking: add
|
||||
max_fan: 999
|
||||
|
||||
phases:
|
||||
- name: deal
|
||||
type: auto
|
||||
action: deal_cards
|
||||
next: play
|
||||
|
||||
- name: play
|
||||
type: mahjong_turn
|
||||
turn_order: counter_clockwise
|
||||
sub_phases:
|
||||
draw:
|
||||
type: auto
|
||||
action: draw_card
|
||||
on_empty_deck: check_ting
|
||||
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: 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
|
||||
parallel_elimination: true
|
||||
on_eliminate: hu_paid
|
||||
end_conditions:
|
||||
- { type: last_one_standing, action: settle }
|
||||
- { type: deck_exhausted, action: check_hua_zhu }
|
||||
# 过水
|
||||
fu_flag:
|
||||
type: dirty_flag
|
||||
set_on: can_win_but_pass
|
||||
clear_on: next_discard_self
|
||||
effect: block_win_on_current_tile
|
||||
|
||||
- name: settle
|
||||
type: auto
|
||||
action: calculate_scores
|
||||
next: null
|
||||
|
||||
scoring:
|
||||
mode: fan_table
|
||||
pre_hooks:
|
||||
- name: check_hua_zhu
|
||||
condition: deck_exhausted
|
||||
description: 检查未胡玩家是否三种花色都有
|
||||
- name: check_ting
|
||||
condition: deck_exhausted AND not hua_zhu
|
||||
description: 检查是否听牌,不听牌赔听牌的人
|
||||
Reference in New Issue
Block a user