Some initial work on prescient AI, AI runner, and some random fixes
All checks were successful
Build / Build (push) Successful in 1m3s
All checks were successful
Build / Build (push) Successful in 1m3s
This commit is contained in:
@@ -8,8 +8,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="iluvadev.ConsoleProgressBar"/>
|
||||
<PackageReference Include="Serilog"/>
|
||||
<PackageReference Include="Serilog.Sinks.Console"/>
|
||||
<PackageReference Include="ShellProgressBar"/>
|
||||
<PackageReference Include="System.CommandLine"/>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ using PkmnLib.Plugin.Gen7;
|
||||
using PkmnLib.Static.Species;
|
||||
using PkmnLib.Static.Utils;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using ShellProgressBar;
|
||||
|
||||
namespace AIRunner;
|
||||
|
||||
@@ -20,6 +22,7 @@ public static class TestCommandRunner
|
||||
|
||||
Log.Information("Running {Battles} battles between {AI1} and {AI2}", battles, ai1.Name, ai2.Name);
|
||||
var averageTimePerTurnPerBattle = new List<double>(battles);
|
||||
var turnsPerBattle = new ConcurrentBag<uint>();
|
||||
var results = new ConcurrentBag<BattleResult>();
|
||||
var rootRandom = new RandomImpl();
|
||||
|
||||
@@ -31,27 +34,51 @@ public static class TestCommandRunner
|
||||
randoms[i] = new RandomImpl(rootRandom.GetInt());
|
||||
battleTasks[i] = Task.CompletedTask; // Initialize tasks to avoid null references
|
||||
}
|
||||
// Here you would implement the logic to run the AI scripts against each other.
|
||||
// This is a placeholder for demonstration purposes.
|
||||
|
||||
// Show a progress bar if debug logging is not enabled.
|
||||
// This is to avoid weird console output where the progress bar is drawn in the middle of debug logs.
|
||||
ProgressBar? pb = null;
|
||||
if (!Log.IsEnabled(LogEventLevel.Debug))
|
||||
{
|
||||
pb = new ProgressBar(battles, "Running battles...", new ProgressBarOptions
|
||||
{
|
||||
ShowEstimatedDuration = true,
|
||||
ProgressBarOnBottom = true,
|
||||
});
|
||||
pb.EstimatedDuration = TimeSpan.FromMilliseconds(battles);
|
||||
}
|
||||
for (var i = 0; i < battles; i++)
|
||||
{
|
||||
var taskIndex = i % maxTasks;
|
||||
var index = i;
|
||||
var battleTask = Task.Run(async () =>
|
||||
{
|
||||
Log.Information("Battle {BattleNumber}: {AI1} vs {AI2}", index + 1, ai1.Name, ai2.Name);
|
||||
Log.Debug("Battle {BattleNumber}: {AI1} vs {AI2}", index + 1, ai1.Name, ai2.Name);
|
||||
var random = randoms[taskIndex];
|
||||
var battle = GenerateBattle(library, 3, random);
|
||||
var timePerTurn = new List<double>(20);
|
||||
while (!battle.HasEnded)
|
||||
{
|
||||
if (battle.CurrentTurnNumber > 1000)
|
||||
{
|
||||
Log.Warning("Battle {BattleNumber} exceeded 1000 turns, ending battle early", index + 1);
|
||||
battle.ForceEndBattle();
|
||||
var last10Choices = battle.PreviousTurnChoices.TakeLast(10).ToList();
|
||||
Log.Warning("Last 10 choices: {Choices}", last10Choices);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var res = await GetAndSetChoices(battle, ai1, ai2);
|
||||
timePerTurn.Add(res.MsPerTurn);
|
||||
}
|
||||
var result = battle.Result;
|
||||
Log.Information("Battle {BattleNumber} ended with result: {Result}", index + 1, result);
|
||||
Log.Debug("Battle {BattleNumber} ended with result: {Result}", index + 1, result);
|
||||
averageTimePerTurnPerBattle.Add(timePerTurn.Average());
|
||||
results.Add(result.Value);
|
||||
turnsPerBattle.Add(battle.CurrentTurnNumber);
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
pb?.Tick();
|
||||
});
|
||||
battleTasks[taskIndex] = battleTask;
|
||||
if (i % maxTasks == maxTasks - 1 || i == battles - 1)
|
||||
@@ -62,11 +89,13 @@ public static class TestCommandRunner
|
||||
Array.Fill(battleTasks, Task.CompletedTask); // Reset tasks for the next batch
|
||||
}
|
||||
}
|
||||
pb?.Dispose();
|
||||
|
||||
var t2 = DateTime.UtcNow;
|
||||
Log.Information("{Amount} battles completed in {Duration} ms", battles, (t2 - t1).TotalMilliseconds);
|
||||
var averageTimePerTurn = averageTimePerTurnPerBattle.Average();
|
||||
Log.Information("Average time per turn: {AverageTimePerTurn} ms", averageTimePerTurn);
|
||||
Log.Information("Average turns per battle: {AverageTurnsPerBattle}", turnsPerBattle.Average(x => x));
|
||||
|
||||
var winCount1 = results.Count(x => x.WinningSide == 0);
|
||||
var winCount2 = results.Count(x => x.WinningSide == 1);
|
||||
@@ -123,26 +152,32 @@ public static class TestCommandRunner
|
||||
private static async Task<GetAndSetChoicesResult> GetAndSetChoices(BattleImpl battle, PokemonAI ai1, PokemonAI ai2)
|
||||
{
|
||||
var pokemon1 = battle.Sides[0].Pokemon[0];
|
||||
if (pokemon1 is null)
|
||||
while (pokemon1 is null && !battle.HasEnded)
|
||||
{
|
||||
pokemon1 = battle.Parties[0].Party.WhereNotNull().FirstOrDefault(x => x.IsUsable);
|
||||
if (pokemon1 is null)
|
||||
throw new InvalidOperationException("No usable Pokémon found in party 1.");
|
||||
battle.Sides[0].SwapPokemon(0, pokemon1);
|
||||
pokemon1 = battle.Sides[0].Pokemon[0];
|
||||
}
|
||||
var pokemon2 = battle.Sides[1].Pokemon[0];
|
||||
if (pokemon2 is null)
|
||||
while (pokemon2 is null && !battle.HasEnded)
|
||||
{
|
||||
pokemon2 = battle.Parties[1].Party.WhereNotNull().FirstOrDefault(x => x.IsUsable);
|
||||
if (pokemon2 is null)
|
||||
throw new InvalidOperationException("No usable Pokémon found in party 2.");
|
||||
battle.Sides[1].SwapPokemon(0, pokemon2);
|
||||
pokemon2 = battle.Sides[1].Pokemon[0];
|
||||
}
|
||||
if (pokemon1 is null || pokemon2 is null)
|
||||
{
|
||||
throw new InvalidOperationException("Both Pokémon must be non-null to proceed with the battle.");
|
||||
}
|
||||
|
||||
var taskAiOne = !battle.HasForcedTurn(pokemon1, out var choice1)
|
||||
var taskAiOne = !battle.HasForcedTurn(pokemon1!, out var choice1)
|
||||
? Task.Run(() => ai1.GetChoice(battle, pokemon1))
|
||||
: Task.FromResult(choice1);
|
||||
var taskAiTwo = !battle.HasForcedTurn(pokemon2, out var choice2)
|
||||
var taskAiTwo = !battle.HasForcedTurn(pokemon2!, out var choice2)
|
||||
? Task.Run(() => ai2.GetChoice(battle, pokemon2))
|
||||
: Task.FromResult(choice2);
|
||||
await Task.WhenAll(taskAiOne, taskAiTwo);
|
||||
|
||||
Reference in New Issue
Block a user