feat(PX): 自训练v1.5 P0准备 — NHN模式 + 总分法胜率解析 + 评估管线

CB-01: Program.cs 加 --nhn 模式 [NEURAL,ISMCTS,NEURAL]
- 3人跑得快 N插花坐排列
- Rotations 数组补全为4种排列 (IIN/NII/INI/NHN)
- --nhn 参数: 不生成CSV, 直接评估

CB-02: 胜率解析统一总分法
- TrainingCollector 加 ScoreAccumulator/BotWins/BotGames 静态累加器
- GameOver 无条件调用 AccumulateScore (eval + 训练模式均记录)
- stress_summary.txt 输出 #BOT_SCORES: bot=wins/games,wr=XX%,score=YYY
- orchestrator eval 解析改用 #BOT_SCORES 格式, 正则回退逻辑移除

管线: orchestrator eval→stress_summary→总分法解析, 闭环可验证
This commit is contained in:
2026-07-20 15:40:59 +08:00
parent e777aef687
commit 7566fe99a1
4 changed files with 85 additions and 15 deletions

View File

@ -492,7 +492,6 @@ namespace PdkFriendServer.Logic
#endif
// 训练数据收集:输出本局所有 ISMCTS 决策
if (TrainingCollector.Enabled)
{
float[] rewards = new float[GamePack.Info.PlayNum];
for (int i = 0; i < GamePack.Info.PlayNum; i++)
@ -501,6 +500,8 @@ namespace PdkFriendServer.Logic
float s = GamePack.Info.Score[i];
rewards[i] = s > 0 ? 1f : s < 0 ? -1f : 0f;
}
var rawScores = GamePack.Info.Score.Select(s => (float)s).ToArray();
TrainingCollector.AccumulateScore(BotTypes, rawScores);
TrainingCollector.FlushGame(_flushGameCounter++, rewards, GamePack.Info.OverPos, BotTypes);
}

View File

@ -16,6 +16,15 @@ namespace PdkFriendServer.Logic
private static readonly List<TrainingRecord> _records = new List<TrainingRecord>();
private static readonly string _sessionPrefix = DateTime.Now.ToString("yyyyMMdd_HHmm");
/// <summary>总分累加器botType → 累积 Score用于评估统计</summary>
public static readonly Dictionary<string, float> ScoreAccumulator = new Dictionary<string, float>();
/// <summary>每 bot 的胜局计数botType → wins</summary>
public static readonly Dictionary<string, int> BotWins = new Dictionary<string, int>();
/// <summary>每 bot 的总局数botType → total games</summary>
public static readonly Dictionary<string, int> BotGames = new Dictionary<string, int>();
private static readonly int[] Ranks = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16 };
private const int NumRanks = 13;
private const int NumSuits = 4;
@ -58,6 +67,42 @@ namespace PdkFriendServer.Logic
_records.Clear();
}
/// <summary>
/// 累加 bot 得分botType → Score 总和 + win/game 计数(总分法评估用)
/// </summary>
public static void AccumulateScore(string[] botTypes, float[] rawScores)
{
if (botTypes == null || rawScores == null) return;
for (int i = 0; i < botTypes.Length && i < rawScores.Length; i++)
{
string t = botTypes[i];
if (!ScoreAccumulator.ContainsKey(t))
ScoreAccumulator[t] = 0;
ScoreAccumulator[t] += rawScores[i];
if (!BotGames.ContainsKey(t))
BotGames[t] = 0;
BotGames[t]++;
if (rawScores[i] > 0)
{
if (!BotWins.ContainsKey(t))
BotWins[t] = 0;
BotWins[t]++;
}
}
}
/// <summary>
/// 重置总分累加器
/// </summary>
public static void ResetAccumulator()
{
ScoreAccumulator.Clear();
BotWins.Clear();
BotGames.Clear();
}
/// <summary>
/// 状态编码:与 Python encode_game_state 完全一致。
/// </summary>