diff --git a/OpenBench/Test/Makefile b/OpenBench/Test/Makefile new file mode 100644 index 0000000..f09e073 --- /dev/null +++ b/OpenBench/Test/Makefile @@ -0,0 +1,40 @@ +.DEFAULT_GOAL := publish + +ifndef EXE + EXE = Sapling +endif + +# Set a default output directory if not already defined +OUTPUT_DIR ?= ./ + +# Detect OS and Architecture +UNAME_S := $(shell uname -s) +UNAME_P := $(shell uname -p) + +ifeq ($(OS),Windows_NT) + RUNTIME=win-x64 + SHELL := cmd.exe + MKDIR_CMD := if not exist "$(subst /,\,$(OUTPUT_DIR))" mkdir "$(subst /,\,$(OUTPUT_DIR))" +else + # Default runtime for Linux/MacOS + ifeq ($(UNAME_S),Linux) + RUNTIME=linux-x64 + ifneq ($(filter aarch64% armv8% arm%,$(UNAME_P)),) + RUNTIME=linux-arm64 + endif + else ifeq ($(UNAME_S),Darwin) + RUNTIME=osx-x64 + ifneq ($(filter arm%,$(UNAME_P)),) + RUNTIME=osx-arm64 + endif + endif + SHELL := /bin/sh + MKDIR_CMD := mkdir -p $(OUTPUT_DIR) +endif + +# Publish target +publish: + $(MKDIR_CMD) + dotnet publish ../../src/Sapling/Sapling.csproj -c Release --runtime $(RUNTIME) --self-contained \ + -p:PublishSingleFile=true -p:DeterministicBuild=true \ + -o $(OUTPUT_DIR) -p:ExecutableName=$(EXE) \ No newline at end of file diff --git a/Makefile b/OpenBench/Tune/Makefile similarity index 89% rename from Makefile rename to OpenBench/Tune/Makefile index 04456b2..879c73a 100644 --- a/Makefile +++ b/OpenBench/Tune/Makefile @@ -35,6 +35,6 @@ endif # Publish target publish: $(MKDIR_CMD) - dotnet publish src/Sapling/Sapling.csproj -c Release --runtime $(RUNTIME) --self-contained \ + dotnet publish ../../src/Sapling/Sapling.csproj -c Release --runtime $(RUNTIME) --self-contained \ -p:PublishSingleFile=true -p:DeterministicBuild=true \ -o $(OUTPUT_DIR) -p:ExecutableName=$(EXE) -p:DefineConstants="OpenBench" \ No newline at end of file diff --git a/src/Sapling.Engine/MathHelpers.cs b/src/Sapling.Engine/MathHelpers.cs index 23db2b2..0222261 100644 --- a/src/Sapling.Engine/MathHelpers.cs +++ b/src/Sapling.Engine/MathHelpers.cs @@ -14,15 +14,4 @@ public static unsafe class MemoryHelpers return (T*)block; } } - public static class MathHelpers - { - public static float[] LogLookup = new float[230]; - static MathHelpers() - { - for (var i = 0; i < LogLookup.Length; i++) - { - LogLookup[i] = (float)Math.Log(i); - } - } - } } diff --git a/src/Sapling.Engine/Search/NegaMaxSearch.cs b/src/Sapling.Engine/Search/NegaMaxSearch.cs index 463b54c..bcad0ed 100644 --- a/src/Sapling.Engine/Search/NegaMaxSearch.cs +++ b/src/Sapling.Engine/Search/NegaMaxSearch.cs @@ -1,4 +1,4 @@ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using System.Runtime.Intrinsics.X86; using Sapling.Engine.MoveGen; using Sapling.Engine.Transpositions; @@ -124,7 +124,7 @@ public unsafe int (transpositionType != TranspositionTableFlag.Alpha || transpositionEvaluation >= beta) && depth > SpsaOptions.NullMovePruningDepth && currentBoardState->HasMajorPieces()) { - var reduction = (int)Math.Max(0, (depth - SpsaOptions.NullMovePruningReductionA) / SpsaOptions.NullMovePruningReductionB + SpsaOptions.NullMovePruningReductionC); + var reduction = *(SpsaOptions.NullMovePruningReductionTable + depth); Unsafe.CopyBlock(newBoardState, currentBoardState, BoardStateData.BoardStateSize); @@ -378,7 +378,7 @@ public unsafe int } } - var logDepth = MathHelpers.LogLookup[depth]; + var lmpDepth = depth * depth + SpsaOptions.LateMovePruningConstant; var searchedMoves = 0; uint bestMove = default; @@ -441,7 +441,7 @@ public unsafe int if (canPrune && !isInteresting && - searchedMoves > depth * depth + SpsaOptions.LateMovePruningConstant) + searchedMoves > lmpDepth) { // Late move pruning continue; @@ -468,9 +468,9 @@ public unsafe int if (depth >= SpsaOptions.LateMoveReductionMinDepth && searchedMoves >= SpsaOptions.LateMoveReductionMinMoves) { // LMR: Move ordering should ensure a better move has already been found by now so do a shallow search - var reduction = (int)(isInteresting - ? SpsaOptions.LateMoveReductionInterestingA + logDepth * MathHelpers.LogLookup[searchedMoves] / SpsaOptions.LateMoveReductionInterestingB - : SpsaOptions.LateMoveReductionA + logDepth * MathHelpers.LogLookup[searchedMoves] / SpsaOptions.LateMoveReductionB); + var reduction = isInteresting + ? *(SpsaOptions.LateMovePruningInterestingReductionTable + depth * 218 + searchedMoves) + : *(SpsaOptions.LateMovePruningReductionTable + depth * 218 + searchedMoves); if (reduction > 0) { diff --git a/src/Sapling.Engine/Tuning/SpsaOptions.cs b/src/Sapling.Engine/Tuning/SpsaOptions.cs index 21003ce..4fa6355 100644 --- a/src/Sapling.Engine/Tuning/SpsaOptions.cs +++ b/src/Sapling.Engine/Tuning/SpsaOptions.cs @@ -1,4 +1,4 @@ -using Sapling.Engine.Search; +using Sapling.Engine.Search; using System.Reflection; namespace Sapling.Engine.Tuning @@ -41,43 +41,43 @@ public SpsaMaxValueAttribute(string maxValue) } - public static class SpsaOptions + public static unsafe class SpsaOptions { #if OpenBench [SpsaMinValue("60"), SpsaMaxValue("80")] - public static int ReverseFutilityPruningMargin = 67; + public static int ReverseFutilityPruningMargin = 68; - [SpsaMinValue("0"), SpsaMaxValue("10"), SpsaIgnore()] + [SpsaMinValue("0"), SpsaMaxValue("10")] public static int ReverseFutilityPruningDepth = 7; - [SpsaMinValue("0"), SpsaMaxValue("10"), SpsaIgnore()] + [SpsaMinValue("0"), SpsaMaxValue("10")] public static int NullMovePruningDepth = 2; - [SpsaMinValue("0"), SpsaMaxValue("10"), SpsaIgnore()] - public static float NullMovePruningReductionA = 2; + [SpsaMinValue("0"), SpsaMaxValue("10")] + public static float NullMovePruningReductionA = 3; - [SpsaMinValue("0"), SpsaMaxValue("10"), SpsaIgnore()] - public static float NullMovePruningReductionB = 7; + [SpsaMinValue("0"), SpsaMaxValue("10")] + public static float NullMovePruningReductionB = 4; - [SpsaMinValue("0"), SpsaMaxValue("10"), SpsaIgnore()] - public static float NullMovePruningReductionC = 5; + [SpsaMinValue("0"), SpsaMaxValue("10")] + public static float NullMovePruningReductionC = 3; [SpsaMinValue("50"), SpsaMaxValue("100")] public static int RazorMarginA = 57; [SpsaMinValue("250"), SpsaMaxValue("400")] - public static int RazorMarginB = 366; + public static int RazorMarginB = 370; - [SpsaMinValue("0"), SpsaMaxValue("10"), SpsaIgnore()] + [SpsaMinValue("0"), SpsaMaxValue("10")] public static int InternalIterativeDeepeningDepth = 2; - [SpsaMinValue("0"), SpsaMaxValue("10"), SpsaIgnore()] + [SpsaMinValue("0"), SpsaMaxValue("10")] public static int LateMovePruningConstant = 8; - [SpsaMinValue("0"), SpsaMaxValue("10"), SpsaIgnore()] + [SpsaMinValue("0"), SpsaMaxValue("10")] public static int LateMoveReductionMinDepth = 3; - [SpsaMinValue("0"), SpsaMaxValue("10"), SpsaIgnore()] + [SpsaMinValue("0"), SpsaMaxValue("10")] public static int LateMoveReductionMinMoves = 2; [SpsaMinValueAttribute("0.1"), SpsaMaxValue("10")] @@ -93,73 +93,73 @@ public static class SpsaOptions public static float LateMoveReductionB = 2.7831153f; [SpsaMinValue("7000"), SpsaMaxValue("10000")] - public static int HistoryHeuristicMaxHistory = 9527; + public static int HistoryHeuristicMaxHistory = 9599; [SpsaMinValue("256"), SpsaMaxValue("1000")] - public static int HistoryHeuristicBonusMax = 416; + public static int HistoryHeuristicBonusMax = 425; [SpsaMinValue("50"), SpsaMaxValue("256")] - public static int HistoryHeuristicBonusCoeff = 95; + public static int HistoryHeuristicBonusCoeff = 86; [SpsaMinValue("100000"), SpsaMaxValue("300000")] - public static int MoveOrderingBestMoveBias = 231532; + public static int MoveOrderingBestMoveBias = 240615; [SpsaMinValue("50000"), SpsaMaxValue("100000")] - public static int MoveOrderingEnPassantMoveBias = 78027; + public static int MoveOrderingEnPassantMoveBias = 77989; [SpsaMinValue("50000"), SpsaMaxValue("200000")] - public static int MoveOrderingWinningCaptureBias = 144788; + public static int MoveOrderingWinningCaptureBias = 142295; [SpsaMinValue("10000"), SpsaMaxValue("100000")] - public static int MoveOrderingLosingCaptureBias = 15878; + public static int MoveOrderingLosingCaptureBias = 15778; [SpsaMinValue("30000"), SpsaMaxValue("100000")] - public static int MoveOrderingPromoteBias = 45854; + public static int MoveOrderingPromoteBias = 45184; [SpsaMinValue("30000"), SpsaMaxValue("100000")] - public static int MoveOrderingCapturePromoteBias = 32860; + public static int MoveOrderingCapturePromoteBias = 30512; [SpsaMinValue("10000"), SpsaMaxValue("100000")] - public static int MoveOrderingKillerABias = 57006; + public static int MoveOrderingKillerABias = 61238; [SpsaMinValue("10000"), SpsaMaxValue("100000")] - public static int MoveOrderingCounterMoveBias = 85984; + public static int MoveOrderingCounterMoveBias = 87526; [SpsaMinValue("10000"), SpsaMaxValue("100000")] - public static int InterestingNegaMaxMoveScore = 39362; + public static int InterestingNegaMaxMoveScore = 40731; [SpsaMinValue("10000"), SpsaMaxValue("100000")] - public static int InterestingQuiescenceMoveScore = 44750; + public static int InterestingQuiescenceMoveScore = 40988; - [SpsaMinValue("200"), SpsaMaxValue("300"), SpsaIgnore()] + [SpsaMinValue("200"), SpsaMaxValue("300")] public static int ProbCutBetaMargin = 220; - [SpsaMinValue("0"), SpsaMaxValue("6"), SpsaIgnore()] + [SpsaMinValue("0"), SpsaMaxValue("6")] public static int ProbCutMinDepth = 3; [SpsaMinValue("30"), SpsaMaxValue("100")] - public static int AsperationWindowA = 37; + public static int AsperationWindowA = 38; [SpsaMinValue("50"), SpsaMaxValue("200")] - public static int AsperationWindowB = 57; + public static int AsperationWindowB = 60; [SpsaMinValue("100"), SpsaMaxValue("500")] - public static int AsperationWindowC = 274; + public static int AsperationWindowC = 275; [SpsaMinValue("400"), SpsaMaxValue("1500")] - public static int AsperationWindowD = 833; + public static int AsperationWindowD = 837; [SpsaMinValue("1400"), SpsaMaxValue("3000")] - public static int AsperationWindowE = 2721; + public static int AsperationWindowE = 2662; #else - public const int ReverseFutilityPruningMargin = 67; + public const int ReverseFutilityPruningMargin = 68; public const int ReverseFutilityPruningDepth = 7; public const int NullMovePruningDepth = 2; - public const float NullMovePruningReductionA = 2; - public const float NullMovePruningReductionB = 7; - public const float NullMovePruningReductionC = 5; + public const float NullMovePruningReductionA = 3; + public const float NullMovePruningReductionB = 4; + public const float NullMovePruningReductionC = 3; public const int RazorMarginA = 57; - public const int RazorMarginB = 366; + public const int RazorMarginB = 370; public const int InternalIterativeDeepeningDepth = 2; public const int LateMovePruningConstant = 8; public const int LateMoveReductionMinDepth = 3; @@ -168,27 +168,59 @@ public static class SpsaOptions public const float LateMoveReductionInterestingB = 2.4956496f; public const float LateMoveReductionA = 1.315961f; public const float LateMoveReductionB = 2.7831153f; - public const int HistoryHeuristicMaxHistory = 9527; - public const int HistoryHeuristicBonusMax = 416; - public const int HistoryHeuristicBonusCoeff = 95; - public const int MoveOrderingBestMoveBias = 231532; - public const int MoveOrderingEnPassantMoveBias = 78027; - public const int MoveOrderingWinningCaptureBias = 144788; - public const int MoveOrderingLosingCaptureBias = 15878; - public const int MoveOrderingPromoteBias = 45854; - public const int MoveOrderingCapturePromoteBias = 32860; - public const int MoveOrderingKillerABias = 57006; - public const int MoveOrderingCounterMoveBias = 85984; - public const int InterestingNegaMaxMoveScore = 39362; - public const int InterestingQuiescenceMoveScore = 44750; + public const int HistoryHeuristicMaxHistory = 9599; + public const int HistoryHeuristicBonusMax = 425; + public const int HistoryHeuristicBonusCoeff = 86; + public const int MoveOrderingBestMoveBias = 240615; + public const int MoveOrderingEnPassantMoveBias = 77989; + public const int MoveOrderingWinningCaptureBias = 142295; + public const int MoveOrderingLosingCaptureBias = 15778; + public const int MoveOrderingPromoteBias = 45184; + public const int MoveOrderingCapturePromoteBias = 30512; + public const int MoveOrderingKillerABias = 61238; + public const int MoveOrderingCounterMoveBias = 87526; + public const int InterestingNegaMaxMoveScore = 40731; + public const int InterestingQuiescenceMoveScore = 40988; public const int ProbCutBetaMargin = 220; public const int ProbCutMinDepth = 3; - public const int AsperationWindowA = 37; - public const int AsperationWindowB = 57; - public const int AsperationWindowC = 274; - public const int AsperationWindowD = 833; - public const int AsperationWindowE = 2721; + public const int AsperationWindowA = 38; + public const int AsperationWindowB = 60; + public const int AsperationWindowC = 275; + public const int AsperationWindowD = 837; + public const int AsperationWindowE = 2662; #endif + + public static int* LateMovePruningInterestingReductionTable; + public static int* LateMovePruningReductionTable; + public static int* NullMovePruningReductionTable; + static SpsaOptions() + { + NullMovePruningReductionTable = MemoryHelpers.Allocate(Constants.MaxSearchDepth); + LateMovePruningReductionTable = MemoryHelpers.Allocate(Constants.MaxSearchDepth * 218); + LateMovePruningInterestingReductionTable = MemoryHelpers.Allocate(Constants.MaxSearchDepth * 218); + UpdateNullMovePruningReductionTable(); + UpdateLateMovePruningReductionTable(); + } + + public static void UpdateNullMovePruningReductionTable() + { + for (var i = 0; i < Constants.MaxSearchDepth; i++) + { + NullMovePruningReductionTable[i] = (int)Math.Round(Math.Max(0, ((i - NullMovePruningReductionA) / NullMovePruningReductionB) + NullMovePruningReductionC)); + } + } + + public static void UpdateLateMovePruningReductionTable() + { + for (var i = 0; i < Constants.MaxSearchDepth; i++) + { + for (var j = 0; j < 218; j++) + { + LateMovePruningInterestingReductionTable[i*218 +j] = (int)Math.Round(LateMoveReductionInterestingA + (Math.Log(i) * Math.Log(j)) / LateMoveReductionInterestingB); + LateMovePruningReductionTable[i * 218 + j] = (int)Math.Round(LateMoveReductionA + (Math.Log(i) * Math.Log(j)) / LateMoveReductionB); + } + } + } } public static class SpsaTuner @@ -209,7 +241,7 @@ public static string FormatParameter(SpsaParameter parameter) { var min = int.Parse(parameter.MinValue); var max = int.Parse(parameter.MaxValue); - var ss = Math.Max(minStepSize, (max - min) / 20.0); + var ss = Math.Max(minStepSize, (max - min) / 40.0); var lr = double.Round(Math.Max(normalLearningRate, normalLearningRate * (0.50 / ss)), 4); return @@ -219,7 +251,7 @@ public static string FormatParameter(SpsaParameter parameter) { var min = double.Parse(parameter.MinValue); var max = double.Parse(parameter.MaxValue); - var ss = Math.Max(minStepSize, (max - min) / 20.0); + var ss = Math.Max(minStepSize, (max - min) / 40.0); var lr = double.Round(Math.Max(normalLearningRate, normalLearningRate * (0.50 / ss)), 4); return @@ -234,6 +266,11 @@ public static void ProcessUCIOptions() foreach (var field in typeof(SpsaOptions).GetFields(BindingFlags.Public | BindingFlags.Static) .Where(x => !x.IsLiteral).ToList()) { + + if (field.FieldType != typeof(int) && field.FieldType != typeof(float)) + { + continue; + } var defaultValue = field.GetValue(null)?.ToString() ?? "0"; // Retrieve the custom attributes, if present var minValueAttribute = field.GetCustomAttribute(); @@ -292,6 +329,12 @@ public static void SetParameterValue(string[] tokens) if (parameter.Name is "HistoryHeuristicBonusMax" or "HistoryHeuristicBonusCoeff") { HistoryHeuristicExtensions.UpdateBonusTable(); + }else if (parameter.Name.Contains("NullMovePruningReduction")) + { + SpsaOptions.UpdateNullMovePruningReductionTable(); + }else if (parameter.Name.Contains("LateMoveReduction")) + { + SpsaOptions.UpdateLateMovePruningReductionTable(); } ProcessUCIOptions(); diff --git a/src/Sapling/Program.cs b/src/Sapling/Program.cs index afd385f..54fb863 100644 --- a/src/Sapling/Program.cs +++ b/src/Sapling/Program.cs @@ -7,6 +7,7 @@ using Sapling.Engine.Evaluation; using Sapling.Engine.MoveGen; using Sapling.Engine.Search; +using Sapling.Engine.Tuning; namespace Sapling; @@ -105,9 +106,9 @@ private static void Main(string[] args) Task.Run(() => System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(RepetitionDetector).TypeHandle)), Task.Run(() => System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(HistoryHeuristicExtensions).TypeHandle)), Task.Run(() => System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(PVTable).TypeHandle)), - Task.Run(() => System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Zobrist).TypeHandle)) + Task.Run(() => System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Zobrist).TypeHandle)), + Task.Run(() => System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(SpsaOptions).TypeHandle)) }; - // Wait for all tasks to complete Task.WaitAll(tasks);