fix: 国标花牌初始不补牌 + 花牌补牌不递归

2处修复:
1. Deal(): 初始花牌替换条件扩展
   - 原来仅 replace_tiles:BEFORE_GAME_START(广东)触发
   - 现在 deal_cards_with_flowers action(国标)也触发
2. Deal() + StepTurn: 补花牌 → while循环替代单次if
   - 补来的牌也可能是花牌,需持续补直到非花牌或牌墙空

验证: 国标花牌计入得分(AI-南 +15, 花牌2张=底分3+花2×discard3)
58 tests pass, 5 variants 0 errors
This commit is contained in:
xiaoou
2026-07-04 21:37:31 +08:00
parent a94e68fee2
commit dbfc9c8b76

View File

@ -184,7 +184,7 @@ public class MahjongRoom
State.AddEvent("draw", player, drawn,
$"摸牌: {MahjongTile.ToString(drawn)}");
// Check flower
// Check flower — keep replacing until non-flower or deck empty
if (MahjongTile.IsFlower(drawn))
{
State.FlowerReplaced++;
@ -194,13 +194,26 @@ public class MahjongRoom
State.FlowerPool[player] = new List<int>();
State.FlowerPool[player].Add(drawn);
State.Hands[player].Remove(drawn);
if (State.Deck.Count > 0)
// Keep drawing replacements until we get a non-flower
int extra = 0;
while (State.Deck.Count > 0)
{
int extra = State.Deck[^1];
extra = State.Deck[^1];
State.Deck.RemoveAt(State.Deck.Count - 1);
State.Hands[player].Add(extra);
State.AddEvent("draw", player, extra,
$"补牌: {MahjongTile.ToString(extra)}");
if (!MahjongTile.IsFlower(extra))
{
State.Hands[player].Add(extra);
State.AddEvent("draw", player, extra,
$"补牌: {MahjongTile.ToString(extra)}");
break;
}
// Replacement is also a flower → add to pool, keep going
State.FlowerReplaced++;
if (!State.FlowerPool.ContainsKey(player))
State.FlowerPool[player] = new List<int>();
State.FlowerPool[player].Add(extra);
State.AddEvent("flower_drawn", player, extra,
$"补花牌 {MahjongTile.ToString(extra)} → 再补");
}
}
}
@ -768,21 +781,26 @@ public class MahjongRoom
State.IsFirstTurn = true;
State.DealerOnFirstTurn = State.Dealer;
// Replace flowers in initial hands BEFORE game starts (e.g. Guangdong 鸡平胡)
if (_rules.FlowerRules != null && includeFlowers
&& string.Equals(_rules.FlowerRules.ReplaceTiles, "BEFORE_GAME_START", StringComparison.OrdinalIgnoreCase))
// Replace flowers in initial hands BEFORE game starts.
// Triggers for: Guangdong (replace_tiles: BEFORE_GAME_START),
// Guobiao (deal action: deal_cards_with_flowers), or any variant with flowers.
bool replaceInitialFlowers = _rules.FlowerRules != null && includeFlowers
&& (string.Equals(_rules.FlowerRules.ReplaceTiles, "BEFORE_GAME_START", StringComparison.OrdinalIgnoreCase)
|| string.Equals(_playPhase.Action, "deal_cards_with_flowers", StringComparison.OrdinalIgnoreCase));
if (replaceInitialFlowers)
{
int replaced = 0;
foreach (var p in State.PlayerOrder)
{
var hand = State.Hands[p];
var flowers = hand.Where(MahjongTile.IsFlower).ToList();
foreach (var f in flowers)
while (true)
{
hand.Remove(f);
var flower = hand.FirstOrDefault(MahjongTile.IsFlower);
if (flower == 0) break; // no more flowers
hand.Remove(flower);
if (!State.FlowerPool.ContainsKey(p))
State.FlowerPool[p] = new List<int>();
State.FlowerPool[p].Add(f);
State.FlowerPool[p].Add(flower);
if (State.Deck.Count > 0)
{
int extra = State.Deck[^1];
@ -790,6 +808,7 @@ public class MahjongRoom
hand.Add(extra);
replaced++;
}
else break; // deck empty, stop
}
}
if (replaced > 0)