feat: 番型识别DSL驱动化 + 全不靠/一色双龙会测试完善
IdentifyFans: 从4种扩展到17种结构性番型自动识别 - 清一色/混一色/字一色/对对胡/暗七对/带幺九/混幺九 - 缺一门/平胡/断幺九/全大/全中/全小/大于五/小于五/全双/碰碰和 - FanConfig 新增 Condition 字段预留game-event条件 TryAllOrphans: 修正容差 +2(16选14),允许非幺九数牌 TryDoubleDragon: 增加honor/越界保护 测试: 33→45,新增12个(全不靠×3/一色双龙会×2/番型×7) 100局压测: 0出错
This commit is contained in:
@ -213,16 +213,140 @@ public class MeldsSolverTests
|
||||
}
|
||||
|
||||
// === 全不靠 ===
|
||||
// 全不靠需要从16张模板(147×3=9 + 7字)中选14张
|
||||
// 当前算法要求9数位全占,需搭配wildcard。纯手牌场景留待后续完善
|
||||
// [Fact(Skip = "全不靠需wildcard补位,纯14牌无wildcard场景待完善")]
|
||||
// 全不靠: 14 tiles chosen from 16-position template (9 numbered gaps + 7 honors).
|
||||
// Two positions may be empty per rule — no wildcards needed.
|
||||
[Fact]
|
||||
public void 全不靠_7字牌_7数牌_ShouldWin()
|
||||
public void 全不靠_14牌0wildcard_ShouldWin()
|
||||
{
|
||||
// 仅验证算法不崩溃
|
||||
// 7 honors + 万147 + 条258 + 筒3 = 14 tiles from 16-position pool
|
||||
var hand = new List<int> { 1, 4, 7, 12, 15, 18, 23, 31, 32, 33, 34, 35, 36, 37 };
|
||||
var result = _solver.CheckWin(hand, wildcardCount: 0);
|
||||
Assert.True(result.IsWin, "全不靠(14牌从16位置模板选)应判定为胡牌");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void 全不靠_3wildcard补位_ShouldWin()
|
||||
{
|
||||
// 5 honors + 万147 + 条258 = 11 tiles + 3 wildcards → fill 5 missing → 14 total
|
||||
var hand = new List<int> { 1, 4, 7, 12, 15, 18, 31, 32, 33, 34, 35 };
|
||||
var result = _solver.CheckWin(hand, wildcardCount: 3);
|
||||
Assert.True(result.IsWin, "全不靠(11牌+3wildcard补位)应判定为胡牌");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void 全不靠_0wildcard不完整_ShouldNotWin()
|
||||
{
|
||||
// Only 6 honors + 万147 = 9 tiles, 5 positions missing, 0 wildcard → fail
|
||||
var hand = new List<int> { 1, 4, 7, 31, 32, 33, 34, 35, 36 };
|
||||
var result = _solver.CheckWin(hand, wildcardCount: 0);
|
||||
Assert.False(result.IsWin, "全不靠(9牌+0wildcard不完整)不应判定为胡牌");
|
||||
}
|
||||
|
||||
// === 一色双龙会 (需 wildcard: 9 ranks × 2 = 18 positions, 14 tiles + wildcards) ===
|
||||
[Fact]
|
||||
public void 一色双龙会_4wildcard_ShouldWin()
|
||||
{
|
||||
// 5 ranks × 2 = 10 tiles + 4 wildcards for remaining 4 ranks = 14 total
|
||||
var hand = new List<int> { 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 };
|
||||
var result = _solver.CheckWin(hand, wildcardCount: 4);
|
||||
Assert.True(result.IsWin, "一色双龙会(10牌+4wildcard)应判定为胡牌");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void 一色双龙会_不足wildcard_ShouldNotWin()
|
||||
{
|
||||
// Same hand, only 2 wildcards → ranks 6-9 need 2 each → 6 missing, only 2 available
|
||||
var hand = new List<int> { 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 };
|
||||
var result = _solver.CheckWin(hand, wildcardCount: 2);
|
||||
Assert.False(result.IsWin, "一色双龙会不足wildcard不应判定为胡牌");
|
||||
}
|
||||
|
||||
// === 新番型识别测试 ===
|
||||
[Fact]
|
||||
public void 混一色_ShouldIdentify()
|
||||
{
|
||||
// 万牌 + 字牌对将: 111万 234万 567万 东东东 发发
|
||||
var hand = new List<int> { 1, 1, 1, 2, 3, 4, 5, 6, 7, 31, 31, 31, 36, 36 };
|
||||
var result = _solver.CheckWin(hand);
|
||||
// 标准回溯和七对都失败后全不靠也因缺位而null,预期不崩
|
||||
Assert.True(result == null || !result.IsWin);
|
||||
Assert.True(result.IsWin);
|
||||
var fans = _solver.IdentifyFans(result!);
|
||||
Assert.Contains("混一色", fans);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void 字一色_ShouldIdentify()
|
||||
{
|
||||
// All honors: 111 333 555 777 99 in encoding
|
||||
var hand = new List<int> { 31, 31, 31, 33, 33, 33, 35, 35, 35, 37, 37, 37, 32, 32 };
|
||||
var result = _solver.CheckWin(hand);
|
||||
Assert.True(result.IsWin);
|
||||
var fans = _solver.IdentifyFans(result!);
|
||||
Assert.Contains("字一色", fans);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void 缺一门_ShouldIdentify()
|
||||
{
|
||||
// Only 万 and 条, no 筒: 123万 456万 789万 111条 99条
|
||||
var hand = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 11, 11, 19, 19 };
|
||||
var result = _solver.CheckWin(hand);
|
||||
Assert.True(result.IsWin);
|
||||
var fans = _solver.IdentifyFans(result!);
|
||||
Assert.Contains("缺一门", fans);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void 平胡_全顺子_ShouldIdentify()
|
||||
{
|
||||
// All shunzi: 123万 456万 789万 234条 55条
|
||||
var hand = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 15 };
|
||||
var result = _solver.CheckWin(hand);
|
||||
Assert.True(result.IsWin);
|
||||
var fans = _solver.IdentifyFans(result!);
|
||||
Assert.Contains("平胡", fans);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void 全大_7到9_ShouldIdentify()
|
||||
{
|
||||
// All ranks 7-9: 777万 888万 999万 777条 99条
|
||||
var hand = new List<int> { 7, 7, 7, 8, 8, 8, 9, 9, 9, 17, 17, 17, 19, 19 };
|
||||
var result = _solver.CheckWin(hand);
|
||||
Assert.True(result.IsWin);
|
||||
var fans = _solver.IdentifyFans(result!);
|
||||
Assert.Contains("全大", fans);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void 全小_1到3_ShouldIdentify()
|
||||
{
|
||||
// All ranks 1-3: 111万 222万 333万 111条 33条
|
||||
var hand = new List<int> { 1, 1, 1, 2, 2, 2, 3, 3, 3, 11, 11, 11, 13, 13 };
|
||||
var result = _solver.CheckWin(hand);
|
||||
Assert.True(result.IsWin);
|
||||
var fans = _solver.IdentifyFans(result!);
|
||||
Assert.Contains("全小", fans);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void 全双_偶数_ShouldIdentify()
|
||||
{
|
||||
// All even ranks: 222万 444万 666万 222条 88条
|
||||
var hand = new List<int> { 2, 2, 2, 4, 4, 4, 6, 6, 6, 12, 12, 12, 18, 18 };
|
||||
var result = _solver.CheckWin(hand);
|
||||
Assert.True(result.IsWin);
|
||||
var fans = _solver.IdentifyFans(result!);
|
||||
Assert.Contains("全双", fans);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void 断幺九_ShouldIdentify()
|
||||
{
|
||||
// No terminals: 234万 456万 678万 234条 55条 (ranks 2-8 only)
|
||||
var hand = new List<int> { 2, 3, 4, 4, 5, 6, 6, 7, 8, 12, 13, 14, 15, 15 };
|
||||
var result = _solver.CheckWin(hand);
|
||||
Assert.True(result.IsWin);
|
||||
var fans = _solver.IdentifyFans(result!);
|
||||
Assert.Contains("断幺九", fans);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user