fix: 4个HIGH severity DSL规则bug(子代理审查发现)

子代理审查(对照4个DSL)发现27个bug,本轮修复HIGH级别4个:

1. PhaseMachine wildcard计数用Core.MahjongTile.IsWildcard(静态)→
   不检查配置的红中(35)→_wildcards.CountWildcards(实例)

2. 番型名不匹配:引擎生成 对对胡/暗七对,但DSL定义
   碰碰胡/碰碰和/七对→FanName()动态匹配_fanConfig

3. 十三幺/七对/全不靠/一色双龙会不识别自身番型→
   CheckWin各路径返回前注入番型名+IdentifyFans追加

4. 258将: 七对路径缺检查+IsValid258Pair OR→AND逻辑
   (一张癞子+非258牌应拒绝,不是通过)

此前已修:chi越权(d95eab8)+花牌计分+BEFORE_GAME_START(9b9e4c3)
剩余17个MED/LOW→下一轮评估
This commit is contained in:
xiaoou
2026-07-04 20:32:39 +08:00
parent 9b9e4c310b
commit d5973fd2bc
2 changed files with 51 additions and 19 deletions

View File

@ -45,7 +45,23 @@ public class MeldsSolver
var sevenPairs = TrySevenPairs(tiles, wildcardCount);
if (sevenPairs != null)
{
sevenPairs.Fans = ApplyFanExclusions(IdentifyFans(sevenPairs));
// 258将检查: 武汉七对每对都需258
if (require258Pair)
{
foreach (var m in sevenPairs.Melds)
{
if (m.Type == "pair" && m.Tiles.Count >= 2)
{
int a = m.Tiles[0], b = m.Tiles[1];
// Both tiles must be 258 or wildcard
if (!Is258OrWildcard(a) && !Is258OrWildcard(b))
return new MeldsResult { IsWin = false };
}
}
}
sevenPairs.Fans = new List<string> { FanName("暗七对", "七对") };
sevenPairs.Fans = ApplyFanExclusions(
sevenPairs.Fans.Concat(IdentifyFans(sevenPairs)).Distinct().ToList());
return sevenPairs;
}
@ -53,7 +69,9 @@ public class MeldsSolver
var thirteen = TryThirteenOrphans(tiles, wildcardCount);
if (thirteen != null)
{
thirteen.Fans = ApplyFanExclusions(IdentifyFans(thirteen));
thirteen.Fans = new List<string> { "十三幺" };
thirteen.Fans = ApplyFanExclusions(
thirteen.Fans.Concat(IdentifyFans(thirteen)).Distinct().ToList());
return thirteen;
}
@ -61,7 +79,9 @@ public class MeldsSolver
var allOrphans = TryAllOrphans(tiles, wildcardCount);
if (allOrphans != null)
{
allOrphans.Fans = ApplyFanExclusions(IdentifyFans(allOrphans));
allOrphans.Fans = new List<string> { "全不靠" };
allOrphans.Fans = ApplyFanExclusions(
allOrphans.Fans.Concat(IdentifyFans(allOrphans)).Distinct().ToList());
return allOrphans;
}
@ -69,7 +89,9 @@ public class MeldsSolver
var doubleDragon = TryDoubleDragon(tiles, wildcardCount);
if (doubleDragon != null)
{
doubleDragon.Fans = ApplyFanExclusions(IdentifyFans(doubleDragon));
doubleDragon.Fans = new List<string> { "一色双龙会" };
doubleDragon.Fans = ApplyFanExclusions(
doubleDragon.Fans.Concat(IdentifyFans(doubleDragon)).Distinct().ToList());
return doubleDragon;
}
@ -538,13 +560,27 @@ public class MeldsSolver
// === 258将检查 ===
private bool IsValid258Pair(int tileA, int tileB)
{
bool Check(int t)
{
if (t == -1) return true; // wildcard
int r = MahjongTile.Rank(t);
return r == 2 || r == 5 || r == 8;
}
return Check(tileA) || Check(tileB);
// Both wildcards → OK. One wildcard → check the other. Both real → both must be 258.
if (tileA == -1 && tileB == -1) return true;
if (tileA == -1) return Is258Tile(tileB);
if (tileB == -1) return Is258Tile(tileA);
return Is258Tile(tileA) && Is258Tile(tileB);
}
private bool Is258OrWildcard(int tile) => tile == -1 || Is258Tile(tile);
private bool Is258Tile(int tile)
{
int r = MahjongTile.Rank(tile);
return r == 2 || r == 5 || r == 8;
}
/// <summary>Resolve canonical fan name to DSL fan name (e.g. 对对胡→碰碰胡/碰碰和).</summary>
private string FanName(params string[] candidates)
{
foreach (var c in candidates)
if (_fanConfig.ContainsKey(c)) return c;
return candidates[0];
}
// === 番型识别 (DSL-driven: structural patterns auto-detected) ===
@ -581,11 +617,11 @@ public class MeldsSolver
// 对对胡: all melds are kezi
if (melds.Count > 0 && melds.All(m => m.Type == "kezi"))
fans.Add("对对胡");
fans.Add(FanName("对对胡", "碰碰胡", "碰碰和"));
// 暗七对 (all melds are pairs — from seven-pairs path)
if (melds.Count > 0 && melds.All(m => m.Type == "pair"))
fans.Add("暗七对");
fans.Add(FanName("暗七对", "七对"));
// 带幺九: all melds contain 1/9/honor
if (melds.Count > 0 && pair.Count >= 1)
@ -594,7 +630,7 @@ public class MeldsSolver
m.Tiles.Where(t => t != -1).All(MahjongTile.IsTerminal));
bool pairTerminal = pair.All(t => t == -1 || MahjongTile.IsTerminal(t));
if (allMeldsTerminal && pairTerminal)
fans.Add("带幺九");
fans.Add(FanName("带幺九", "全带幺"));
}
// 混幺九: all tiles are terminals or honors, includes at least one honor and one numbered
@ -660,10 +696,6 @@ public class MeldsSolver
if (allEven && allTiles.All(t => MahjongTile.IsNumbered(t) || MahjongTile.IsHonor(t)))
fans.Add("全双");
// 碰碰和 (same as 对对胡, guobiao name)
if (melds.Count > 0 && melds.All(m => m.Type == "kezi") && !fans.Contains("对对胡"))
fans.Add("碰碰和");
// Filter: only keep fans that exist in DSL config or are well-known
return fans.Where(f => _fanConfig.ContainsKey(f) ||
f is "清一色" or "混一色" or "对对胡" or "碰碰和" or "暗七对" or "七对"