|
12 | 12 | using System.Collections.Generic; |
13 | 13 | using System.Linq; |
14 | 14 | using System.Threading.Tasks; |
| 15 | +using CSharp.Core; |
15 | 16 | using CSharp.Core.Extensions; |
16 | 17 | using G33kShell.Desktop.Console.Controls; |
17 | 18 | using JetBrains.Annotations; |
@@ -86,44 +87,47 @@ public override void StopScreensaver() |
86 | 87 | private void TrainAiImpl(Action<byte[]> saveBrainBytes) |
87 | 88 | { |
88 | 89 | m_nextGenBrains ??= Enumerable.Range(0, InitialPopSize).Select(_ => CreateBrain()).ToList(); |
| 90 | + m_generation++; |
89 | 91 |
|
90 | | - var games = new (double AverageRating, AiGameBase Game, AiBrainBase Brain)[m_nextGenBrains.Count]; |
91 | | - Parallel.For(0, games.Length, i => |
| 92 | + var gameResults = new (double AverageRating, double BestRating, int GameSeed, AiBrainBase Brain, string GameStats)[m_nextGenBrains.Count]; |
| 93 | + Parallel.For(0, gameResults.Length, i => |
92 | 94 | { |
93 | | - // Play the base game. |
94 | | - var baseGame = CreateGameWithSeed(m_generation); |
95 | | - while (!baseGame.IsGameOver && !m_stopTraining) |
96 | | - baseGame.Tick(); |
97 | | - |
98 | | - var totalRating = baseGame.Rating; |
99 | | - if (baseGame.Rating > 0.0) |
| 95 | + try |
100 | 96 | { |
101 | | - // Play several more games. |
102 | | - var gameCount = 1; |
103 | | - for (var trial = 0; trial < 4 && !m_stopTraining; trial++, gameCount++) |
| 97 | + // Play a few games. |
| 98 | + var brain = m_nextGenBrains[i]; |
| 99 | + var seeds = Enumerable.Range(0, 4).Select(_ => Random.Shared.Next(10000)).ToArray(); |
| 100 | + var games = seeds.Select(seed => CreateGameWithSeed(seed, brain)).ToArray(); |
| 101 | + for (var gameIndex = 0; gameIndex < games.Length; gameIndex++) |
104 | 102 | { |
105 | | - var game = CreateGameWithSeed(Random.Shared.Next(), baseGame.Brain); |
106 | | - while (!game.IsGameOver) |
107 | | - game.Tick(); |
108 | | - totalRating += game.Rating; |
| 103 | + while (!games[gameIndex].IsGameOver && !m_stopTraining) |
| 104 | + games[gameIndex].Tick(); |
109 | 105 | } |
| 106 | + |
| 107 | + // Get the average rating. |
| 108 | + var averageRating = games.FastAvg(o => o.Rating); |
| 109 | + var bestGame = games.FastFindMax(o => o.Rating); |
| 110 | + var bestGameSeed = seeds[games.FastFindIndexOf(bestGame)]; |
| 111 | + |
| 112 | + // Capture game stats. |
| 113 | + var extraStats = bestGame.ExtraGameStats().Select(o => $"{o.Name} {o.Value}").ToCsv('|'); |
110 | 114 |
|
111 | | - totalRating /= gameCount; |
| 115 | + gameResults[i] = (averageRating, bestGame.Rating, bestGameSeed, brain, extraStats); |
| 116 | + } |
| 117 | + catch (Exception e) |
| 118 | + { |
| 119 | + Logger.Instance.Exception("Training failed.", e); |
112 | 120 | } |
113 | | - |
114 | | - games[i] = (totalRating, baseGame, baseGame.Brain); |
115 | 121 | }); |
116 | 122 |
|
117 | 123 | // Select the breeders. |
118 | | - var orderedGames = games.OrderByDescending(o => o.AverageRating).ToArray(); |
| 124 | + var orderedGames = gameResults.OrderByDescending(o => o.AverageRating).ToArray(); |
119 | 125 | var theBest = orderedGames[0]; |
120 | 126 |
|
121 | 127 | // Report summary of results. |
122 | | - m_generation++; |
123 | | - var stats = $"Gen {m_generation}|Pop {m_currentPopSize}|Rating {theBest.AverageRating:F1}|GOAT {m_savedRating:F1}"; |
124 | | - var extraStats = theBest.Game.ExtraGameStats().Select(o => $" {o.Name}: {o.Value}").ToArray().ToCsv().Trim(); |
125 | | - if (!string.IsNullOrEmpty(extraStats)) |
126 | | - stats += $"|{extraStats}"; |
| 128 | + var stats = $"Gen {m_generation}|Pop {m_currentPopSize}|GOAT {m_savedRating:F1}|Rating {theBest.AverageRating:F1}|Seed {theBest.GameSeed}"; |
| 129 | + if (!string.IsNullOrEmpty(theBest.GameStats)) |
| 130 | + stats += $"|{theBest.GameStats}"; |
127 | 131 | System.Console.WriteLine(stats); |
128 | 132 |
|
129 | 133 | // Remember the GOAT brains. |
@@ -163,7 +167,7 @@ private void TrainAiImpl(Action<byte[]> saveBrainBytes) |
163 | 167 | } |
164 | 168 |
|
165 | 169 | // Build the brains for the next generation. |
166 | | - var nextBrains = new List<AiBrainBase>(games.Length); |
| 170 | + var nextBrains = new List<AiBrainBase>(gameResults.Length); |
167 | 171 | nextBrains.AddRange(m_goatBrains.Select(o => o.Brain.Clone())); |
168 | 172 |
|
169 | 173 | // Spawn 5% pure randoms. |
|
0 commit comments