feat: CSV加player列+#RESULT汇总 — 训练中直接看胜率进化
This commit is contained in:
@ -499,7 +499,7 @@ namespace PdkFriendServer.Logic
|
|||||||
float s = GamePack.Info.Score[i];
|
float s = GamePack.Info.Score[i];
|
||||||
rewards[i] = s > 0 ? 1f : s < 0 ? -1f : 0f;
|
rewards[i] = s > 0 ? 1f : s < 0 ? -1f : 0f;
|
||||||
}
|
}
|
||||||
TrainingCollector.FlushGame(_flushGameCounter++, rewards);
|
TrainingCollector.FlushGame(_flushGameCounter++, rewards, GamePack.Info.OverPos, BotTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DeskGameDo != null)
|
if (DeskGameDo != null)
|
||||||
|
|||||||
@ -39,19 +39,21 @@ namespace PdkFriendServer.Logic
|
|||||||
/// 游戏结束,输出本轮训练数据到文件。
|
/// 游戏结束,输出本轮训练数据到文件。
|
||||||
/// rewards: 每个玩家的奖励 (1=赢, 0=第二, -1=第三)
|
/// rewards: 每个玩家的奖励 (1=赢, 0=第二, -1=第三)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void FlushGame(int gameId, float[] rewards)
|
public static void FlushGame(int gameId, float[] rewards, int winnerPos, string[] botTypes)
|
||||||
{
|
{
|
||||||
if (!Enabled || _records.Count == 0) return;
|
if (!Enabled || _records.Count == 0) return;
|
||||||
var path = $"training_data/game_{gameId:D5}.csv";
|
var path = $"training_data/game_{gameId:D5}.csv";
|
||||||
System.IO.Directory.CreateDirectory("training_data");
|
System.IO.Directory.CreateDirectory("training_data");
|
||||||
using var w = new System.IO.StreamWriter(path);
|
using var w = new System.IO.StreamWriter(path);
|
||||||
w.WriteLine("state,action,reward");
|
w.WriteLine("state,action,reward,player");
|
||||||
foreach (var r in _records)
|
foreach (var r in _records)
|
||||||
{
|
{
|
||||||
float reward = r.Player >= 0 && r.Player < rewards.Length ? rewards[r.Player] : 0;
|
float reward = r.Player >= 0 && r.Player < rewards.Length ? rewards[r.Player] : 0;
|
||||||
var stateStr = string.Join(",", r.State.Select(f => f.ToString("F6")));
|
var stateStr = string.Join(",", r.State.Select(f => f.ToString("F6")));
|
||||||
w.WriteLine($"\"{stateStr}\",{r.Action},{reward:F3}");
|
w.WriteLine($"\"{stateStr}\",{r.Action},{reward:F3},{r.Player}");
|
||||||
}
|
}
|
||||||
|
// 游戏结果摘要 — 方便不中断训练即可评估模型进化
|
||||||
|
w.WriteLine($"#RESULT,winner={winnerPos},bots={string.Join(",", botTypes ?? new[]{"ISMCTS","ISMCTS","ISMCTS"})}");
|
||||||
_records.Clear();
|
_records.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
67
scripts/scan_wins.py
Normal file
67
scripts/scan_wins.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""扫描 training_data 目录下所有 CSV,按 bot 类型统计胜率"""
|
||||||
|
import sys, os, glob, re
|
||||||
|
from collections import Counter, defaultdict
|
||||||
|
|
||||||
|
def scan(csv_dir='training_data'):
|
||||||
|
csvs = sorted(glob.glob(os.path.join(csv_dir, 'game_*.csv')))
|
||||||
|
if not csvs:
|
||||||
|
print("No CSV files found")
|
||||||
|
return
|
||||||
|
|
||||||
|
total = 0
|
||||||
|
per_bot = defaultdict(lambda: {'wins': 0, 'games': 0})
|
||||||
|
per_game = [] # (game_id, winner_pos, bots_str)
|
||||||
|
|
||||||
|
for fp in csvs:
|
||||||
|
with open(fp) as f:
|
||||||
|
for line in f:
|
||||||
|
if line.startswith('#RESULT'):
|
||||||
|
parts = line.strip().split(',')
|
||||||
|
winner = int(parts[1].split('=')[1])
|
||||||
|
bots = parts[2].split('=')[1].split(',')
|
||||||
|
game_id = os.path.splitext(os.path.basename(fp))[0]
|
||||||
|
per_game.append((game_id, winner, line.strip()))
|
||||||
|
total += 1
|
||||||
|
|
||||||
|
# winner_pos is 1-indexed, bots[winner-1]
|
||||||
|
for i, bt in enumerate(bots):
|
||||||
|
per_bot[bt]['games'] += 1
|
||||||
|
winner_bt = bots[winner - 1] if 0 < winner <= len(bots) else '?'
|
||||||
|
per_bot[winner_bt]['wins'] += 1
|
||||||
|
break
|
||||||
|
|
||||||
|
if total == 0:
|
||||||
|
print("No #RESULT lines found. Run with updated hjha-console.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"{total} games scanned")
|
||||||
|
print(f"{'Bot':<12} {'Wins':>6} {'Rate':>8} {'Games':>6}")
|
||||||
|
print("-" * 35)
|
||||||
|
for bt in sorted(per_bot.keys()):
|
||||||
|
d = per_bot[bt]
|
||||||
|
rate = d['wins'] / d['games'] * 100 if d['games'] > 0 else 0
|
||||||
|
print(f"{bt:<12} {d['wins']:>6} {rate:>7.1f}% {d['games']:>6}")
|
||||||
|
|
||||||
|
# 也统计按 bot 组合
|
||||||
|
combo_stats = defaultdict(lambda: {'total': 0, 'winners': Counter()})
|
||||||
|
for gid, winner, line in per_game:
|
||||||
|
m = re.search(r'bots=([^,]+(?:,[^,]+)*)', line)
|
||||||
|
if m:
|
||||||
|
key = m.group(1)
|
||||||
|
combo_stats[key]['total'] += 1
|
||||||
|
combo_stats[key]['winners'][winner] += 1
|
||||||
|
|
||||||
|
if len(combo_stats) > 0:
|
||||||
|
print(f"\nBy bot composition:")
|
||||||
|
for combo in sorted(combo_stats.keys()):
|
||||||
|
d = combo_stats[combo]
|
||||||
|
bots = combo.split(',')
|
||||||
|
print(f" [{combo}] — {d['total']} games:")
|
||||||
|
for pos, count in sorted(d['winners'].items()):
|
||||||
|
bt = bots[pos-1] if 0 < pos <= len(bots) else '?'
|
||||||
|
print(f" pos{pos}({bt}): {count}/{d['total']} ({count/d['total']*100:.0f}%)")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
d = sys.argv[1] if len(sys.argv) > 1 else 'training_data'
|
||||||
|
scan(d)
|
||||||
Reference in New Issue
Block a user