fix: CheckWin守卫!=14拒绝副露后手牌 + FullHand合并副露

根因: CheckWin要求tiles.Count==14,碰/杠后手牌只有11/8/5/2张
修复:
- CheckWin守卫: !=14 → total%3!=2 (接受2,5,8,11,14)
- FullHand(): 合并手牌+Exposed副露→虚拟完整手牌
- 所有CheckWin调用点改用FullHand (MahjongRoom+PhaseMachine+HumanMahjongPlayer)
- 新增集成测试: 副露后11张手牌自摸
This commit is contained in:
xiaoou
2026-07-04 17:31:31 +08:00
parent d416450e56
commit bf041b7e50
5 changed files with 54 additions and 18 deletions

View File

@ -69,10 +69,9 @@ public class MahjongPhaseMachine
if (CanMingKong(state, playerId, state.LastDiscard.Value))
actions.Add(new ActionOption { Action = "ming_kong", Priority = 3 });
var handWithTile = new List<int>(state.Hands[playerId]) { state.LastDiscard.Value };
int wildInHand = state.Hands[playerId].Count(Core.MahjongTile.IsWildcard)
+ (Core.MahjongTile.IsWildcard(state.LastDiscard.Value) ? 1 : 0);
var result = _solver.CheckWin(handWithTile,
var fullHand = FullHand(state, playerId, extraTile: state.LastDiscard.Value);
int wildInHand = fullHand.Count(Core.MahjongTile.IsWildcard);
var result = _solver.CheckWin(fullHand,
wildcardCount: wildInHand, require258Pair: _require258);
if (result != null && result.IsWin)
{
@ -87,8 +86,9 @@ public class MahjongPhaseMachine
if (CanBuKong(state, playerId))
actions.Add(new ActionOption { Action = "bu_kong", Priority = 1 });
int wildInSelf = state.Hands[playerId].Count(Core.MahjongTile.IsWildcard);
var tumoResult = _solver.CheckWin(state.Hands[playerId],
var fullSelfHand = FullHand(state, playerId);
int wildInSelf = fullSelfHand.Count(Core.MahjongTile.IsWildcard);
var tumoResult = _solver.CheckWin(fullSelfHand,
wildcardCount: wildInSelf, require258Pair: _require258);
if (tumoResult != null && tumoResult.IsWin)
{
@ -176,4 +176,15 @@ public class MahjongPhaseMachine
int next = (idx + 1) % alive.Count;
return alive[next];
}
/// <summary>Combine hand + exposed melds into full virtual hand for CheckWin.</summary>
private static List<int> FullHand(MahjongGameState state, string player, int? extraTile = null)
{
var tiles = new List<int>(state.Hands[player]);
if (extraTile.HasValue) tiles.Add(extraTile.Value);
if (state.Exposed.TryGetValue(player, out var melds))
foreach (var m in melds)
tiles.AddRange(m.Tiles.Where(t => t != -1));
return tiles;
}
}