diff --git a/.gitignore b/.gitignore index 7837a5d..24b69a8 100644 --- a/.gitignore +++ b/.gitignore @@ -353,7 +353,9 @@ MigrationBackup/ /cloc.bat /Cosette.Arbiter/settings.json /Cosette.Arbiter/book.bin -/Cosette.Tuner/settings.json -/Cosette.Tuner/book.bin +/Cosette.Tuner.SelfPlay/settings.json +/Cosette.Tuner.SelfPlay/book.bin +/Cosette.Tuner.Texel/settings.json +/Cosette.Tuner.Texel/positions.epd /Cosette.Tuner.Web/Database.sqlite /Cosette.Tuner.Web/wwwroot/lib/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 57e3d1b..90f0ad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# Version 4.0 (Komekko), 22.01.2021 + - Added Texel project + - Added check extension + - Added check detection in quiescence search + - Added evasion moves generator + - Added static null-move pruning + - Added futility pruning + - Added new formulas calculating reduction for LMR and null move pruning + - Added a new way of building Piece-Square tables, better for tuning + - Fixed reduntant saving of transposition table entries + - Adjusted search parameters + - Adjusted evaluation parameters + - Improved threefold repetition detection + - Updated .NET runtime version to 5.0.102 + # Version 3.0 (Luna), 12.12.2020 - Added better time control for Arbiter - Added more UCI options @@ -26,9 +41,7 @@ - Disabled returning of exact transposition table entries in the PV nodes - Adjusted evaluation scores - Optimized king safety evaluation - - Updated .NET Core runtime version to 5.0.100 - -Estimated strength: 2000 ELO + - Updated .NET runtime version to 5.0.100 # Version 2.0 (Darkness), 19.10.2020 - Added fifty-move rule detection @@ -52,9 +65,5 @@ Estimated strength: 2000 ELO - Adjusted move ordering scores - Updated .NET Core runtime version to 3.1.403 -Estimated strength: 1950 ELO - # Version 1.0 (Aqua), 19.09.2020 - - Initial version - -Estimated strength: 1900 ELO \ No newline at end of file + - Initial version \ No newline at end of file diff --git a/Cosette.Arbiter/Engine/EngineOperator.cs b/Cosette.Arbiter/Engine/EngineOperator.cs index ca17a52..961a42a 100644 --- a/Cosette.Arbiter/Engine/EngineOperator.cs +++ b/Cosette.Arbiter/Engine/EngineOperator.cs @@ -75,6 +75,8 @@ public BestMoveData Go(List moves, int whiteClock, int blackClock) if (moves.Count > 0) { Write($"position startpos moves {movesJoined}"); + Write("isready"); + WaitForMessage("readyok"); } Write($"go wtime {whiteClock} btime {blackClock} winc {SettingsLoader.Data.IncTime} binc {SettingsLoader.Data.IncTime}"); diff --git a/Cosette.Arbiter/Tournament/TournamentArbiter.cs b/Cosette.Arbiter/Tournament/TournamentArbiter.cs index 7077453..335b86b 100644 --- a/Cosette.Arbiter/Tournament/TournamentArbiter.cs +++ b/Cosette.Arbiter/Tournament/TournamentArbiter.cs @@ -140,7 +140,7 @@ private void WriteResults() Console.WriteLine($"{participant.EngineData.Name} {originalRating} ELO ({performance:+0;-#}, {wonGamesPercent}%): " + $"{participant.Wins} wins ({winsByTime} by time), {participant.Losses} losses ({lossesByTime} by time), " + $"{participant.Draws} draws"); - Console.WriteLine($" === {participant.AverageDepth:F1} average depth, {participant.AverageNodesCount} average nodes, " + + Console.WriteLine($" === {participant.AverageDepth:F} average depth, {participant.AverageNodesCount} average nodes, " + $"{participant.AverageNps} average nodes per second"); Console.WriteLine($"Executable hash: {participant.EngineOperator.ExecutableHash.Value}"); Console.WriteLine(); diff --git a/Cosette.Tests/AdvancedPerftTests.cs b/Cosette.Tests/AdvancedPerftTests.cs index db8f4a0..866dd0e 100644 --- a/Cosette.Tests/AdvancedPerftTests.cs +++ b/Cosette.Tests/AdvancedPerftTests.cs @@ -1,4 +1,5 @@ -using Cosette.Engine.Board; +using Cosette.Engine.Ai.Score.PieceSquareTables; +using Cosette.Engine.Board; using Cosette.Engine.Moves.Magic; using Cosette.Engine.Perft; using Xunit; @@ -10,6 +11,7 @@ public class AdvancedPerftTests public AdvancedPerftTests() { MagicBitboards.InitWithInternalKeys(); + PieceSquareTablesData.BuildPieceSquareTables(); } [Theory] @@ -24,7 +26,7 @@ public AdvancedPerftTests() public void AdvancedPerft_DefaultBoard(int depth, ulong expectedLeafsCount, ulong expectedCapturesCount, ulong expectedEnPassantsCount, ulong expectedCastlesCount, ulong expectedChecksCount, ulong expectedCheckmatesCount) { - var boardState = new BoardState(); + var boardState = new BoardState(true); boardState.SetDefaultState(); var result = AdvancedPerft.Run(boardState, depth); diff --git a/Cosette.Tests/DividedPerftTests.cs b/Cosette.Tests/DividedPerftTests.cs index de198d8..2dc942d 100644 --- a/Cosette.Tests/DividedPerftTests.cs +++ b/Cosette.Tests/DividedPerftTests.cs @@ -1,4 +1,5 @@ -using Cosette.Engine.Board; +using Cosette.Engine.Ai.Score.PieceSquareTables; +using Cosette.Engine.Board; using Cosette.Engine.Moves.Magic; using Cosette.Engine.Perft; using Xunit; @@ -10,12 +11,13 @@ public class DividedPerftTests public DividedPerftTests() { MagicBitboards.InitWithInternalKeys(); + PieceSquareTablesData.BuildPieceSquareTables(); } [Fact] public void DividedPerft_DefaultBoard() { - var boardState = new BoardState(); + var boardState = new BoardState(true); boardState.SetDefaultState(); var result = DividedPerft.Run(boardState, 6); diff --git a/Cosette.Tests/FenTests.cs b/Cosette.Tests/FenTests.cs index ca523f2..541098b 100644 --- a/Cosette.Tests/FenTests.cs +++ b/Cosette.Tests/FenTests.cs @@ -1,4 +1,5 @@ -using Cosette.Engine.Board; +using Cosette.Engine.Ai.Score.PieceSquareTables; +using Cosette.Engine.Board; using Cosette.Engine.Fen; using Cosette.Engine.Moves.Magic; using Cosette.Engine.Perft; @@ -8,6 +9,11 @@ namespace Cosette.Tests { public class FenTests { + public FenTests() + { + PieceSquareTablesData.BuildPieceSquareTables(); + } + [Theory] [InlineData("5r2/2P4p/P5Pp/1b6/P1r1N2N/1pp4K/P7/5k2 w - - 0 10")] [InlineData("3K2N1/k7/p1p3qB/3p1R1Q/6P1/1pP4r/P6r/8 w KQ - 20 25")] @@ -16,7 +22,7 @@ public class FenTests [InlineData("r5R1/8/2Bp3p/2kq4/2N1p3/2KPP3/2pRP1P1/8 w - h3 0 1")] public void DividedPerft_DefaultBoard(string fen) { - var boardFromFen = FenToBoard.Parse(fen); + var boardFromFen = FenToBoard.Parse(fen, true); Assert.Equal(fen, boardFromFen.ToString()); } } diff --git a/Cosette.Tests/SimplePerftTests.cs b/Cosette.Tests/SimplePerftTests.cs index 957b3c5..f9e76c5 100644 --- a/Cosette.Tests/SimplePerftTests.cs +++ b/Cosette.Tests/SimplePerftTests.cs @@ -1,3 +1,4 @@ +using Cosette.Engine.Ai.Score.PieceSquareTables; using Cosette.Engine.Board; using Cosette.Engine.Fen; using Cosette.Engine.Moves.Magic; @@ -11,6 +12,7 @@ public class SimplePerftTests public SimplePerftTests() { MagicBitboards.InitWithInternalKeys(); + PieceSquareTablesData.BuildPieceSquareTables(); } [Theory] @@ -24,7 +26,7 @@ public SimplePerftTests() [InlineData(7, 3195901860)] public void SimplePerft_DefaultBoard(int depth, ulong expectedLeafsCount) { - var boardState = new BoardState(); + var boardState = new BoardState(true); boardState.SetDefaultState(); var result = SimplePerft.Run(boardState, depth); @@ -40,7 +42,7 @@ public void SimplePerft_DefaultBoard(int depth, ulong expectedLeafsCount) [InlineData(5, 120955130)] public void SimplePerft_MidGameBoard(int depth, ulong expectedLeafsCount) { - var boardState = FenToBoard.Parse("r2qr1k1/p2n1p2/1pb3pp/2ppN1P1/1R1PpP2/BQP1n1PB/P4N1P/1R4K1 w - - 0 21"); + var boardState = FenToBoard.Parse("r2qr1k1/p2n1p2/1pb3pp/2ppN1P1/1R1PpP2/BQP1n1PB/P4N1P/1R4K1 w - - 0 21", true); var result = SimplePerft.Run(boardState, depth); Assert.Equal(expectedLeafsCount, result.LeafsCount); @@ -56,7 +58,7 @@ public void SimplePerft_MidGameBoard(int depth, ulong expectedLeafsCount) [InlineData(6, 108146453)] public void SimplePerft_EndGameBoard(int depth, ulong expectedLeafsCount) { - var boardState = FenToBoard.Parse("7r/8/2k3P1/1p1p2Kp/1P6/2P5/7r/Q7 w - - 0 1"); + var boardState = FenToBoard.Parse("7r/8/2k3P1/1p1p2Kp/1P6/2P5/7r/Q7 w - - 0 1", true); var result = SimplePerft.Run(boardState, depth); Assert.Equal(expectedLeafsCount, result.LeafsCount); diff --git a/Cosette.Tests/StaticExchangeEvaluationTests.cs b/Cosette.Tests/StaticExchangeEvaluationTests.cs index f8f3069..5e97cae 100644 --- a/Cosette.Tests/StaticExchangeEvaluationTests.cs +++ b/Cosette.Tests/StaticExchangeEvaluationTests.cs @@ -1,4 +1,5 @@ using Cosette.Engine.Ai.Ordering; +using Cosette.Engine.Ai.Score.PieceSquareTables; using Cosette.Engine.Common; using Xunit; @@ -9,6 +10,7 @@ public class StaticExchangeEvaluationTests public StaticExchangeEvaluationTests() { StaticExchangeEvaluation.Init(); + PieceSquareTablesData.BuildPieceSquareTables(); } [Theory] diff --git a/Cosette.Tests/VerificationPerftTests.cs b/Cosette.Tests/VerificationPerftTests.cs index 77963d4..99aada7 100644 --- a/Cosette.Tests/VerificationPerftTests.cs +++ b/Cosette.Tests/VerificationPerftTests.cs @@ -1,4 +1,5 @@ -using Cosette.Engine.Board; +using Cosette.Engine.Ai.Score.PieceSquareTables; +using Cosette.Engine.Board; using Cosette.Engine.Fen; using Cosette.Engine.Moves.Magic; using Cosette.Engine.Perft; @@ -11,12 +12,13 @@ public class VerificationPerftTests public VerificationPerftTests() { MagicBitboards.InitWithInternalKeys(); + PieceSquareTablesData.BuildPieceSquareTables(); } [Fact] public void VerificationPerft_DefaultBoard() { - var boardState = new BoardState(); + var boardState = new BoardState(true); boardState.SetDefaultState(); var result = VerificationPerft.Run(boardState, 6); @@ -26,7 +28,7 @@ public void VerificationPerft_DefaultBoard() [Fact] public void VerificationPerft_MidGameBoard() { - var boardState = FenToBoard.Parse("r2qr1k1/p2n1p2/1pb3pp/2ppN1P1/1R1PpP2/BQP1n1PB/P4N1P/1R4K1 w - - 0 21"); + var boardState = FenToBoard.Parse("r2qr1k1/p2n1p2/1pb3pp/2ppN1P1/1R1PpP2/BQP1n1PB/P4N1P/1R4K1 w - - 0 21", true); var result = VerificationPerft.Run(boardState, 5); Assert.True(result.VerificationSuccess); @@ -35,7 +37,7 @@ public void VerificationPerft_MidGameBoard() [Fact] public void VerificationPerft_EndGameBoard() { - var boardState = FenToBoard.Parse("7r/8/2k3P1/1p1p2Kp/1P6/2P5/7r/Q7 w - - 0 1"); + var boardState = FenToBoard.Parse("7r/8/2k3P1/1p1p2Kp/1P6/2P5/7r/Q7 w - - 0 1", true); var result = VerificationPerft.Run(boardState, 6); Assert.True(result.VerificationSuccess); diff --git a/Cosette.Tuner.Common/Cosette.Tuner.Common.csproj b/Cosette.Tuner.Common/Cosette.Tuner.Common.csproj index 9f5c4f4..6e86919 100644 --- a/Cosette.Tuner.Common/Cosette.Tuner.Common.csproj +++ b/Cosette.Tuner.Common/Cosette.Tuner.Common.csproj @@ -4,4 +4,8 @@ netstandard2.0 + + + + diff --git a/Cosette.Tuner.Common/Requests/ChromosomeDataRequest.cs b/Cosette.Tuner.Common/Requests/ChromosomeDataRequest.cs index 5c0e0b9..7f9c610 100644 --- a/Cosette.Tuner.Common/Requests/ChromosomeDataRequest.cs +++ b/Cosette.Tuner.Common/Requests/ChromosomeDataRequest.cs @@ -6,13 +6,10 @@ public class ChromosomeDataRequest { public int TestId { get; set; } public double ElapsedTime { get; set; } - public int Fitness { get; set; } - public int ReferenceEngineWins { get; set; } - public int ExperimentalEngineWins { get; set; } - public int Draws { get; set; } + public double Fitness { get; set; } - public EngineStatisticsDataRequest ReferenceEngineStatistics { get; set; } - public EngineStatisticsDataRequest ExperimentalEngineStatistics { get; set; } + public SelfPlayStatisticsDataRequest ReferenceEngineStatistics { get; set; } + public SelfPlayStatisticsDataRequest ExperimentalEngineStatistics { get; set; } public List Genes { get; set; } } } diff --git a/Cosette.Tuner.Common/Requests/GenerationDataRequest.cs b/Cosette.Tuner.Common/Requests/GenerationDataRequest.cs index f43e7f3..dc6a9b0 100644 --- a/Cosette.Tuner.Common/Requests/GenerationDataRequest.cs +++ b/Cosette.Tuner.Common/Requests/GenerationDataRequest.cs @@ -6,7 +6,7 @@ public class GenerationDataRequest { public int TestId { get; set; } public double ElapsedTime { get; set; } - public int BestFitness { get; set; } + public double BestFitness { get; set; } public List BestChromosomeGenes { get; set; } } diff --git a/Cosette.Tuner.Common/Requests/RegisterTestRequest.cs b/Cosette.Tuner.Common/Requests/RegisterTestRequest.cs new file mode 100644 index 0000000..35a2a8c --- /dev/null +++ b/Cosette.Tuner.Common/Requests/RegisterTestRequest.cs @@ -0,0 +1,7 @@ +namespace Cosette.Tuner.Common.Requests +{ + public class RegisterTestRequest + { + public TestType Type { get; set; } + } +} diff --git a/Cosette.Tuner.Common/Requests/EngineStatisticsDataRequest.cs b/Cosette.Tuner.Common/Requests/SelfPlayStatisticsDataRequest.cs similarity index 68% rename from Cosette.Tuner.Common/Requests/EngineStatisticsDataRequest.cs rename to Cosette.Tuner.Common/Requests/SelfPlayStatisticsDataRequest.cs index d8aef7d..e31dafe 100644 --- a/Cosette.Tuner.Common/Requests/EngineStatisticsDataRequest.cs +++ b/Cosette.Tuner.Common/Requests/SelfPlayStatisticsDataRequest.cs @@ -1,10 +1,12 @@ namespace Cosette.Tuner.Common.Requests { - public class EngineStatisticsDataRequest + public class SelfPlayStatisticsDataRequest { public double AverageTimePerGame { get; set; } public double AverageDepth { get; set; } public double AverageNodesCount { get; set; } public double AverageNodesPerSecond { get; set; } + public int Wins { get; set; } + public int Draws { get; set; } } } diff --git a/Cosette.Tuner.Common/Requests/TestType.cs b/Cosette.Tuner.Common/Requests/TestType.cs new file mode 100644 index 0000000..1c0a5f7 --- /dev/null +++ b/Cosette.Tuner.Common/Requests/TestType.cs @@ -0,0 +1,8 @@ +namespace Cosette.Tuner.Common.Requests +{ + public enum TestType + { + SelfPlay, + Texel + } +} diff --git a/Cosette.Tuner/Web/WebService.cs b/Cosette.Tuner.Common/Services/WebService.cs similarity index 90% rename from Cosette.Tuner/Web/WebService.cs rename to Cosette.Tuner.Common/Services/WebService.cs index 0a8e8b8..96c3244 100644 --- a/Cosette.Tuner/Web/WebService.cs +++ b/Cosette.Tuner.Common/Services/WebService.cs @@ -1,13 +1,12 @@ using System; using System.Net.Http; -using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using Cosette.Tuner.Common.Requests; using Cosette.Tuner.Common.Responses; using Newtonsoft.Json; -namespace Cosette.Tuner.Web +namespace Cosette.Tuner.Common.Services { public class WebService { @@ -42,7 +41,7 @@ public async Task EnableIfAvailable() } } - public async Task RegisterTest() + public async Task RegisterTest(RegisterTestRequest requestData) { if (!_enabled) { @@ -51,7 +50,8 @@ public async Task RegisterTest() try { - var response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Post, "test/register")); + var httpContent = new StringContent(JsonConvert.SerializeObject(requestData), Encoding.UTF8, "application/json"); + var response = await _httpClient.PostAsync("test/register", httpContent); var responseContent = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) diff --git a/Cosette.Tuner/Cosette.Tuner.csproj b/Cosette.Tuner.SelfPlay/Cosette.Tuner.SelfPlay.csproj similarity index 100% rename from Cosette.Tuner/Cosette.Tuner.csproj rename to Cosette.Tuner.SelfPlay/Cosette.Tuner.SelfPlay.csproj diff --git a/Cosette.Tuner/Engine/BestMoveData.cs b/Cosette.Tuner.SelfPlay/Engine/BestMoveData.cs similarity index 76% rename from Cosette.Tuner/Engine/BestMoveData.cs rename to Cosette.Tuner.SelfPlay/Engine/BestMoveData.cs index faf8236..55a5384 100644 --- a/Cosette.Tuner/Engine/BestMoveData.cs +++ b/Cosette.Tuner.SelfPlay/Engine/BestMoveData.cs @@ -1,4 +1,4 @@ -namespace Cosette.Tuner.Engine +namespace Cosette.Tuner.SelfPlay.Engine { public class BestMoveData { diff --git a/Cosette.Tuner/Engine/EngineOperator.cs b/Cosette.Tuner.SelfPlay/Engine/EngineOperator.cs similarity index 95% rename from Cosette.Tuner/Engine/EngineOperator.cs rename to Cosette.Tuner.SelfPlay/Engine/EngineOperator.cs index f9cd0ec..1cd4b71 100644 --- a/Cosette.Tuner/Engine/EngineOperator.cs +++ b/Cosette.Tuner.SelfPlay/Engine/EngineOperator.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using Cosette.Tuner.Settings; +using Cosette.Tuner.SelfPlay.Settings; -namespace Cosette.Tuner.Engine +namespace Cosette.Tuner.SelfPlay.Engine { public class EngineOperator { @@ -84,6 +84,8 @@ public BestMoveData Go(List moves, int whiteClock, int blackClock) if (moves.Count > 0) { Write($"position startpos moves {movesJoined}"); + Write("isready"); + WaitForMessage("readyok"); } Write($"go wtime {whiteClock} btime {blackClock} winc {SettingsLoader.Data.IncTime} binc {SettingsLoader.Data.IncTime}"); diff --git a/Cosette.Tuner/Engine/InfoData.cs b/Cosette.Tuner.SelfPlay/Engine/InfoData.cs similarity index 95% rename from Cosette.Tuner/Engine/InfoData.cs rename to Cosette.Tuner.SelfPlay/Engine/InfoData.cs index 82fb449..58b351a 100644 --- a/Cosette.Tuner/Engine/InfoData.cs +++ b/Cosette.Tuner.SelfPlay/Engine/InfoData.cs @@ -1,7 +1,6 @@ using System; -using System.Collections.Generic; -namespace Cosette.Tuner.Engine +namespace Cosette.Tuner.SelfPlay.Engine { public class InfoData { diff --git a/Cosette.Tuner.SelfPlay/Genetics/EvaluationChromosome.cs b/Cosette.Tuner.SelfPlay/Genetics/EvaluationChromosome.cs new file mode 100644 index 0000000..9ac3cc9 --- /dev/null +++ b/Cosette.Tuner.SelfPlay/Genetics/EvaluationChromosome.cs @@ -0,0 +1,27 @@ +using Cosette.Tuner.SelfPlay.Settings; +using GeneticSharp.Domain.Chromosomes; +using GeneticSharp.Domain.Randomizations; + +namespace Cosette.Tuner.SelfPlay.Genetics +{ + public class EvaluationChromosome : ChromosomeBase + { + public EvaluationChromosome() : base(SettingsLoader.Data.Genes.Count) + { + CreateGenes(); + } + + public override Gene GenerateGene(int geneIndex) + { + var gene = SettingsLoader.Data.Genes[geneIndex]; + var value = RandomizationProvider.Current.GetInt(gene.MinValue, gene.MaxValue + 1); + + return new Gene(value); + } + + public override IChromosome CreateNew() + { + return new EvaluationChromosome(); + } + } +} diff --git a/Cosette.Tuner/Genetics/EvaluationFitness.cs b/Cosette.Tuner.SelfPlay/Genetics/EvaluationFitness.cs similarity index 83% rename from Cosette.Tuner/Genetics/EvaluationFitness.cs rename to Cosette.Tuner.SelfPlay/Genetics/EvaluationFitness.cs index d2d523f..91466ab 100644 --- a/Cosette.Tuner/Genetics/EvaluationFitness.cs +++ b/Cosette.Tuner.SelfPlay/Genetics/EvaluationFitness.cs @@ -1,16 +1,15 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using Cosette.Polyglot; -using Cosette.Tuner.Common.Requests; -using Cosette.Tuner.Engine; -using Cosette.Tuner.Genetics.Game; -using Cosette.Tuner.Settings; -using Cosette.Tuner.Web; +using Cosette.Tuner.Common.Services; +using Cosette.Tuner.SelfPlay.Engine; +using Cosette.Tuner.SelfPlay.Genetics.Game; +using Cosette.Tuner.SelfPlay.Settings; +using Cosette.Tuner.SelfPlay.Web; using GeneticSharp.Domain.Chromosomes; using GeneticSharp.Domain.Fitnesses; -namespace Cosette.Tuner.Genetics +namespace Cosette.Tuner.SelfPlay.Genetics { public class EvaluationFitness : IFitness { @@ -45,8 +44,20 @@ public double Evaluate(IChromosome chromosome) experimentalParticipant.EngineOperator.SetOption(geneName, geneValue); } - referenceParticipant.EngineOperator.ApplyOptions(); - experimentalParticipant.EngineOperator.ApplyOptions(); + while (true) + { + try + { + referenceParticipant.EngineOperator.ApplyOptions(); + experimentalParticipant.EngineOperator.ApplyOptions(); + break; + } + catch + { + referenceParticipant.EngineOperator.Restart(); + experimentalParticipant.EngineOperator.Restart(); + } + } var stopwatch = Stopwatch.StartNew(); var (whitePlayer, blackPlayer) = (referenceParticipant, experimentalParticipant); @@ -115,7 +126,7 @@ public double Evaluate(IChromosome chromosome) } var elapsedTime = (double)stopwatch.ElapsedMilliseconds / 1000; - var fitness = experimentalParticipant.Wins - referenceParticipant.Wins + referenceParticipant.Draws / 2; + var fitness = CalculateEloPerformance(experimentalParticipant.Wins, experimentalParticipant.Losses, experimentalParticipant.Draws); var chromosomeRequest = RequestsFactory.CreateChromosomeRequest(_testId, fitness, elapsedTime, chromosome, referenceParticipant, experimentalParticipant); _webService.SendChromosomeData(chromosomeRequest).GetAwaiter().GetResult(); @@ -123,5 +134,10 @@ public double Evaluate(IChromosome chromosome) Console.WriteLine($"[{DateTime.Now}] Run done! Fitness: {fitness}"); return fitness; } + + private int CalculateEloPerformance(int wins, int losses, int draws) + { + return 400 * (wins - losses) / (wins + losses + draws); + } } } diff --git a/Cosette.Tuner/Genetics/Game/ArchivedGame.cs b/Cosette.Tuner.SelfPlay/Genetics/Game/ArchivedGame.cs similarity index 84% rename from Cosette.Tuner/Genetics/Game/ArchivedGame.cs rename to Cosette.Tuner.SelfPlay/Genetics/Game/ArchivedGame.cs index c0bdd17..48f08d1 100644 --- a/Cosette.Tuner/Genetics/Game/ArchivedGame.cs +++ b/Cosette.Tuner.SelfPlay/Genetics/Game/ArchivedGame.cs @@ -1,4 +1,4 @@ -namespace Cosette.Tuner.Genetics.Game +namespace Cosette.Tuner.SelfPlay.Genetics.Game { public class ArchivedGame { diff --git a/Cosette.Tuner/Genetics/Game/Color.cs b/Cosette.Tuner.SelfPlay/Genetics/Game/Color.cs similarity index 65% rename from Cosette.Tuner/Genetics/Game/Color.cs rename to Cosette.Tuner.SelfPlay/Genetics/Game/Color.cs index 843b51f..1db94b7 100644 --- a/Cosette.Tuner/Genetics/Game/Color.cs +++ b/Cosette.Tuner.SelfPlay/Genetics/Game/Color.cs @@ -1,4 +1,4 @@ -namespace Cosette.Tuner.Genetics.Game +namespace Cosette.Tuner.SelfPlay.Genetics.Game { public enum Color { diff --git a/Cosette.Tuner/Genetics/Game/EvaluationParticipant.cs b/Cosette.Tuner.SelfPlay/Genetics/Game/EvaluationParticipant.cs similarity index 92% rename from Cosette.Tuner/Genetics/Game/EvaluationParticipant.cs rename to Cosette.Tuner.SelfPlay/Genetics/Game/EvaluationParticipant.cs index 4ddfb51..6e33842 100644 --- a/Cosette.Tuner/Genetics/Game/EvaluationParticipant.cs +++ b/Cosette.Tuner.SelfPlay/Genetics/Game/EvaluationParticipant.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Linq; -using Cosette.Tuner.Engine; +using Cosette.Tuner.SelfPlay.Engine; -namespace Cosette.Tuner.Genetics.Game +namespace Cosette.Tuner.SelfPlay.Genetics.Game { public class EvaluationParticipant { diff --git a/Cosette.Tuner/Genetics/Game/GameData.cs b/Cosette.Tuner.SelfPlay/Genetics/Game/GameData.cs similarity index 95% rename from Cosette.Tuner/Genetics/Game/GameData.cs rename to Cosette.Tuner.SelfPlay/Genetics/Game/GameData.cs index 161e6f9..c69a9a6 100644 --- a/Cosette.Tuner/Genetics/Game/GameData.cs +++ b/Cosette.Tuner.SelfPlay/Genetics/Game/GameData.cs @@ -1,9 +1,8 @@ using System.Collections.Generic; -using System.Drawing; -using Cosette.Tuner.Engine; -using Cosette.Tuner.Settings; +using Cosette.Tuner.SelfPlay.Engine; +using Cosette.Tuner.SelfPlay.Settings; -namespace Cosette.Tuner.Genetics.Game +namespace Cosette.Tuner.SelfPlay.Genetics.Game { public class GameData { diff --git a/Cosette.Tuner/Genetics/Game/GameResult.cs b/Cosette.Tuner.SelfPlay/Genetics/Game/GameResult.cs similarity index 62% rename from Cosette.Tuner/Genetics/Game/GameResult.cs rename to Cosette.Tuner.SelfPlay/Genetics/Game/GameResult.cs index 0c2337c..c7aef53 100644 --- a/Cosette.Tuner/Genetics/Game/GameResult.cs +++ b/Cosette.Tuner.SelfPlay/Genetics/Game/GameResult.cs @@ -1,4 +1,4 @@ -namespace Cosette.Tuner.Genetics.Game +namespace Cosette.Tuner.SelfPlay.Genetics.Game { public enum GameResult { diff --git a/Cosette.Tuner/Program.cs b/Cosette.Tuner.SelfPlay/Program.cs similarity index 90% rename from Cosette.Tuner/Program.cs rename to Cosette.Tuner.SelfPlay/Program.cs index 5d96af7..c2b1072 100644 --- a/Cosette.Tuner/Program.cs +++ b/Cosette.Tuner.SelfPlay/Program.cs @@ -3,9 +3,10 @@ using System.Diagnostics; using System.Threading.Tasks; using Cosette.Tuner.Common.Requests; -using Cosette.Tuner.Genetics; -using Cosette.Tuner.Settings; -using Cosette.Tuner.Web; +using Cosette.Tuner.Common.Services; +using Cosette.Tuner.SelfPlay.Genetics; +using Cosette.Tuner.SelfPlay.Settings; +using Cosette.Tuner.SelfPlay.Web; using GeneticSharp.Domain; using GeneticSharp.Domain.Crossovers; using GeneticSharp.Domain.Mutations; @@ -13,7 +14,7 @@ using GeneticSharp.Domain.Selections; using GeneticSharp.Domain.Terminations; -namespace Cosette.Tuner +namespace Cosette.Tuner.SelfPlay { public class Program { @@ -30,7 +31,10 @@ public static async Task Main(string[] args) _generationStopwatch = new Stopwatch(); await _webService.EnableIfAvailable(); - _testId = await _webService.RegisterTest(); + _testId = await _webService.RegisterTest(new RegisterTestRequest + { + Type = TestType.SelfPlay + }); var selection = new EliteSelection(); var crossover = new UniformCrossover(0.5f); diff --git a/Cosette.Tuner.SelfPlay/Settings/GeneInfo.cs b/Cosette.Tuner.SelfPlay/Settings/GeneInfo.cs new file mode 100644 index 0000000..1d4e453 --- /dev/null +++ b/Cosette.Tuner.SelfPlay/Settings/GeneInfo.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace Cosette.Tuner.SelfPlay.Settings +{ + public class GeneInfo + { + public string Name { get; set; } + + [JsonProperty("min_value")] + public int MinValue { get; set; } + + [JsonProperty("max_value")] + public int MaxValue { get; set; } + } +} diff --git a/Cosette.Tuner.SelfPlay/Settings/SettingsLoader.cs b/Cosette.Tuner.SelfPlay/Settings/SettingsLoader.cs new file mode 100644 index 0000000..230a1ee --- /dev/null +++ b/Cosette.Tuner.SelfPlay/Settings/SettingsLoader.cs @@ -0,0 +1,19 @@ +using System.IO; +using Newtonsoft.Json; + +namespace Cosette.Tuner.SelfPlay.Settings +{ + public static class SettingsLoader + { + public static SettingsModel Data { get; set; } + + public static void Init(string settingsPath) + { + using (var streamReader = new StreamReader(settingsPath)) + { + var content = streamReader.ReadToEnd(); + Data = JsonConvert.DeserializeObject(content); + } + } + } +} diff --git a/Cosette.Tuner/Settings/SettingsModel.cs b/Cosette.Tuner.SelfPlay/Settings/SettingsModel.cs similarity index 96% rename from Cosette.Tuner/Settings/SettingsModel.cs rename to Cosette.Tuner.SelfPlay/Settings/SettingsModel.cs index c16022f..5e33b6b 100644 --- a/Cosette.Tuner/Settings/SettingsModel.cs +++ b/Cosette.Tuner.SelfPlay/Settings/SettingsModel.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace Cosette.Tuner.Settings +namespace Cosette.Tuner.SelfPlay.Settings { public class SettingsModel { diff --git a/Cosette.Tuner/Web/RequestsFactory.cs b/Cosette.Tuner.SelfPlay/Web/RequestsFactory.cs similarity index 73% rename from Cosette.Tuner/Web/RequestsFactory.cs rename to Cosette.Tuner.SelfPlay/Web/RequestsFactory.cs index 4b01972..3a95f06 100644 --- a/Cosette.Tuner/Web/RequestsFactory.cs +++ b/Cosette.Tuner.SelfPlay/Web/RequestsFactory.cs @@ -1,38 +1,39 @@ using System.Collections.Generic; using Cosette.Tuner.Common.Requests; -using Cosette.Tuner.Genetics.Game; -using Cosette.Tuner.Settings; +using Cosette.Tuner.SelfPlay.Genetics.Game; +using Cosette.Tuner.SelfPlay.Settings; using GeneticSharp.Domain.Chromosomes; -namespace Cosette.Tuner.Web +namespace Cosette.Tuner.SelfPlay.Web { public static class RequestsFactory { - public static ChromosomeDataRequest CreateChromosomeRequest(int testId, int fitness, double elapsedTime, IChromosome chromosome, EvaluationParticipant referenceParticipant, EvaluationParticipant experimentalParticipant) + public static ChromosomeDataRequest CreateChromosomeRequest(int testId, double fitness, double elapsedTime, IChromosome chromosome, EvaluationParticipant referenceParticipant, EvaluationParticipant experimentalParticipant) { return new ChromosomeDataRequest { TestId = testId, ElapsedTime = elapsedTime, Fitness = fitness, - ReferenceEngineWins = referenceParticipant.Wins, - ExperimentalEngineWins = experimentalParticipant.Wins, - Draws = referenceParticipant.Draws, - ReferenceEngineStatistics = new EngineStatisticsDataRequest + ReferenceEngineStatistics = new SelfPlayStatisticsDataRequest { AverageTimePerGame = elapsedTime / SettingsLoader.Data.GamesPerFitnessTest, AverageDepth = referenceParticipant.AverageDepth, AverageNodesCount = referenceParticipant.AverageNodesCount, - AverageNodesPerSecond = referenceParticipant.AverageNps + AverageNodesPerSecond = referenceParticipant.AverageNps, + Wins = referenceParticipant.Wins, + Draws = referenceParticipant.Draws }, - ExperimentalEngineStatistics = new EngineStatisticsDataRequest + ExperimentalEngineStatistics = new SelfPlayStatisticsDataRequest { AverageTimePerGame = elapsedTime / SettingsLoader.Data.GamesPerFitnessTest, AverageDepth = experimentalParticipant.AverageDepth, AverageNodesCount = experimentalParticipant.AverageNodesCount, - AverageNodesPerSecond = experimentalParticipant.AverageNps + AverageNodesPerSecond = experimentalParticipant.AverageNps, + Wins = experimentalParticipant.Wins, + Draws = experimentalParticipant.Draws }, Genes = CreateGenesRequest(chromosome) @@ -44,7 +45,7 @@ public static GenerationDataRequest CreateGenerationRequest(int testId, double e return new GenerationDataRequest { TestId = testId, - BestFitness = (int)chromosome.Fitness, + BestFitness = chromosome.Fitness.Value, ElapsedTime = elapsedTime, BestChromosomeGenes = CreateGenesRequest(chromosome) }; diff --git a/Cosette.Tuner.Texel/Cosette.Tuner.Texel.csproj b/Cosette.Tuner.Texel/Cosette.Tuner.Texel.csproj new file mode 100644 index 0000000..10b266a --- /dev/null +++ b/Cosette.Tuner.Texel/Cosette.Tuner.Texel.csproj @@ -0,0 +1,26 @@ + + + + Exe + net5.0 + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/Cosette.Tuner.Texel/Engine/EngineOperator.cs b/Cosette.Tuner.Texel/Engine/EngineOperator.cs new file mode 100644 index 0000000..f831de6 --- /dev/null +++ b/Cosette.Tuner.Texel/Engine/EngineOperator.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using Cosette.Tuner.Texel.Settings; + +namespace Cosette.Tuner.Texel.Engine +{ + public class EngineOperator + { + private string _enginePath; + private string _engineArguments; + private Process _engineProcess; + private Dictionary _options; + + public EngineOperator(string path, string arguments) + { + _enginePath = path; + _engineArguments = arguments; + _options = new Dictionary(); + } + + public void Init() + { + _engineProcess = Process.Start(new ProcessStartInfo + { + FileName = _enginePath, + Arguments = _engineArguments, + CreateNoWindow = true, + RedirectStandardInput = true, + RedirectStandardOutput = true + }); + } + + public void Restart() + { + if (!_engineProcess.HasExited) + { + _engineProcess.Close(); + } + + Init(); + ApplyOptions(); + } + + public void SetOption(string name, string value) + { + _options[name] = value; + } + + public void ApplyOptions() + { + Write("uci"); + WaitForMessage("uciok"); + + Write("isready"); + WaitForMessage("readyok"); + + foreach (var option in _options) + { + Write($"setoption name {option.Key} value {option.Value}"); + } + + Write("isready"); + + if (!WaitForMessage("readyok")) + { + throw new Exception("Invalid option passed to the engine"); + } + + Write("leave"); + } + + public void LoadEpd(string epdPath) + { + Write($"tuner load {epdPath}"); + + var response = string.Empty; + while (response.Contains("Ok")) + { + response = Read(); + } + } + + public double Evaluate(double scalingFactor) + { + Write($"tuner evaluate {scalingFactor}"); + + while (true) + { + var response = Read(); + if (response == "Ok") + { + continue; + } + + return double.Parse(response, CultureInfo.InvariantCulture); + } + } + + public void Write(string message) + { + _engineProcess.StandardInput.WriteLine(message); + } + + public string Read() + { + return _engineProcess.StandardOutput.ReadLine(); + } + + public bool WaitForMessage(string message) + { + while (true) + { + var response = Read(); + if (response.StartsWith("error")) + { + return false; + } + + if (response == message) + { + return true; + } + } + } + } +} diff --git a/Cosette.Tuner/Genetics/EvaluationChromosome.cs b/Cosette.Tuner.Texel/Genetics/Evaluation/EvaluationChromosome.cs similarity index 85% rename from Cosette.Tuner/Genetics/EvaluationChromosome.cs rename to Cosette.Tuner.Texel/Genetics/Evaluation/EvaluationChromosome.cs index 5189d19..4278e13 100644 --- a/Cosette.Tuner/Genetics/EvaluationChromosome.cs +++ b/Cosette.Tuner.Texel/Genetics/Evaluation/EvaluationChromosome.cs @@ -1,8 +1,8 @@ -using Cosette.Tuner.Settings; +using Cosette.Tuner.Texel.Settings; using GeneticSharp.Domain.Chromosomes; using GeneticSharp.Domain.Randomizations; -namespace Cosette.Tuner.Genetics +namespace Cosette.Tuner.Texel.Genetics { public class EvaluationChromosome : ChromosomeBase { @@ -14,7 +14,7 @@ public EvaluationChromosome() : base(SettingsLoader.Data.Genes.Count) public override Gene GenerateGene(int geneIndex) { var gene = SettingsLoader.Data.Genes[geneIndex]; - var value = RandomizationProvider.Current.GetInt(gene.MinValue, gene.MaxValue); + var value = RandomizationProvider.Current.GetInt(gene.MinValue, gene.MaxValue + 1); return new Gene(value); } diff --git a/Cosette.Tuner.Texel/Genetics/Evaluation/EvaluationFitness.cs b/Cosette.Tuner.Texel/Genetics/Evaluation/EvaluationFitness.cs new file mode 100644 index 0000000..96de5d5 --- /dev/null +++ b/Cosette.Tuner.Texel/Genetics/Evaluation/EvaluationFitness.cs @@ -0,0 +1,65 @@ +using System; +using System.Diagnostics; +using Cosette.Tuner.Common.Services; +using Cosette.Tuner.Texel.Engine; +using Cosette.Tuner.Texel.Settings; +using Cosette.Tuner.Texel.Web; +using GeneticSharp.Domain.Chromosomes; +using GeneticSharp.Domain.Fitnesses; + +namespace Cosette.Tuner.Texel.Genetics +{ + public class EvaluationFitness : IFitness + { + private int _testId; + private WebService _webService; + + private EngineOperator _engineOperator; + + public EvaluationFitness(int testId, WebService webService) + { + _testId = testId; + _webService = webService; + + _engineOperator = new EngineOperator(SettingsLoader.Data.EnginePath, SettingsLoader.Data.EngineArguments); + _engineOperator.Init(); + _engineOperator.LoadEpd(SettingsLoader.Data.PositionsDatabasePath); + } + + public double Evaluate(IChromosome chromosome) + { + for (var geneIndex = 0; geneIndex < SettingsLoader.Data.Genes.Count; geneIndex++) + { + var geneName = SettingsLoader.Data.Genes[geneIndex].Name; + var geneValue = chromosome.GetGene(geneIndex).ToString(); + + _engineOperator.SetOption(geneName, geneValue); + } + + while (true) + { + try + { + _engineOperator.ApplyOptions(); + break; + } + catch + { + _engineOperator.Restart(); + _engineOperator.LoadEpd(SettingsLoader.Data.PositionsDatabasePath); + } + } + + var stopwatch = Stopwatch.StartNew(); + var error = _engineOperator.Evaluate(SettingsLoader.Data.ScalingConstant); + var fitness = 1.0 - error; + var elapsedTime = (double)stopwatch.ElapsedMilliseconds / 1000; + + var chromosomeRequest = RequestsFactory.CreateChromosomeRequest(_testId, fitness, elapsedTime, chromosome); + _webService.SendChromosomeData(chromosomeRequest).GetAwaiter().GetResult(); + + Console.WriteLine($"[{DateTime.Now}] Run done! Fitness: {fitness}"); + return fitness; + } + } +} diff --git a/Cosette.Tuner.Texel/Genetics/ScalingFactor/ScalingFactorChromosome.cs b/Cosette.Tuner.Texel/Genetics/ScalingFactor/ScalingFactorChromosome.cs new file mode 100644 index 0000000..8241784 --- /dev/null +++ b/Cosette.Tuner.Texel/Genetics/ScalingFactor/ScalingFactorChromosome.cs @@ -0,0 +1,24 @@ +using Cosette.Tuner.Texel.Settings; +using GeneticSharp.Domain.Chromosomes; +using GeneticSharp.Domain.Randomizations; + +namespace Cosette.Tuner.Texel.Genetics.ScalingFactor +{ + public class ScalingFactorChromosome : ChromosomeBase + { + public ScalingFactorChromosome() : base(2) + { + CreateGenes(); + } + + public override Gene GenerateGene(int geneIndex) + { + return new Gene(RandomizationProvider.Current.GetInt(0, 2000)); + } + + public override IChromosome CreateNew() + { + return new ScalingFactorChromosome(); + } + } +} \ No newline at end of file diff --git a/Cosette.Tuner.Texel/Genetics/ScalingFactor/ScalingFactorFitness.cs b/Cosette.Tuner.Texel/Genetics/ScalingFactor/ScalingFactorFitness.cs new file mode 100644 index 0000000..a8d30e4 --- /dev/null +++ b/Cosette.Tuner.Texel/Genetics/ScalingFactor/ScalingFactorFitness.cs @@ -0,0 +1,46 @@ +using System; +using System.Diagnostics; +using Cosette.Tuner.Common.Services; +using Cosette.Tuner.Texel.Engine; +using Cosette.Tuner.Texel.Settings; +using Cosette.Tuner.Texel.Web; +using GeneticSharp.Domain.Chromosomes; +using GeneticSharp.Domain.Fitnesses; + +namespace Cosette.Tuner.Texel.Genetics +{ + public class ScalingFactorFitness : IFitness + { + private int _testId; + private WebService _webService; + + private EngineOperator _engineOperator; + + public ScalingFactorFitness(int testId, WebService webService) + { + _testId = testId; + _webService = webService; + + _engineOperator = new EngineOperator(SettingsLoader.Data.EnginePath, SettingsLoader.Data.EngineArguments); + _engineOperator.Init(); + _engineOperator.LoadEpd(SettingsLoader.Data.PositionsDatabasePath); + } + + public double Evaluate(IChromosome chromosome) + { + var stopwatch = Stopwatch.StartNew(); + + var scalingFactor = (double)(int) chromosome.GetGene(0).Value / 1000; + var error = _engineOperator.Evaluate(scalingFactor); + + var fitness = 1.0 - error; + var elapsedTime = (double)stopwatch.ElapsedMilliseconds / 1000; + + var chromosomeRequest = RequestsFactory.CreateChromosomeRequest(_testId, fitness, elapsedTime, chromosome); + _webService.SendChromosomeData(chromosomeRequest).GetAwaiter().GetResult(); + + Console.WriteLine($"[{DateTime.Now}] Run done! Fitness: {fitness}"); + return fitness; + } + } +} diff --git a/Cosette.Tuner.Texel/Program.cs b/Cosette.Tuner.Texel/Program.cs new file mode 100644 index 0000000..46c5ecb --- /dev/null +++ b/Cosette.Tuner.Texel/Program.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Cosette.Tuner.Common.Requests; +using Cosette.Tuner.Common.Services; +using Cosette.Tuner.Texel.Genetics; +using Cosette.Tuner.Texel.Genetics.ScalingFactor; +using Cosette.Tuner.Texel.Settings; +using Cosette.Tuner.Texel.Web; +using GeneticSharp.Domain; +using GeneticSharp.Domain.Chromosomes; +using GeneticSharp.Domain.Crossovers; +using GeneticSharp.Domain.Fitnesses; +using GeneticSharp.Domain.Mutations; +using GeneticSharp.Domain.Populations; +using GeneticSharp.Domain.Selections; +using GeneticSharp.Domain.Terminations; + +namespace Cosette.Tuner.Texel +{ + class Program + { + private static int _testId; + private static WebService _webService; + private static Stopwatch _generationStopwatch; + + static async Task Main(string[] args) + { + Console.WriteLine($"[{DateTime.Now}] Tuner start"); + SettingsLoader.Init("settings.json"); + + IFitness fitness = null; + IChromosome chromosome = null; + + _webService = new WebService(); + await _webService.EnableIfAvailable(); + _testId = await _webService.RegisterTest(new RegisterTestRequest + { + Type = TestType.Texel + }); + + var scalingFactorMode = args.Contains("scaling_factor"); + if (scalingFactorMode) + { + SettingsLoader.Data.Genes.Clear(); + SettingsLoader.Data.Genes.Add(new GeneInfo + { + Name = "ScalingFactor", + MinValue = 0, + MaxValue = 2000 + }); + SettingsLoader.Data.Genes.Add(new GeneInfo + { + Name = "Unused", + MinValue = 0, + MaxValue = 1 + }); + + fitness = new ScalingFactorFitness(_testId, _webService); + chromosome = new ScalingFactorChromosome(); + } + else + { + fitness = new EvaluationFitness(_testId, _webService); + chromosome = new EvaluationChromosome(); + } + + var selection = new EliteSelection(); + var crossover = new UniformCrossover(0.5f); + var mutation = new UniformMutation(true); + var population = new Population(SettingsLoader.Data.MinPopulation, SettingsLoader.Data.MaxPopulation, chromosome); + var geneticAlgorithm = new GeneticAlgorithm(population, fitness, selection, crossover, mutation); + geneticAlgorithm.Termination = new GenerationNumberTermination(SettingsLoader.Data.GenerationsCount); + geneticAlgorithm.GenerationRan += GeneticAlgorithm_GenerationRan; + + _generationStopwatch = new Stopwatch(); + _generationStopwatch.Start(); + geneticAlgorithm.Start(); + + Console.WriteLine("Best solution found has {0} fitness.", geneticAlgorithm.BestChromosome.Fitness); + Console.ReadLine(); + } + + private static void GeneticAlgorithm_GenerationRan(object sender, EventArgs e) + { + var geneticAlgorithm = (GeneticAlgorithm)sender; + var genesList = new List(); + + for (var geneIndex = 0; geneIndex < SettingsLoader.Data.Genes.Count; geneIndex++) + { + var name = SettingsLoader.Data.Genes[geneIndex].Name; + var value = geneticAlgorithm.BestChromosome.GetGene(geneIndex).ToString(); + + genesList.Add($"{name}={value}"); + } + + var generationDataRequest = RequestsFactory.CreateGenerationRequest(_testId, _generationStopwatch.Elapsed.TotalSeconds, geneticAlgorithm.BestChromosome); + _webService.SendGenerationData(generationDataRequest).GetAwaiter().GetResult(); + + Console.WriteLine("======================================"); + Console.WriteLine($"[{DateTime.Now}] Generation done!"); + Console.WriteLine($" - best chromosome: {string.Join(", ", genesList)}"); + Console.WriteLine($" - best fitness: {geneticAlgorithm.BestChromosome.Fitness}"); + Console.WriteLine("======================================"); + + _generationStopwatch.Restart(); + } + } +} diff --git a/Cosette.Tuner/Settings/GeneInfo.cs b/Cosette.Tuner.Texel/Settings/GeneInfo.cs similarity index 87% rename from Cosette.Tuner/Settings/GeneInfo.cs rename to Cosette.Tuner.Texel/Settings/GeneInfo.cs index 57b4713..7aae649 100644 --- a/Cosette.Tuner/Settings/GeneInfo.cs +++ b/Cosette.Tuner.Texel/Settings/GeneInfo.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace Cosette.Tuner.Settings +namespace Cosette.Tuner.Texel.Settings { public class GeneInfo { diff --git a/Cosette.Tuner/Settings/SettingsLoader.cs b/Cosette.Tuner.Texel/Settings/SettingsLoader.cs similarity index 92% rename from Cosette.Tuner/Settings/SettingsLoader.cs rename to Cosette.Tuner.Texel/Settings/SettingsLoader.cs index a987562..562cf8a 100644 --- a/Cosette.Tuner/Settings/SettingsLoader.cs +++ b/Cosette.Tuner.Texel/Settings/SettingsLoader.cs @@ -1,7 +1,7 @@ using System.IO; using Newtonsoft.Json; -namespace Cosette.Tuner.Settings +namespace Cosette.Tuner.Texel.Settings { public static class SettingsLoader { diff --git a/Cosette.Tuner.Texel/Settings/SettingsModel.cs b/Cosette.Tuner.Texel/Settings/SettingsModel.cs new file mode 100644 index 0000000..58c7975 --- /dev/null +++ b/Cosette.Tuner.Texel/Settings/SettingsModel.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Cosette.Tuner.Texel.Settings +{ + public class SettingsModel + { + public List Genes { get; set; } + + [JsonProperty("engine_path")] + public string EnginePath { get; set; } + + [JsonProperty("engine_arguments")] + public string EngineArguments { get; set; } + + [JsonProperty("positions_database_path")] + public string PositionsDatabasePath { get; set; } + + [JsonProperty("min_population")] + public int MinPopulation { get; set; } + + [JsonProperty("max_population")] + public int MaxPopulation { get; set; } + + [JsonProperty("generations_count")] + public int GenerationsCount { get; set; } + + [JsonProperty("scaling_constant")] + public double ScalingConstant { get; set; } + } +} diff --git a/Cosette.Tuner.Texel/Web/RequestsFactory.cs b/Cosette.Tuner.Texel/Web/RequestsFactory.cs new file mode 100644 index 0000000..4c0d727 --- /dev/null +++ b/Cosette.Tuner.Texel/Web/RequestsFactory.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using Cosette.Tuner.Common.Requests; +using Cosette.Tuner.Texel.Settings; +using GeneticSharp.Domain.Chromosomes; + +namespace Cosette.Tuner.Texel.Web +{ + public static class RequestsFactory + { + public static ChromosomeDataRequest CreateChromosomeRequest(int testId, double fitness, double elapsedTime, IChromosome chromosome) + { + return new ChromosomeDataRequest + { + TestId = testId, + ElapsedTime = elapsedTime, + Fitness = fitness, + Genes = CreateGenesRequest(chromosome) + }; + } + + public static GenerationDataRequest CreateGenerationRequest(int testId, double elapsedTime, IChromosome chromosome) + { + return new GenerationDataRequest + { + TestId = testId, + BestFitness = chromosome.Fitness.Value, + ElapsedTime = elapsedTime, + BestChromosomeGenes = CreateGenesRequest(chromosome) + }; + } + + public static List CreateGenesRequest(IChromosome chromosome) + { + var genes = new List(); + for (var geneIndex = 0; geneIndex < chromosome.Length; geneIndex++) + { + genes.Add(new GeneDataRequest + { + Name = SettingsLoader.Data.Genes[geneIndex].Name, + Value = (int)chromosome.GetGene(geneIndex).Value + }); + } + + return genes; + } + } +} diff --git a/Cosette.Tuner.Web/Controllers/ApiController.cs b/Cosette.Tuner.Web/Controllers/ApiController.cs index a50d80b..336185e 100644 --- a/Cosette.Tuner.Web/Controllers/ApiController.cs +++ b/Cosette.Tuner.Web/Controllers/ApiController.cs @@ -35,11 +35,11 @@ public async Task Ping() [HttpPost] [Route("api/test/register")] - public async Task RegisterTest() + public async Task RegisterTest([FromBody] RegisterTestRequest requestData) { var response = new TestDataResponse { - Id = await _testService.GenerateNewTest() + Id = await _testService.GenerateNewTest(requestData.Type) }; return new JsonResult(response); diff --git a/Cosette.Tuner.Web/Controllers/HomeController.cs b/Cosette.Tuner.Web/Controllers/HomeController.cs index a3af084..0704a6b 100644 --- a/Cosette.Tuner.Web/Controllers/HomeController.cs +++ b/Cosette.Tuner.Web/Controllers/HomeController.cs @@ -36,9 +36,9 @@ public async Task Index(int? id) var test = id.HasValue ? await _testService.GetTestById(id.Value) : await _testService.GetLastTest(); var allTests = await _testService.GetAll(); var allGenerations = await _generationService.GetAll(test.Id); - var bestGenerations = await _generationService.GetBest(test.Id, 15); + var bestGenerations = await _generationService.GetBest(test.Id, 25); var allChromosomes = await _chromosomeService.GetAll(test.Id); - var bestChromosomes = await _chromosomeService.GetBest(test.Id, 15); + var bestChromosomes = await _chromosomeService.GetBest(test.Id, 25); var generationFitnessData = _chartJsService.GenerateGenerationFitnessData(allGenerations); var chromosomeFitnessData = _chartJsService.GenerateChromosomeFitnessData(allChromosomes); @@ -49,7 +49,7 @@ public async Task Index(int? id) return View(new MainViewModel { - LastTest = _mapper.Map(test), + CurrentTest = _mapper.Map(test), Tests = _mapper.Map>(allTests), AllGenerations = _mapper.Map>(allGenerations), BestGenerations = _mapper.Map>(bestGenerations), diff --git a/Cosette.Tuner.Web/Database/DatabaseContext.cs b/Cosette.Tuner.Web/Database/DatabaseContext.cs index 1e6cfc4..1261a66 100644 --- a/Cosette.Tuner.Web/Database/DatabaseContext.cs +++ b/Cosette.Tuner.Web/Database/DatabaseContext.cs @@ -9,7 +9,7 @@ public class DatabaseContext : DbContext { public virtual DbSet ChromosomeGenes { get; set; } public virtual DbSet Chromosomes { get; set; } - public virtual DbSet EngineStatistics { get; set; } + public virtual DbSet SelfPlayStatistics { get; set; } public virtual DbSet GenerationGenes { get; set; } public virtual DbSet Generations { get; set; } public virtual DbSet Tests { get; set; } diff --git a/Cosette.Tuner.Web/Database/Models/ChromosomeModel.cs b/Cosette.Tuner.Web/Database/Models/ChromosomeModel.cs index 713c855..2a407e3 100644 --- a/Cosette.Tuner.Web/Database/Models/ChromosomeModel.cs +++ b/Cosette.Tuner.Web/Database/Models/ChromosomeModel.cs @@ -12,12 +12,9 @@ public class ChromosomeModel public DateTime CreationTimeUtc { get; set; } public double ElapsedTime { get; set; } - public int Fitness { get; set; } - public int ReferenceEngineWins { get; set; } - public int ExperimentalEngineWins { get; set; } - public int Draws { get; set; } + public double Fitness { get; set; } - public virtual List EnginesStatistics { get; set; } + public virtual List SelfPlayStatistics { get; set; } public virtual List Genes { get; set; } } } diff --git a/Cosette.Tuner.Web/Database/Models/GenerationModel.cs b/Cosette.Tuner.Web/Database/Models/GenerationModel.cs index 12e5569..f8b06cd 100644 --- a/Cosette.Tuner.Web/Database/Models/GenerationModel.cs +++ b/Cosette.Tuner.Web/Database/Models/GenerationModel.cs @@ -12,7 +12,7 @@ public class GenerationModel public DateTime CreationTimeUtc { get; set; } public double ElapsedTime { get; set; } - public int BestFitness { get; set; } + public double BestFitness { get; set; } public virtual List BestGenes { get; set; } } diff --git a/Cosette.Tuner.Web/Database/Models/EngineStatisticsModel.cs b/Cosette.Tuner.Web/Database/Models/SelfPlayStatisticsModel.cs similarity index 82% rename from Cosette.Tuner.Web/Database/Models/EngineStatisticsModel.cs rename to Cosette.Tuner.Web/Database/Models/SelfPlayStatisticsModel.cs index 23fe67e..1accc62 100644 --- a/Cosette.Tuner.Web/Database/Models/EngineStatisticsModel.cs +++ b/Cosette.Tuner.Web/Database/Models/SelfPlayStatisticsModel.cs @@ -2,7 +2,7 @@ namespace Cosette.Tuner.Web.Database.Models { - public class EngineStatisticsModel + public class SelfPlayStatisticsModel { public int Id { get; set; } @@ -15,5 +15,7 @@ public class EngineStatisticsModel public double AverageDepth { get; set; } public double AverageNodesCount { get; set; } public double AverageNodesPerSecond { get; set; } + public int Wins { get; set; } + public int Draws { get; set; } } } diff --git a/Cosette.Tuner.Web/Database/Models/TestModel.cs b/Cosette.Tuner.Web/Database/Models/TestModel.cs index 1858a06..94c836a 100644 --- a/Cosette.Tuner.Web/Database/Models/TestModel.cs +++ b/Cosette.Tuner.Web/Database/Models/TestModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Cosette.Tuner.Common.Requests; namespace Cosette.Tuner.Web.Database.Models { @@ -7,6 +8,7 @@ public class TestModel { public int Id { get; set; } public DateTime CreationTimeUtc { get; set; } + public TestType Type { get; set; } public virtual List Generation { get; set; } } diff --git a/Cosette.Tuner.Web/MapperProfile.cs b/Cosette.Tuner.Web/MapperProfile.cs index 098b411..18c32b9 100644 --- a/Cosette.Tuner.Web/MapperProfile.cs +++ b/Cosette.Tuner.Web/MapperProfile.cs @@ -18,18 +18,20 @@ public MapperProfile() .ForMember(p => p.CreationTimeUtc, p => p.MapFrom(q => DateTime.UtcNow)) .ForMember(p => p.ElapsedTime, p => p.MapFrom(q => q.ElapsedTime)) .ForMember(p => p.Fitness, p => p.MapFrom(q => q.Fitness)) - .ForMember(p => p.ReferenceEngineWins, p => p.MapFrom(q => q.ReferenceEngineWins)) - .ForMember(p => p.ExperimentalEngineWins, p => p.MapFrom(q => q.ExperimentalEngineWins)) - .ForMember(p => p.Draws, p => p.MapFrom(q => q.Draws)) - .ForMember(p => p.EnginesStatistics, p => p.MapFrom((src, dest, order, context) => + .ForMember(p => p.SelfPlayStatistics, p => p.MapFrom((src, dest, order, context) => { - var referenceEngineStatistics = context.Mapper.Map(src.ReferenceEngineStatistics); - var experimentalEngineStatistics = context.Mapper.Map(src.ExperimentalEngineStatistics); + if (src.ReferenceEngineStatistics == null && src.ExperimentalEngineStatistics == null) + { + return new List(); + } + + var referenceEngineStatistics = context.Mapper.Map(src.ReferenceEngineStatistics); + var experimentalEngineStatistics = context.Mapper.Map(src.ExperimentalEngineStatistics); referenceEngineStatistics.IsReferenceEngine = true; experimentalEngineStatistics.IsReferenceEngine = false; - var outputList = new List + var outputList = new List { referenceEngineStatistics, experimentalEngineStatistics @@ -47,12 +49,14 @@ public MapperProfile() .ForMember(p => p.Name, p => p.MapFrom(q => q.Name)) .ForMember(p => p.Value, p => p.MapFrom(q => q.Value)); - CreateMap() + CreateMap() .ForMember(p => p.CreationTimeUtc, p => p.MapFrom(q => DateTime.UtcNow)) .ForMember(p => p.AverageTimePerGame, p => p.MapFrom(q => q.AverageTimePerGame)) .ForMember(p => p.AverageDepth, p => p.MapFrom(q => q.AverageDepth)) .ForMember(p => p.AverageNodesCount, p => p.MapFrom(q => q.AverageNodesCount)) - .ForMember(p => p.AverageNodesPerSecond, p => p.MapFrom(q => q.AverageNodesPerSecond)); + .ForMember(p => p.AverageNodesPerSecond, p => p.MapFrom(q => q.AverageNodesPerSecond)) + .ForMember(p => p.Wins, p => p.MapFrom(q => q.Wins)) + .ForMember(p => p.Draws, p => p.MapFrom(q => q.Draws)); CreateMap() .ForMember(p => p.TestId, p => p.MapFrom(q => q.TestId)) @@ -64,7 +68,8 @@ public MapperProfile() CreateMap() .ForMember(p => p.Id, p => p.MapFrom(q => q.Id)) - .ForMember(p => p.CreationTimeUtc, p => p.MapFrom(q => q.CreationTimeUtc)); + .ForMember(p => p.CreationTimeUtc, p => p.MapFrom(q => q.CreationTimeUtc)) + .ForMember(p => p.Type, p => p.MapFrom(q => q.Type)); CreateMap() .ForMember(p => p.Id, p => p.MapFrom(q => q.Id)) @@ -88,20 +93,20 @@ public MapperProfile() .ForMember(p => p.CreationTimeUtc, p => p.MapFrom(q => q.CreationTimeUtc)) .ForMember(p => p.ElapsedTime, p => p.MapFrom(q => q.ElapsedTime)) .ForMember(p => p.Fitness, p => p.MapFrom(q => q.Fitness)) - .ForMember(p => p.ReferenceEngineWins, p => p.MapFrom(q => q.ReferenceEngineWins)) - .ForMember(p => p.ExperimentalEngineWins, p => p.MapFrom(q => q.ExperimentalEngineWins)) - .ForMember(p => p.Draws, p => p.MapFrom(q => q.Draws)) - .ForMember(p => p.EnginesStatistics, p => p.MapFrom(q => q.EnginesStatistics)) + .ForMember(p => p.ReferenceEngineStatistics, p => p.MapFrom(q => q.SelfPlayStatistics.FirstOrDefault(p => p.IsReferenceEngine))) + .ForMember(p => p.ExperimentalEngineStatistics, p => p.MapFrom(q => q.SelfPlayStatistics.FirstOrDefault(p => !p.IsReferenceEngine))) .ForMember(p => p.Genes, p => p.MapFrom(q => q.Genes)); - CreateMap() + CreateMap() .ForMember(p => p.Id, p => p.MapFrom(q => q.Id)) .ForMember(p => p.CreationTimeUtc, p => p.MapFrom(q => q.CreationTimeUtc)) .ForMember(p => p.IsReferenceEngine, p => p.MapFrom(q => q.IsReferenceEngine)) .ForMember(p => p.AverageTimePerGame, p => p.MapFrom(q => q.AverageTimePerGame)) .ForMember(p => p.AverageDepth, p => p.MapFrom(q => q.AverageDepth)) .ForMember(p => p.AverageNodesCount, p => p.MapFrom(q => q.AverageNodesCount)) - .ForMember(p => p.AverageNodesPerSecond, p => p.MapFrom(q => q.AverageNodesPerSecond)); + .ForMember(p => p.AverageNodesPerSecond, p => p.MapFrom(q => q.AverageNodesPerSecond)) + .ForMember(p => p.Wins, p => p.MapFrom(q => q.Wins)) + .ForMember(p => p.Draws, p => p.MapFrom(q => q.Draws)); } } } diff --git a/Cosette.Tuner.Web/Services/ChartJsService.cs b/Cosette.Tuner.Web/Services/ChartJsService.cs index ae9ad19..4b399ba 100644 --- a/Cosette.Tuner.Web/Services/ChartJsService.cs +++ b/Cosette.Tuner.Web/Services/ChartJsService.cs @@ -12,11 +12,11 @@ public class ChartJsService private const string ReferenceColor = "#4dc9f6"; private const string ExperimentalColor = "#ff0000"; - public ChartJsData GenerateGenerationFitnessData(List generations) + public ChartJsData GenerateGenerationFitnessData(List generations) { var labels = Enumerable.Range(0, generations.Count).Select(p => p.ToString()).ToList(); var values = generations.Select(p => p.BestFitness).ToList(); - var datasets = new List> + var datasets = new List> { GenerateDataset("Fitness", values, ReferenceColor) }; @@ -24,11 +24,11 @@ public ChartJsData GenerateGenerationFitnessData(List gene return GenerateData(labels, datasets); } - public ChartJsData GenerateChromosomeFitnessData(List chromosomes) + public ChartJsData GenerateChromosomeFitnessData(List chromosomes) { var labels = Enumerable.Range(0, chromosomes.Count).Select(p => p.ToString()).ToList(); var values = chromosomes.Select(p => p.Fitness).ToList(); - var datasets = new List> + var datasets = new List> { GenerateDataset("Fitness", values, ReferenceColor) }; @@ -52,12 +52,12 @@ public ChartJsData GenerateAverageDepthData(List chromo { var labels = Enumerable.Range(0, chromosomes.Count).Select(p => p.ToString()).ToList(); var referenceValues = chromosomes - .SelectMany(p => p.EnginesStatistics) + .SelectMany(p => p.SelfPlayStatistics) .Where(p => p.IsReferenceEngine) .Select(p => p.AverageDepth) .ToList(); var experimentalValues = chromosomes - .SelectMany(p => p.EnginesStatistics) + .SelectMany(p => p.SelfPlayStatistics) .Where(p => !p.IsReferenceEngine) .Select(p => p.AverageDepth) .ToList(); @@ -75,12 +75,12 @@ public ChartJsData GenerateAverageNodesData(List chromo { var labels = Enumerable.Range(0, chromosomes.Count).Select(p => p.ToString()).ToList(); var referenceValues = chromosomes - .SelectMany(p => p.EnginesStatistics) + .SelectMany(p => p.SelfPlayStatistics) .Where(p => p.IsReferenceEngine) .Select(p => p.AverageNodesCount) .ToList(); var experimentalValues = chromosomes - .SelectMany(p => p.EnginesStatistics) + .SelectMany(p => p.SelfPlayStatistics) .Where(p => !p.IsReferenceEngine) .Select(p => p.AverageNodesCount) .ToList(); @@ -98,7 +98,7 @@ public ChartJsData GenerateAverageTimePerGameData(List { var labels = Enumerable.Range(0, chromosomes.Count).Select(p => p.ToString()).ToList(); var values = chromosomes - .SelectMany(p => p.EnginesStatistics) + .SelectMany(p => p.SelfPlayStatistics) .Where(p => p.IsReferenceEngine) .Select(p => p.AverageTimePerGame) .ToList(); diff --git a/Cosette.Tuner.Web/Services/ChromosomeService.cs b/Cosette.Tuner.Web/Services/ChromosomeService.cs index 1637fe6..17f5f75 100644 --- a/Cosette.Tuner.Web/Services/ChromosomeService.cs +++ b/Cosette.Tuner.Web/Services/ChromosomeService.cs @@ -25,9 +25,12 @@ public async Task Add(ChromosomeModel chromosomeModel) public async Task> GetAll(int testId) { + var count = _databaseContext.Chromosomes.Count(p => p.TestId == testId); + var nth = Math.Max(1, count / 500); + return await _databaseContext.Chromosomes - .Where(p => p.TestId == testId) - .Include(p => p.EnginesStatistics) + .Where(p => p.TestId == testId && p.Id % nth == 0) + .Include(p => p.SelfPlayStatistics) .Include(p => p.Genes) .ToListAsync(); } diff --git a/Cosette.Tuner.Web/Services/GenerationService.cs b/Cosette.Tuner.Web/Services/GenerationService.cs index bc7a59c..2099c6f 100644 --- a/Cosette.Tuner.Web/Services/GenerationService.cs +++ b/Cosette.Tuner.Web/Services/GenerationService.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Cosette.Tuner.Web.Database; @@ -24,8 +25,11 @@ public async Task Add(GenerationModel generationModel) public async Task> GetAll(int testId) { + var count = _databaseContext.Generations.Count(p => p.TestId == testId); + var nth = Math.Max(1, count / 500); + return await _databaseContext.Generations - .Where(p => p.TestId == testId) + .Where(p => p.TestId == testId && p.Id % nth == 0) .Include(p => p.BestGenes) .ToListAsync(); } diff --git a/Cosette.Tuner.Web/Services/TestService.cs b/Cosette.Tuner.Web/Services/TestService.cs index e7b8b8b..0aa7e9f 100644 --- a/Cosette.Tuner.Web/Services/TestService.cs +++ b/Cosette.Tuner.Web/Services/TestService.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Cosette.Tuner.Common.Requests; using Cosette.Tuner.Web.Database; using Cosette.Tuner.Web.Database.Models; using Microsoft.EntityFrameworkCore; @@ -18,11 +19,12 @@ public TestService(DatabaseContext databaseContext) _databaseContext = databaseContext; } - public async Task GenerateNewTest() + public async Task GenerateNewTest(TestType type) { var entityTracking = await _databaseContext.Tests.AddAsync(new TestModel { CreationTimeUtc = DateTime.Now, + Type = type }); await _databaseContext.SaveChangesAsync(); diff --git a/Cosette.Tuner.Web/ViewModels/ChromosomeViewModel.cs b/Cosette.Tuner.Web/ViewModels/ChromosomeViewModel.cs index 06a8ffa..89f15ba 100644 --- a/Cosette.Tuner.Web/ViewModels/ChromosomeViewModel.cs +++ b/Cosette.Tuner.Web/ViewModels/ChromosomeViewModel.cs @@ -11,12 +11,11 @@ public class ChromosomeViewModel public DateTime CreationTimeUtc { get; set; } public double ElapsedTime { get; set; } - public int Fitness { get; set; } - public int ReferenceEngineWins { get; set; } - public int ExperimentalEngineWins { get; set; } - public int Draws { get; set; } + public double Fitness { get; set; } - public virtual List EnginesStatistics { get; set; } - public virtual List Genes { get; set; } + public SelfPlayStatisticsViewModel ReferenceEngineStatistics { get; set; } + public SelfPlayStatisticsViewModel ExperimentalEngineStatistics { get; set; } + + public List Genes { get; set; } } } diff --git a/Cosette.Tuner.Web/ViewModels/GenerationViewModel.cs b/Cosette.Tuner.Web/ViewModels/GenerationViewModel.cs index 351d98e..bbcd413 100644 --- a/Cosette.Tuner.Web/ViewModels/GenerationViewModel.cs +++ b/Cosette.Tuner.Web/ViewModels/GenerationViewModel.cs @@ -11,8 +11,8 @@ public class GenerationViewModel public DateTime CreationTimeUtc { get; set; } public double ElapsedTime { get; set; } - public int BestFitness { get; set; } + public double BestFitness { get; set; } - public virtual List BestGenes { get; set; } + public List BestGenes { get; set; } } } diff --git a/Cosette.Tuner.Web/ViewModels/MainViewModel.cs b/Cosette.Tuner.Web/ViewModels/MainViewModel.cs index f5bfc6a..e62dd13 100644 --- a/Cosette.Tuner.Web/ViewModels/MainViewModel.cs +++ b/Cosette.Tuner.Web/ViewModels/MainViewModel.cs @@ -4,7 +4,7 @@ namespace Cosette.Tuner.Web.ViewModels { public class MainViewModel { - public TestViewModel LastTest { get; set; } + public TestViewModel CurrentTest { get; set; } public List Tests { get; set; } public List AllGenerations { get; set; } public List BestGenerations { get; set; } diff --git a/Cosette.Tuner.Web/ViewModels/EngineStatisticsViewModel.cs b/Cosette.Tuner.Web/ViewModels/SelfPlayStatisticsViewModel.cs similarity index 80% rename from Cosette.Tuner.Web/ViewModels/EngineStatisticsViewModel.cs rename to Cosette.Tuner.Web/ViewModels/SelfPlayStatisticsViewModel.cs index 0a7081f..63b6035 100644 --- a/Cosette.Tuner.Web/ViewModels/EngineStatisticsViewModel.cs +++ b/Cosette.Tuner.Web/ViewModels/SelfPlayStatisticsViewModel.cs @@ -5,7 +5,7 @@ namespace Cosette.Tuner.Web.ViewModels { - public class EngineStatisticsViewModel + public class SelfPlayStatisticsViewModel { public int Id { get; set; } @@ -15,5 +15,7 @@ public class EngineStatisticsViewModel public double AverageDepth { get; set; } public double AverageNodesCount { get; set; } public double AverageNodesPerSecond { get; set; } + public int Wins { get; set; } + public int Draws { get; set; } } } diff --git a/Cosette.Tuner.Web/ViewModels/TestViewModel.cs b/Cosette.Tuner.Web/ViewModels/TestViewModel.cs index 09a934e..146ba9b 100644 --- a/Cosette.Tuner.Web/ViewModels/TestViewModel.cs +++ b/Cosette.Tuner.Web/ViewModels/TestViewModel.cs @@ -1,4 +1,5 @@ using System; +using Cosette.Tuner.Common.Requests; namespace Cosette.Tuner.Web.ViewModels { @@ -6,5 +7,6 @@ public class TestViewModel { public int Id { get; set; } public DateTime CreationTimeUtc { get; set; } + public TestType Type { get; set; } } } diff --git a/Cosette.Tuner.Web/Views/Home/Index.cshtml b/Cosette.Tuner.Web/Views/Home/Index.cshtml index 85d0637..64edeb5 100644 --- a/Cosette.Tuner.Web/Views/Home/Index.cshtml +++ b/Cosette.Tuner.Web/Views/Home/Index.cshtml @@ -1,4 +1,5 @@ -@model Cosette.Tuner.Web.ViewModels.MainViewModel +@using Cosette.Tuner.Common.Requests +@model Cosette.Tuner.Web.ViewModels.MainViewModel @{ Layout = "~/Views/Shared/_Layout.cshtml"; } @@ -20,7 +21,7 @@ @@ -42,23 +43,26 @@ -
-
-
- + @if (Model.CurrentTest.Type == TestType.SelfPlay) + { +
+
+
+ +
-
-
-
- +
+
+ +
-
-
-
- +
+
+ +
-
+ }