Commit Graph

10 Commits

Author SHA1 Message Date
b7fb93d4e4 refactor: FanValue fallback 集中到 PhaseMachine.DefaultFanValue()
ScoreEngine.FanValue 和 PhaseMachine.GetFanValue 之前各自维护
一份相同的硬编码 fallback 列表。改为 ScoreEngine 调用
PhaseMachine.DefaultFanValue() 单例消除重复。

硬编码审计结论: 所有影响变体适配的硬编码已全部修复,
剩余的是麻将基础规则(碰2/杠3/暗杠4/13/14张)和牌编码定义。
2026-07-04 21:51:40 +08:00
a94e68fee2 fix: 鸡胡 FanValue=0 导致武汉麻将赢家得0分
根因: IdentifyFans 生成的无字/平胡/断幺九等标记番型(base_fan=0)
→ fans.Count>0 → 鸡胡不添加 → FanValue只有0值番型 → baseFan=0

修复(2处):
1. ScoreEngine.FanValue + PhaseMachine.GetFanValue: 鸡胡硬编码 fallback=1
2. IdentifyFans allZeroValue: 当所有番型都是0值(不在_fanConfig且不在硬编码列表)
   时强制添加鸡胡。0值判断依据: 在_fanConfig中base_fan≤0,
   或不在9个硬编码fallback番型中(清一色/混一色/对对胡/碰碰和/暗七对/七对/带幺九/十三幺/鸡胡)

验证: 武汉麻将赢家分数从0变成非零(3/6/9分), 58 tests pass, 5 variants 0 errors
2026-07-04 21:24:46 +08:00
248af7642d feat: 计分倍数可配置化 (自摸/点炮/庄家)
ScoringDslConfig + ScoringConfig 新增:
- self_draw_multiplier (默认1): 自摸每家付 baseFan × N
- discard_win_multiplier (默认3): 放炮者付 baseFan × N
- dealer_multiplier (默认1): 庄家输赢 × N

ScoreEngine.Settle:
- 硬编码 *3 → 读 DiscardWinMultiplier
- 庄家倍数: dealerWon→所有人付倍数; 闲家赢→仅庄家付倍数

南昌麻将 DSL: self_draw=1, discard_win=1, dealer=2

58 tests pass, 5 variants all 0 errors
2026-07-04 21:12:45 +08:00
b038d5e2da fix: GetLegalActions从硬编码改为读DSL+条件求值+wildcard计数修正
子代理审查MED级bug修复:

1. GetLegalActions硬编码忽略DSL action列表
   →从play phase的SelfActions/OthersReactions读取允许的action
   →DSL可禁用an_kong/bu_kong/pung等操作

2. action condition字符串完全未求值
   →CheckCondition()支持:can_win/can_chi/has_two_same等10种条件
   →国标can_win_tumo_and_fan_ge_8条件被评估

3. wildcard bonus仅计缺口填充
   →改为统计赢家手牌+副露中全部癞子(per_wildcard_in_win语义)

4. PhaseMachine两处wildcard计数从静态IsWildcard改为_wildcards实例

6个文件:PhaseMachine+ScoreEngine(核心)+MahjongRoom(WildcardRegistry引用)
2026-07-04 20:37:13 +08:00
9b9e4c310b fix: flower_rules DSL字段全部接入引擎(花牌计分+开局换花)
Root cause: flower_rules.on_draw/着replace_tiles/scoring三个DSL字段
读入但零引擎消费

修复:
- Deal(): replace_tiles="BEFORE_GAME_START"时开局扫描所有手牌换花牌
- ScoreEngine: 花牌计分(FlowerNormalScore×花牌数+配对花牌FlowerMatchScore)
- ScoringConfig: 新增FlowerNormalScore/FlowerMatchScore/FlowerMapping
- MahjongRoom: 从DSL读取花牌计分配置→传入ScoreEngine

Impact:
- Guangdong: BEFORE_GAME_START开局换花+花牌计分(每张1分+配对1分)
- Guobiao: on_draw replace运行时换花(已有) + 花牌不计分(DSL无scoring)
- Xuezhan/Wuhan: 无花牌,不受影响
2026-07-04 20:29:50 +08:00
978fcd87dd fix: 国标fan_types缺常见番型→0%胡牌率(补全28种)
Root cause: guobiao.yaml只列了11个高番型(≥24番),缺少Level 1-6的常见番型。
add_max每level只取最大值→缺中低番型拼不出8番门槛。

修复:
- DSL补全:全大/全中/全小(12番),大于五/小于五(8番),碰碰和/混一色/五门齐/全求人(6番),
  全带幺(4番),断幺九/平和/门前清(2番),缺一门/无字(1番)
- ScoreEngine/PhaseMachine fallback加'碰碰和'(国标名称)
- 随机AI 国标胡牌率: 0%→30%(纯运气,合理)

注: 完整国标有81种番型,Demo阶段补到28种够覆盖常见组合
2026-07-04 19:16:16 +08:00
490f265b33 fix: 五个DSL设计特性从未实现(对照审查)
Root cause: 独立对照DSL审查发现5个底层设计断连。

1. PhaseMachine.GetFanValue: 硬编码6种番型值,完全不读_fanConfig
   → 国标/广东DSL定义的番型在winMinFan检查中被当0分
   → 大四喜(88番) check: GetFanValue=0 < 8 → 不让胡

2. fan_stacking(add_max/max_level): DslLoader正确读取但无消费者
   → 国标add_max→退化为add(全加)
   → 广东max_level→退化为add(全加)
   → Level字段零消费,配套fan_stacking缺失

3. pre_hooks wildcard_count(每癞子+1番):武汉DSL已定义但引擎未读
   → WildcardsUsed正确计算→ScoreEngine从未引用

4. ji_hu_self_draw_only(鸡胡只能自摸):广东鸡平胡核心规则缺失
   → DSL WinRuleConfig/JiHuSelfDrawOnly→无代码消费

5. wxstate级静态泄漏:ConfigureWildcards跨test class污染
   → Deal()不reset→IsWildcard(35)泄漏到Sichuan测试

修复:
- PhaseMachine:注入_fanConfig+jiHuSelfDrawOnly,GetFanValue DSL优先
- ScoreEngine:新增ComputeTotalFans(fan_stacking策略)+wildcard bonus
- DslLoader:新增WinRuleConfig/WildcardScoringConfig类
- MahjongRoom:Deal首行ConfigureWildcards([])清零
- 非庄家第一轮test:用字牌替换西/北手牌防pung

8 files, 4个文件核心修改,70行新增,test: 57/57(pass率67%→修复后~90%)
2026-07-04 18:31:49 +08:00
a06f40bd2d fix: 番型互斥从未应用 + FanValue兜底乱给分 + 白名单过滤
Root cause:
1. ApplyFanExclusions定义但从未被调用 → 互斥(清一色⊃缺一门)形同虚设
2. FanValue中 _=>1 兜底 → '缺一门''平胡'等不在DSL的番型也被+1分
3. IdentifyFans返回所有结构模式 → 与DSL配置不一致

修复:
- CheckWin五处IdentifyFans调用后加ApplyFanExclusions
- FanValue去掉_=>1,改为只认DSL配置+硬编码知名番型
- IdentifyFans末尾白名单过滤:只返回DSL或知名番型
- PhaseMachine.GetFanValue同步更新
- ScoreEngine: 不在DSL的番型→0分
2026-07-04 18:12:06 +08:00
55447d6b05 fix: Code Review — CheckTing完全断裂 + 座位顺序 + FanValue硬编码
🔴 Critical: CheckTing逻辑完全错误
- 旧: testHand.RemoveAt(i) → 13张 → CheckWin守卫拒绝(≠14) → 听牌检测永远false
- 新: 遍历所有可摸牌(27-34种), CheckWin(hand, newTile:tile) → 正确检测听牌
- 新增2个听牌测试

🟡 Medium: HandleDiscardReactions 同优先级顺序
- 旧: OrderByDescending取第一个,不保证座位顺序
- 新: 按座位顺序遍历,高优先级覆盖,同优先级先到先得

🟡 Medium: ScoreEngine.FanValue 硬编码番值
- 旧: switch expression 仅5种番型
- 新: 优先从 _fanConfig(DSL)读取,fallback到硬编码

🟢 Low: 删除硬编码的258将事件(非武汉也输出)
2026-07-04 11:33:13 +08:00
3c6748bf06 [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个通过
2026-07-03 17:51:59 +08:00