fix: 交互模式逐回合展示 + Deal()公开 + 发牌输出修正

问题:
1. 交互模式 Run() 直接跑完整局,只渲染最终状态
2. RenderGame 手牌.OrderBy()排序,洗牌后仍显顺序
3. 牌墙剩余显示0因为游戏已跑完

修复:
- 交互模式: room.Deal()→RenderDeal→逐回合StepTurn+ReadLine
- 发牌输出: 显示每人张数 + 牌墙剩余,不排序手牌
- 每回合: 显示事件(摸/打/碰/杠/胡) + 当前手牌
- Run()保留用于自动模式
This commit is contained in:
xiaoou
2026-07-04 11:22:46 +08:00
parent 504189a918
commit b871395884
2 changed files with 112 additions and 37 deletions

View File

@ -43,12 +43,10 @@ catch (Exception ex)
return 1;
}
Console.WriteLine($"=== 麻将规则引擎 Demo — {rules.Game.Name} === ({(autoMode ? "" : "")})");
Console.WriteLine();
if (autoMode)
{
// Batch mode: run N rounds
// Batch mode: run N rounds silently
Console.WriteLine($"=== 麻将规则引擎 Demo — {rules.Game.Name} === (自动模式 {count}局)");
int wins = 0, errors = 0;
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
@ -78,53 +76,130 @@ if (autoMode)
}
else
{
// Interactive mode: single game with full output
var room = new MahjongRoom(rules, new[] { "AI-东", "AI-南", "AI-西", "AI-北" }, autoMode: false);
room.Run();
// Interactive mode: step-by-step with key-press advancement
Console.WriteLine($"=== 麻将规则引擎 Demo — {rules.Game.Name} === (交互模式,按 Enter 继续下一步)");
Console.WriteLine();
RenderGame(room);
var room = new MahjongRoom(rules, new[] { "AI-东", "AI-南", "AI-西", "AI-北" }, autoMode: false);
room.Deal();
// Show dealt hands
RenderDeal(room);
Console.Write("\n[按 Enter 开始对局]");
Console.ReadLine();
// Step through each turn
int turnNumber = 0;
while (!room.IsFinished)
{
turnNumber++;
var events = room.StepTurn();
// Skip deal phase transition
if (room.State.Phase == "deal") continue;
Console.WriteLine($"\n--- 第 {turnNumber} 回合 | 牌墙剩余 {room.State.Deck.Count} 张 ---");
// Show events for this turn
foreach (var evt in events)
{
switch (evt.Type)
{
case "draw":
Console.WriteLine($" [{evt.Player}] 摸牌: {MahjongTile.ToString(evt.Tile ?? 0)}");
break;
case "discard":
Console.WriteLine($" [{evt.Player}] 出牌: {MahjongTile.ToString(evt.Tile ?? 0)}");
break;
case "pung":
Console.WriteLine($" → {evt.Player}: 碰!{MahjongTile.ToString(evt.Tile ?? 0)}");
break;
case "ming_kong":
Console.WriteLine($" → {evt.Player}: 明杠!{MahjongTile.ToString(evt.Tile ?? 0)}");
break;
case "win":
Console.WriteLine($" ✅ {evt.Player}: 胡!{evt.Description}");
break;
case "deck_exhausted":
Console.WriteLine($" ⚠ 牌墙耗尽!");
break;
case "hua_zhu":
Console.WriteLine($" ⚠ {evt.Player}: 花猪(三色齐全)");
break;
case "hua_zhu_ok":
break;
case "ting_checked":
Console.WriteLine($" ✓ {evt.Player}: 听牌");
break;
case "ting_failed":
Console.WriteLine($" ✗ {evt.Player}: 未听牌");
break;
case "settle":
Console.WriteLine($" 💰 结算");
break;
}
}
// Show current hands
if (!room.IsFinished)
{
Console.WriteLine();
Console.WriteLine(" 当前手牌:");
foreach (var p in room.State.PlayerOrder)
{
if (!room.State.AlivePlayers.Contains(p)) continue;
var hand = room.State.Hands[p];
Console.WriteLine($" {p}: {string.Join(" ", hand.OrderBy(t => t).Select(MahjongTile.ToString))} ({hand.Count}张)");
}
Console.Write("\n[按 Enter 继续]");
Console.ReadLine();
}
}
// Final render
Console.WriteLine("\n══════════════════════════════════════");
RenderFinal(room);
}
return 0;
void RenderGame(MahjongRoom room)
void RenderDeal(MahjongRoom room)
{
var state = room.State;
Console.WriteLine("══════════════════════════════════════");
Console.WriteLine("[发牌]");
Console.WriteLine("═══ 发牌 ═══");
foreach (var p in state.PlayerOrder)
Console.WriteLine($" {p}{(p == state.Dealer ? "()" : "")}: {string.Join(", ", state.Hands[p].OrderBy(t => t).Select(MahjongTile.ToString))} ({state.Hands[p].Count}张)");
Console.WriteLine($" 牌墙剩余: {state.Deck.Count} 张");
Console.WriteLine($" {p}{(p == state.Dealer ? " ()" : "")}: {state.Hands[p].Count}张");
Console.WriteLine($" 牌墙: {state.Deck.Count} 张");
Console.WriteLine($" (手牌已洗牌,按 Enter 查看牌局)");
}
// Replay events
foreach (var evt in state.RecentEvents)
void RenderFinal(MahjongRoom room)
{
var state = room.State;
Console.WriteLine("[最终状态]");
Console.WriteLine($" 游戏结束,牌墙剩余 {state.Deck.Count} 张");
Console.WriteLine();
Console.WriteLine(" 最终手牌:");
foreach (var p in state.PlayerOrder)
{
if (evt.Type == "win")
Console.WriteLine($" ✅ {evt.Player}: 自摸!番型: {evt.Description}");
else if (evt.Type == "discard")
Console.WriteLine($" [{evt.Player}] 出牌: {MahjongTile.ToString(evt.Tile ?? 0)}");
else if (evt.Type == "pung")
Console.WriteLine($" → {evt.Player}: 碰!");
else if (evt.Type == "draw")
Console.WriteLine($" [{evt.Player}] 摸牌: {MahjongTile.ToString(evt.Tile ?? 0)}");
else if (evt.Type == "deck_exhausted")
Console.WriteLine($" 牌墙耗尽!");
else if (evt.Type == "hua_zhu")
Console.WriteLine($" ⚠️ {evt.Player}: 花猪");
else if (evt.Type == "ting_checked")
Console.WriteLine($" ✓ {evt.Player}: 听牌");
else if (evt.Type == "ting_failed")
Console.WriteLine($" ❌ {evt.Player}: 未听牌");
else if (evt.Type == "settle")
Console.WriteLine($" 💰 结算");
var hand = state.Hands[p];
var sorted = string.Join(", ", hand.OrderBy(t => t).Select(MahjongTile.ToString));
Console.WriteLine($" {p}{(p == state.Dealer ? "()" : "")}: {sorted} ({hand.Count}张)");
}
if (state.HuPlayers.Count > 0)
{
Console.WriteLine();
Console.WriteLine($" 胡牌: {string.Join(", ", state.HuPlayers)}");
}
Console.WriteLine();
Console.WriteLine("[最终结算]");
foreach (var (p, score) in state.Scores)
Console.WriteLine($" {p}: {(score >= 0 ? "+" : "")}{score}分");
Console.WriteLine($" {p}: {(score >= 0 ? "+" : "")}{score}分");
int total = state.Scores.Values.Sum();
Console.WriteLine($" 总分: {(total >= 0 ? "+" : "")}{total} {(total == 0 ? "" : " ")}");
Console.WriteLine($" 总分: {(total >= 0 ? "+" : "")}{total} {(total == 0 ? "" : " ")}");
Console.WriteLine("══════════════════════════════════════");
}