using PdkFriendServer.Logic; using System; using System.Diagnostics; using System.IO; namespace hjha_console { class Program { public static int RotateInterval = 0; // 0=disabled, N=rotate every N rounds private static readonly string[][] Rotations = new[] { // 3种排列 new[] { "ISMCTS", "ISMCTS", "NEURAL" }, new[] { "NEURAL", "ISMCTS", "ISMCTS" }, new[] { "ISMCTS", "NEURAL", "ISMCTS" } }; static void Main(string[] args) { if (args.Length > 0 && args[0] == "--eval") { PdkGameMain.BotTypes = new[] { "ISMCTS", "ISMCTS", "NEURAL" }; TrainingCollector.Enabled = false; args = new[] { "--stress", args.Length > 1 ? args[1] : "10" }; } if (args.Length > 0 && args[0] == "--mix") { PdkGameMain.BotTypes = new[] { "ISMCTS", "ISMCTS", "NEURAL" }; TrainingCollector.Enabled = true; args = new[] { "--stress", args.Length > 1 ? args[1] : "10" }; } if (args.Length > 0 && args[0] == "--mix-rotate") { PdkGameMain.BotTypes = new[] { "ISMCTS", "ISMCTS", "NEURAL" }; TrainingCollector.Enabled = true; Program.RotateInterval = 10; args = new[] { "--stress", args.Length > 1 ? args[1] : "10" }; } if (args.Length > 0 && args[0] == "--mix-ismcts") { PdkGameMain.BotTypes = new[] { "ISMCTS", "ISMCTS", "ISMCTS" }; TrainingCollector.Enabled = true; args = new[] { "--stress", args.Length > 1 ? args[1] : "10" }; } if (args.Length > 0 && args[0] == "--bots") { PdkGameMain.BotTypes = args[1].Split(','); TrainingCollector.Enabled = true; args = args.Length > 2 ? new[] { "--stress", args[2] } : new[] { "--stress", "10" }; } if (args.Length > 0 && args[0] == "--n2h2v5") { PdkGameMain.BotTypes = new[] { "ISMCTS", "ISMCTS", "NEURAL_V5" }; TrainingCollector.Enabled = false; args = new[] { "--stress", args.Length > 1 ? args[1] : "10" }; } if (args.Length > 0 && args[0] == "--stress") { int rounds = args.Length > 1 ? int.Parse(args[1]) : 100; RunStressTest(rounds); return; } Console.WriteLine("=== HJHA Console Mode - 跑得快 Bot对局 (3人自动) ==="); Console.WriteLine("牌型:关牌玩法 16张/人 | 策略:ISMCTS 800sims"); Console.WriteLine("压测: dotnet run --project hjha-console -- --stress 1000"); Console.WriteLine(); var main = new PdkGameMain(null); main.BotMode = true; main.Test(args.Length > 0 ? args[0] : ""); } static void RunStressTest(int totalRounds) { var logDir = "stress_logs"; if (!Directory.Exists(logDir)) Directory.CreateDirectory(logDir); // 清理旧日志 foreach (var f in Directory.GetFiles(logDir, "round_*.txt")) File.Delete(f); File.Delete("stress_errors.txt"); File.Delete("stress_summary.txt"); Console.WriteLine($"=== PdkBot Stress Test: {totalRounds} rounds ==="); Console.WriteLine($"Logs: {logDir}/"); Console.WriteLine($"Start: {DateTime.Now:HH:mm:ss}"); Console.WriteLine(); int completed = 0, errors = 0; var totalSw = Stopwatch.StartNew(); var errorLog = new StreamWriter("stress_errors.txt", true) { AutoFlush = true }; for (int r = 1; r <= totalRounds; r++) { // 位置轮换: 每N局切换排列 if (RotateInterval > 0 && (r - 1) % RotateInterval == 0) { int ri = ((r - 1) / RotateInterval) % Rotations.Length; PdkGameMain.BotTypes = Rotations[ri]; } var roundSw = Stopwatch.StartNew(); try { var main = new PdkGameMain(null); main.BotMode = true; // 本轮日志路径 var logPath = $"{logDir}/round_{r:D5}.txt"; if (File.Exists("output.txt")) File.Delete("output.txt"); // 重定向 Console stdout → 本轮 ISMCTS 日志 var ismctsLog = $"{logDir}/round_{r:D5}_ismcts.txt"; var oldOut = Console.Out; var ismctsWriter = new StreamWriter(ismctsLog) { AutoFlush = true }; Console.SetOut(ismctsWriter); main.Test(""); Console.SetOut(oldOut); // 先恢复再Close (macOS兼容) ismctsWriter.Close(); // 合并 output.txt + ISMCTS 日志 → 本轮日志 using (var combined = new StreamWriter(logPath, false)) { if (File.Exists(ismctsLog)) { combined.Write(File.ReadAllText(ismctsLog)); combined.WriteLine(); File.Delete(ismctsLog); } if (File.Exists("output.txt")) { combined.Write(File.ReadAllText("output.txt")); } } if (File.Exists("output.txt")) File.Delete("output.txt"); completed++; roundSw.Stop(); if (r % 50 == 0 || r == totalRounds) { Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {r}/{totalRounds} rounds | " + $"{errors} errors | avg {totalSw.Elapsed.TotalSeconds / r:F1}s/round | " + $"last {roundSw.ElapsedMilliseconds}ms"); } } catch (Exception ex) { errors++; errorLog.WriteLine($"Round {r}: {ex.GetType().Name}: {ex.Message}"); errorLog.WriteLine(ex.StackTrace); errorLog.WriteLine("---"); Console.WriteLine($" R{r} ERROR: {ex.GetType().Name}: {ex.Message}"); // 保存本轮部分日志 if (File.Exists("output.txt")) File.Move("output.txt", $"{logDir}/round_{r:D5}_ERROR.txt"); } } totalSw.Stop(); errorLog.Close(); // 汇总 Console.WriteLine(); Console.WriteLine($"=== Stress Test Complete ==="); Console.WriteLine($"Rounds: {completed}/{totalRounds} completed, {errors} errors"); Console.WriteLine($"Total time: {totalSw.Elapsed.TotalMinutes:F1} min"); Console.WriteLine($"Avg per round: {totalSw.Elapsed.TotalSeconds / totalRounds:F1}s"); File.WriteAllText("stress_summary.txt", $"Rounds: {completed}/{totalRounds}\n" + $"Errors: {errors}\n" + $"Total: {totalSw.Elapsed.TotalMinutes:F1}min\n" + $"Avg: {totalSw.Elapsed.TotalSeconds / totalRounds:F1}s/round\n"); } } }