Files
card-game-engine/RuleEngine/AI/HumanMahjongPlayer.cs
xiaoou 4302ec2fd5 feat: 集成测试 + 手动出牌模式 + discart pool幽灵牌修复
集成测试 (10个):
- 发牌: 庄14闲13, 牌墙55
- 回合流转: 庄家第一轮不摸牌, 非庄家摸牌后出牌
- 数据完整性: 10回合后手牌+副露+牌墙+弃牌=108
- 对局完整性: 4种DSL全部不出错完成
- 构造听牌: 完整手牌第一轮自摸

手动出牌 (--human):
- HumanMahjongPlayer: 显示手牌/可选操作/h=胡/p=碰/k=杠/1-N=出牌
- MahjongRoom: humanPlayer参数

修复: DiscardPool碰/胡后不移除→幽灵牌→总牌数溢出
2026-07-04 11:45:54 +08:00

69 lines
2.5 KiB
C#

namespace RuleEngine.AI;
using RuleEngine.Core;
using RuleEngine.Phase;
/// <summary>
/// Human player — reads commands from console stdin.
/// Commands: h=hu, p=pung, k=kong, c=chi, 1-9=discard tile by index, q=quit
/// </summary>
public class HumanMahjongPlayer
{
public string Name { get; }
public HumanMahjongPlayer(string name)
{
Name = name;
}
public (string action, int? tile) Decide(List<ActionOption> legalActions, MahjongGameState state)
{
var hand = state.Hands.GetValueOrDefault(Name, new List<int>());
var sorted = hand.OrderBy(t => t).ToList();
while (true)
{
Console.WriteLine();
Console.WriteLine($"=== {Name} 的回合 ===");
Console.WriteLine($"手牌 ({hand.Count}张):");
for (int i = 0; i < sorted.Count; i++)
Console.WriteLine($" [{i + 1}] {MahjongTile.ToString(sorted[i])}");
Console.WriteLine();
// Show available actions
var winOpt = legalActions.FirstOrDefault(a => a.Action == "win");
var pungOpt = legalActions.FirstOrDefault(a => a.Action == "pung");
var kongOpt = legalActions.FirstOrDefault(a => a.Action == "ming_kong" || a.Action == "an_kong");
var chiOpt = legalActions.FirstOrDefault(a => a.Action == "chi");
Console.Write("可选操作: ");
var ops = new List<string>();
if (hand.Count > 0) ops.Add("1-{0}(出牌)".Replace("{0}", hand.Count.ToString()));
if (pungOpt != null) ops.Add("p(碰)");
if (kongOpt != null) ops.Add("k(杠)");
if (chiOpt != null) ops.Add("c(吃)");
if (winOpt != null) ops.Add("h(胡)");
ops.Add("q(退出)");
Console.WriteLine(string.Join(" ", ops));
Console.Write("> ");
string? input = Console.ReadLine()?.Trim().ToLower();
if (string.IsNullOrEmpty(input)) continue;
if (input == "q") return ("pass", null);
if (input == "h" && winOpt != null) return ("win", null);
if (input == "p" && pungOpt != null) return ("pung", null);
if (input == "k" && kongOpt != null) return (kongOpt.Action, null);
if (input == "c" && chiOpt != null) return ("chi", null);
// Discard by index
if (int.TryParse(input, out int idx) && idx >= 1 && idx <= sorted.Count)
{
return ("discard", sorted[idx - 1]);
}
Console.WriteLine("无效输入,请重试");
}
}
}