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将事件(非武汉也输出)
This commit is contained in:
@ -35,7 +35,7 @@ public class MahjongRoom
|
||||
{
|
||||
Mode = _rules.Scoring.Mode,
|
||||
MaxCap = _rules.Scoring.MaxCap
|
||||
}, _solver);
|
||||
}, _solver, fanConfig);
|
||||
|
||||
// Init state
|
||||
State.PlayerOrder = playerNames.ToList();
|
||||
@ -233,23 +233,33 @@ public class MahjongRoom
|
||||
if (!State.AlivePlayers.Contains(p)) continue;
|
||||
if (p == discarder) continue;
|
||||
|
||||
int sameCount = State.Hands[p].Count(t => t == discardTile);
|
||||
string? claim = null;
|
||||
int priority = 0;
|
||||
|
||||
// Check win first (highest priority)
|
||||
var handWithTile = new List<int>(State.Hands[p]) { discardTile };
|
||||
var winResult = _solver.CheckWin(handWithTile);
|
||||
|
||||
if (winResult != null && winResult.IsWin)
|
||||
claims.Add((p, 4, "win"));
|
||||
|
||||
int sameCount = State.Hands[p].Count(t => t == discardTile);
|
||||
if (sameCount >= 3)
|
||||
claims.Add((p, 3, "ming_kong"));
|
||||
claim = "win";
|
||||
else if (sameCount >= 3)
|
||||
claim = "ming_kong";
|
||||
else if (sameCount >= 2)
|
||||
claims.Add((p, 2, "pung"));
|
||||
claim = "pung";
|
||||
|
||||
if (claim == null) continue;
|
||||
priority = claim switch { "win" => 4, "ming_kong" => 3, "pung" => 2, _ => 0 };
|
||||
|
||||
// For same priority, closer seat wins (first claim in order)
|
||||
// For higher priority, it overrides all previous claims
|
||||
if (claims.Count == 0 || priority > claims[0].priority)
|
||||
claims.Add((p, priority, claim));
|
||||
// Same priority: first claimant wins (closer to discarder in seat order)
|
||||
// Different (lower) priority: ignored — higher priority already claimed
|
||||
}
|
||||
|
||||
if (claims.Count == 0) return false;
|
||||
|
||||
// Highest-priority claim wins (for simplicity: first highest wins)
|
||||
var best = claims.OrderByDescending(c => c.priority).First();
|
||||
var best = claims[0];
|
||||
string bp = best.player;
|
||||
|
||||
if (best.action == "win")
|
||||
@ -376,25 +386,33 @@ public class MahjongRoom
|
||||
bool hasWildcard = _rules.WildcardRules != null;
|
||||
int wildcardCount = hasWildcard ? _rules.Deck.WildcardCount : 0;
|
||||
bool require258 = _rules.WinCondition?.PairMustBe258 ?? false;
|
||||
|
||||
|
||||
// All possible tiles that could be drawn (numbered 1-9 across 3 suits + 7 honors)
|
||||
var allPossibleTiles = MahjongTile.AllTiles(
|
||||
includeHonors: _rules.Deck.IncludeHonors,
|
||||
includeFlowers: false);
|
||||
|
||||
foreach (var p in State.AlivePlayers)
|
||||
{
|
||||
bool ting = false;
|
||||
var hand = State.Hands[p];
|
||||
for (int i = 0; i < hand.Count; i++)
|
||||
|
||||
// For each possible tile we could draw, check if hand + tile is a win
|
||||
foreach (var tile in allPossibleTiles)
|
||||
{
|
||||
var testHand = new List<int>(hand);
|
||||
testHand.RemoveAt(i);
|
||||
var r = _solver.CheckWin(testHand, wildcardCount: wildcardCount, require258Pair: require258);
|
||||
// Count how many of this tile exist (4 max per tile type)
|
||||
int alreadyHave = hand.Count(t => t == tile);
|
||||
if (alreadyHave >= 4) continue; // can't draw more of this tile
|
||||
|
||||
var r = _solver.CheckWin(hand, newTile: tile,
|
||||
wildcardCount: wildcardCount, require258Pair: require258);
|
||||
if (r != null && r.IsWin) { ting = true; break; }
|
||||
}
|
||||
|
||||
if (ting)
|
||||
State.AddEvent("ting_checked", p, null, "✓ 听牌");
|
||||
else
|
||||
{
|
||||
State.AddEvent("ting_failed", p, null, "❌ 未听牌");
|
||||
State.AddEvent("pair_validated_258", p, null, "258将检查");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user