集成测试 (10个): - 发牌: 庄14闲13, 牌墙55 - 回合流转: 庄家第一轮不摸牌, 非庄家摸牌后出牌 - 数据完整性: 10回合后手牌+副露+牌墙+弃牌=108 - 对局完整性: 4种DSL全部不出错完成 - 构造听牌: 完整手牌第一轮自摸 手动出牌 (--human): - HumanMahjongPlayer: 显示手牌/可选操作/h=胡/p=碰/k=杠/1-N=出牌 - MahjongRoom: humanPlayer参数 修复: DiscardPool碰/胡后不移除→幽灵牌→总牌数溢出
69 lines
2.5 KiB
C#
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("无效输入,请重试");
|
|
}
|
|
}
|
|
}
|