diff --git a/Benchmark/Bench.cs b/Benchmark/Bench.cs index 8bc3c7d..53bfbc8 100644 --- a/Benchmark/Bench.cs +++ b/Benchmark/Bench.cs @@ -113,7 +113,7 @@ public class Bench var solver = new MCTS(config, State); var progress = 0; - solver.Search(Config.Data.Iterations, ref progress, CancellationToken.None); + solver.Search(Config.Data.Iterations, Config.Data.MaxIterations, ref progress, CancellationToken.None); var solution = solver.Solution(); return (solver.MaxScore, solution); diff --git a/Craftimizer/Windows/Settings.cs b/Craftimizer/Windows/Settings.cs index 0221590..44c9103 100644 --- a/Craftimizer/Windows/Settings.cs +++ b/Craftimizer/Windows/Settings.cs @@ -89,6 +89,15 @@ public sealed class Settings : Window, IDisposable } } } + else + { + var newValue = T.Clamp(value, min, max); + if (value != newValue) + { + setter(newValue); + isDirty = true; + } + } if (ImGui.IsItemHovered()) ImGuiUtils.TooltipWrapped(tooltip); } @@ -505,7 +514,7 @@ public sealed class Settings : Window, IDisposable ); DrawOption( - "Iterations", + "Target Iterations", "The total number of iterations to run per crafting step. " + "Higher values require more computational power. Higher values " + "also may decrease variance, so other values should be tweaked " + @@ -517,6 +526,20 @@ public sealed class Settings : Window, IDisposable ref isDirty ); + DrawOption( + "Max Iterations", + "The solver may go about the target iteration value if the craft " + + "is sufficiently difficult, and it wasn't able to find any way to " + + "complete it yet. In rare cases, the solver might go on for a very " + + "long time. This maximum is here to prevent the solver from stealing " + + "all your RAM.", + config.MaxIterations, + config.Iterations, + 5000000, + v => config = config with { MaxIterations = v }, + ref isDirty + ); + DrawOption( "Max Step Count", "The maximum number of crafting steps; this is generally the only " + @@ -995,7 +1018,7 @@ public sealed class Settings : Window, IDisposable "Enforces a maximum number of steps to display in the synth helper to " + "get rid of clutter.", Config.SynthHelperMaxDisplayCount, - 1, + Config.SynthHelperStepCount, 100, v => Config.SynthHelperMaxDisplayCount = v, ref isDirty diff --git a/Solver/MCTS.cs b/Solver/MCTS.cs index a26a219..45e2623 100644 --- a/Solver/MCTS.cs +++ b/Solver/MCTS.cs @@ -253,14 +253,16 @@ public sealed class MCTS } [SkipLocalsInit] - public unsafe void Search(int iterations, ref int progress, CancellationToken token) + public unsafe void Search(int iterations, int maxIterations, ref int progress, CancellationToken token) { + maxIterations = Math.Max(iterations, maxIterations); var simulator = new Simulator(config.ActionPool, config.MaxStepCount, rootNode.State.State); var random = rootNode.State.State.Input.Random; var staleCounter = 0; var i = 0; + Span actionBuffer = stackalloc ActionType[Math.Min(config.MaxStepCount, config.MaxRolloutStepCount)]; - for (; i < iterations || MaxScore == 0; i++) + for (; (i < iterations || MaxScore == 0); i++) { var selectedNode = Select(); var (endNode, score) = ExpandAndRollout(random, simulator, selectedNode, actionBuffer); @@ -268,6 +270,8 @@ public sealed class MCTS { if (endNode == selectedNode) { + if (i >= maxIterations) + return; if (staleCounter++ >= StaleProgressThreshold) { staleCounter = 0; diff --git a/Solver/Solver.cs b/Solver/Solver.cs index aa82137..a848bf3 100644 --- a/Solver/Solver.cs +++ b/Solver/Solver.cs @@ -139,6 +139,7 @@ public sealed class Solver : IDisposable private async Task SearchStepwiseGenetic() { var iterCount = Config.Iterations / Config.ForkCount; + var maxIterCount = Math.Max(Config.Iterations, Config.MaxIterations) / Config.ForkCount; maxProgress = iterCount * Config.ForkCount; var definiteActionCount = 0; @@ -165,7 +166,7 @@ public sealed class Solver : IDisposable await semaphore.WaitAsync(Token).ConfigureAwait(false); try { - solver.Search(iterCount, ref progress, Token); + solver.Search(iterCount, maxIterCount, ref progress, Token); } finally { @@ -256,6 +257,7 @@ public sealed class Solver : IDisposable private async Task SearchStepwiseForked() { var iterCount = Config.Iterations / Config.ForkCount; + var maxIterCount = Math.Max(Config.Iterations, Config.MaxIterations) / Config.ForkCount; maxProgress = iterCount * Config.ForkCount; var actions = new List(); @@ -278,7 +280,7 @@ public sealed class Solver : IDisposable await semaphore.WaitAsync(Token).ConfigureAwait(false); try { - solver.Search(iterCount, ref progress, Token); + solver.Search(iterCount, maxIterCount, ref progress, Token); } finally { @@ -329,7 +331,7 @@ public sealed class Solver : IDisposable var solver = new MCTS(MCTSConfig, state); var s = Stopwatch.StartNew(); - solver.Search(Config.Iterations, ref progress, Token); + solver.Search(Config.Iterations, Config.MaxIterations, ref progress, Token); s.Stop(); OnLog?.Invoke($"{s.Elapsed.TotalMilliseconds:0.00}ms {progress / s.Elapsed.TotalSeconds / 1000:0.00} kI/s"); @@ -350,6 +352,7 @@ public sealed class Solver : IDisposable private async Task SearchOneshotForked() { var iterCount = Config.Iterations / Config.ForkCount; + var maxIterCount = Math.Max(Config.Iterations, Config.MaxIterations) / Config.ForkCount; maxProgress = iterCount * Config.ForkCount; using var semaphore = new SemaphoreSlim(0, Config.MaxThreadCount); @@ -362,7 +365,7 @@ public sealed class Solver : IDisposable await semaphore.WaitAsync(Token).ConfigureAwait(false); try { - solver.Search(iterCount, ref progress, Token); + solver.Search(iterCount, maxIterCount, ref progress, Token); } finally { @@ -394,7 +397,7 @@ public sealed class Solver : IDisposable var solver = new MCTS(MCTSConfig, State); var s = Stopwatch.StartNew(); - solver.Search(Config.Iterations, ref progress, Token); + solver.Search(Config.Iterations, Config.MaxIterations, ref progress, Token); s.Stop(); OnLog?.Invoke($"{s.Elapsed.TotalMilliseconds:0.00}ms {progress / s.Elapsed.TotalSeconds / 1000:0.00} kI/s"); diff --git a/Solver/SolverConfig.cs b/Solver/SolverConfig.cs index 4a2f475..eff8969 100644 --- a/Solver/SolverConfig.cs +++ b/Solver/SolverConfig.cs @@ -17,6 +17,7 @@ public enum SolverAlgorithm public readonly record struct SolverConfig { public int Iterations { get; init; } + public int MaxIterations { get; init; } public float MaxScoreWeightingConstant { get; init; } public float ExplorationConstant { get; init; } public int MaxStepCount { get; init; } @@ -38,6 +39,7 @@ public readonly record struct SolverConfig public SolverConfig() { Iterations = 100_000; + MaxIterations = 1_500_000; MaxScoreWeightingConstant = 0.1f; ExplorationConstant = 4; MaxStepCount = 30;