Add solver progress bar
This commit is contained in:
@@ -196,6 +196,8 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
private int? SolverStartStepCount { get; set; }
|
private int? SolverStartStepCount { get; set; }
|
||||||
private object? SolverQueueLock { get; set; }
|
private object? SolverQueueLock { get; set; }
|
||||||
private List<SimulatedActionStep>? SolverQueuedSteps { get; set; }
|
private List<SimulatedActionStep>? SolverQueuedSteps { get; set; }
|
||||||
|
private int solverProgress;
|
||||||
|
private int maxSolverProgress;
|
||||||
private bool SolverRunning => SolverTokenSource != null;
|
private bool SolverRunning => SolverTokenSource != null;
|
||||||
|
|
||||||
private IDalamudTextureWrap ExpertBadge { get; }
|
private IDalamudTextureWrap ExpertBadge { get; }
|
||||||
@@ -1269,7 +1271,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
ImGui.Dummy(new(0, imageSize));
|
ImGui.Dummy(new(0, imageSize));
|
||||||
ImGui.SameLine(0, 0);
|
ImGui.SameLine(0, 0);
|
||||||
|
|
||||||
var macroActionsHeight = ImGui.GetFrameHeightWithSpacing();
|
var macroActionsHeight = ImGui.GetFrameHeightWithSpacing() * (1 + (SolverRunning ? 1 : 0));
|
||||||
var childHeight = ImGui.GetContentRegionAvail().Y - ImGui.GetStyle().ItemSpacing.Y * 2 - ImGui.GetStyle().CellPadding.Y - macroActionsHeight - ImGui.GetStyle().ItemSpacing.Y * 2;
|
var childHeight = ImGui.GetContentRegionAvail().Y - ImGui.GetStyle().ItemSpacing.Y * 2 - ImGui.GetStyle().CellPadding.Y - macroActionsHeight - ImGui.GetStyle().ItemSpacing.Y * 2;
|
||||||
|
|
||||||
using (var child = ImRaii.Child("##macroActions", new(availSpace, childHeight)))
|
using (var child = ImRaii.Child("##macroActions", new(availSpace, childHeight)))
|
||||||
@@ -1308,6 +1310,19 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
ImGui.Dummy(default);
|
ImGui.Dummy(default);
|
||||||
ImGui.GetWindowDrawList().AddLine(pos, pos + new Vector2(availSpace, 0), ImGui.GetColorU32(ImGuiCol.Border));
|
ImGui.GetWindowDrawList().AddLine(pos, pos + new Vector2(availSpace, 0), ImGui.GetColorU32(ImGuiCol.Border));
|
||||||
ImGui.Dummy(default);
|
ImGui.Dummy(default);
|
||||||
|
if (SolverRunning)
|
||||||
|
{
|
||||||
|
var percentWidth = ImGui.CalcTextSize("100%").X;
|
||||||
|
var progressWidth = availSpace - percentWidth - spacing;
|
||||||
|
var fraction = Math.Clamp((float)solverProgress / maxSolverProgress, 0, 1);
|
||||||
|
using (var color = ImRaii.PushColor(ImGuiCol.PlotHistogram, ImGuiColors.DalamudGrey3))
|
||||||
|
ImGui.ProgressBar(fraction, new(progressWidth, ImGui.GetFrameHeight()), string.Empty);
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
ImGui.SetTooltip($"Solver Progress: {solverProgress} / {maxSolverProgress}");
|
||||||
|
ImGui.SameLine(0, spacing);
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGuiUtils.TextRight($"{fraction * 100:0}%", percentWidth);
|
||||||
|
}
|
||||||
DrawMacroActions(availSpace);
|
DrawMacroActions(availSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1602,6 +1617,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
}
|
}
|
||||||
SolverQueueLock = new();
|
SolverQueueLock = new();
|
||||||
SolverQueuedSteps ??= new();
|
SolverQueuedSteps ??= new();
|
||||||
|
solverProgress = 0;
|
||||||
|
|
||||||
RevertPreviousMacro();
|
RevertPreviousMacro();
|
||||||
|
|
||||||
@@ -1648,6 +1664,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
var solver = new Solver.Solver(config, state) { Token = token };
|
var solver = new Solver.Solver(config, state) { Token = token };
|
||||||
solver.OnLog += Log.Debug;
|
solver.OnLog += Log.Debug;
|
||||||
solver.OnNewAction += QueueSolverStep;
|
solver.OnNewAction += QueueSolverStep;
|
||||||
|
solver.OnProgress += (s, p, m) => { Interlocked.Exchange(ref solverProgress, p); Interlocked.Exchange(ref maxSolverProgress, m); };
|
||||||
solver.Start();
|
solver.Start();
|
||||||
_ = solver.GetTask().GetAwaiter().GetResult();
|
_ = solver.GetTask().GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
|||||||
+11
-5
@@ -15,6 +15,9 @@ public sealed class MCTS
|
|||||||
private readonly Node rootNode;
|
private readonly Node rootNode;
|
||||||
private readonly RootScores rootScores;
|
private readonly RootScores rootScores;
|
||||||
|
|
||||||
|
public const int ProgressUpdateFrequency = 1 << 10;
|
||||||
|
private const int StaleProgressThreshold = 1 << 12;
|
||||||
|
|
||||||
public float MaxScore => rootScores.MaxScore;
|
public float MaxScore => rootScores.MaxScore;
|
||||||
|
|
||||||
public MCTS(in MCTSConfig config, in SimulationState state)
|
public MCTS(in MCTSConfig config, in SimulationState state)
|
||||||
@@ -278,11 +281,11 @@ public sealed class MCTS
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Search(int iterations, CancellationToken token)
|
public void Search(int iterations, CancellationToken token, Action? progressCallback)
|
||||||
{
|
{
|
||||||
Simulator simulator = new(config.MaxStepCount);
|
Simulator simulator = new(config.MaxStepCount);
|
||||||
var random = rootNode.State.State.Input.Random;
|
var random = rootNode.State.State.Input.Random;
|
||||||
var n = 0;
|
var staleCounter = 0;
|
||||||
for (var i = 0; i < iterations || MaxScore == 0; i++)
|
for (var i = 0; i < iterations || MaxScore == 0; i++)
|
||||||
{
|
{
|
||||||
token.ThrowIfCancellationRequested();
|
token.ThrowIfCancellationRequested();
|
||||||
@@ -293,9 +296,9 @@ public sealed class MCTS
|
|||||||
{
|
{
|
||||||
if (endNode == selectedNode)
|
if (endNode == selectedNode)
|
||||||
{
|
{
|
||||||
if (n++ > 5000)
|
if (staleCounter++ >= StaleProgressThreshold)
|
||||||
{
|
{
|
||||||
n = 0;
|
staleCounter = 0;
|
||||||
if (AllNodesComplete())
|
if (AllNodesComplete())
|
||||||
{
|
{
|
||||||
//Console.WriteLine("All nodes solved for. Can't find a valid solution.");
|
//Console.WriteLine("All nodes solved for. Can't find a valid solution.");
|
||||||
@@ -305,10 +308,13 @@ public sealed class MCTS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
n = 0;
|
staleCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Backpropagate(endNode, score);
|
Backpropagate(endNode, score);
|
||||||
|
|
||||||
|
if ((i & (ProgressUpdateFrequency - 1)) == ProgressUpdateFrequency - 1)
|
||||||
|
progressCallback?.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+59
-5
@@ -19,8 +19,13 @@ public sealed class Solver : IDisposable
|
|||||||
private MCTSConfig MCTSConfig => new(Config);
|
private MCTSConfig MCTSConfig => new(Config);
|
||||||
private Task? CompletionTask { get; set; }
|
private Task? CompletionTask { get; set; }
|
||||||
|
|
||||||
|
private int progressSequence;
|
||||||
|
private int progress;
|
||||||
|
private int maxProgress;
|
||||||
|
|
||||||
public delegate void LogDelegate(string text);
|
public delegate void LogDelegate(string text);
|
||||||
public delegate void WorkerProgressDelegate(SolverSolution solution, float score);
|
public delegate void WorkerProgressDelegate(SolverSolution solution, float score);
|
||||||
|
public delegate void ProgressDelegate(int sequence, int value, int maxValue);
|
||||||
public delegate void NewActionDelegate(ActionType action);
|
public delegate void NewActionDelegate(ActionType action);
|
||||||
public delegate void SolutionDelegate(SolverSolution solution);
|
public delegate void SolutionDelegate(SolverSolution solution);
|
||||||
|
|
||||||
@@ -32,6 +37,11 @@ public sealed class Solver : IDisposable
|
|||||||
// For example, to get to the terminal state, execute all OnNewAction actions, then execute all Solution actions.
|
// For example, to get to the terminal state, execute all OnNewAction actions, then execute all Solution actions.
|
||||||
public event WorkerProgressDelegate? OnWorkerProgress;
|
public event WorkerProgressDelegate? OnWorkerProgress;
|
||||||
|
|
||||||
|
// Always called in some form in every algorithm.
|
||||||
|
// In iterative algorithms, the sequence can increment and reset the value back to 0.
|
||||||
|
// In other algorithms, the sequence is always 0 and the value increases monotonically.
|
||||||
|
public event ProgressDelegate? OnProgress;
|
||||||
|
|
||||||
// Always called when a new step is generated.
|
// Always called when a new step is generated.
|
||||||
public event NewActionDelegate? OnNewAction;
|
public event NewActionDelegate? OnNewAction;
|
||||||
|
|
||||||
@@ -63,6 +73,7 @@ public sealed class Solver : IDisposable
|
|||||||
{
|
{
|
||||||
Token.ThrowIfCancellationRequested();
|
Token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
progressSequence = progress = 0;
|
||||||
Solution = await SearchFunc().ConfigureAwait(false);
|
Solution = await SearchFunc().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,8 +121,35 @@ public sealed class Solver : IDisposable
|
|||||||
OnNewAction?.Invoke(sanitizedAction);
|
OnNewAction?.Invoke(sanitizedAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void IncrementProgress() =>
|
||||||
|
IncrementProgressBy(MCTS.ProgressUpdateFrequency);
|
||||||
|
|
||||||
|
private void IncrementRemainingProgress(int iterations) =>
|
||||||
|
IncrementProgressBy(iterations & MCTS.ProgressUpdateFrequency);
|
||||||
|
|
||||||
|
private void IncrementProgressBy(int value)
|
||||||
|
{
|
||||||
|
OnProgress?.Invoke(progressSequence, Interlocked.Add(ref progress, value), maxProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IncrementProgressSequence()
|
||||||
|
{
|
||||||
|
Interlocked.Exchange(ref progress, 0);
|
||||||
|
progressSequence++;
|
||||||
|
OnProgress?.Invoke(progressSequence, 0, maxProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SearchWithIncrement(MCTS mcts, int iterations)
|
||||||
|
{
|
||||||
|
mcts.Search(iterations, Token, IncrementProgress);
|
||||||
|
IncrementRemainingProgress(iterations);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<SolverSolution> SearchStepwiseFurcated()
|
private async Task<SolverSolution> SearchStepwiseFurcated()
|
||||||
{
|
{
|
||||||
|
var iterCount = Config.Iterations / Config.ForkCount;
|
||||||
|
maxProgress = iterCount * Config.ForkCount;
|
||||||
|
|
||||||
var definiteActionCount = 0;
|
var definiteActionCount = 0;
|
||||||
var bestSims = new List<(float Score, SolverSolution Result)>();
|
var bestSims = new List<(float Score, SolverSolution Result)>();
|
||||||
|
|
||||||
@@ -136,7 +174,7 @@ public sealed class Solver : IDisposable
|
|||||||
await semaphore.WaitAsync(Token).ConfigureAwait(false);
|
await semaphore.WaitAsync(Token).ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
solver.Search(Config.Iterations / Config.ForkCount, Token);
|
SearchWithIncrement(solver, iterCount);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -223,6 +261,8 @@ public sealed class Solver : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IncrementProgressSequence();
|
||||||
|
|
||||||
activeStates = newStates;
|
activeStates = newStates;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,6 +278,9 @@ public sealed class Solver : IDisposable
|
|||||||
|
|
||||||
private async Task<SolverSolution> SearchStepwiseForked()
|
private async Task<SolverSolution> SearchStepwiseForked()
|
||||||
{
|
{
|
||||||
|
var iterCount = Config.Iterations / Config.ForkCount;
|
||||||
|
maxProgress = iterCount * Config.ForkCount;
|
||||||
|
|
||||||
var actions = new List<ActionType>();
|
var actions = new List<ActionType>();
|
||||||
var state = State;
|
var state = State;
|
||||||
var sim = new Simulator(Config.MaxStepCount) { State = state };
|
var sim = new Simulator(Config.MaxStepCount) { State = state };
|
||||||
@@ -258,7 +301,7 @@ public sealed class Solver : IDisposable
|
|||||||
await semaphore.WaitAsync(Token).ConfigureAwait(false);
|
await semaphore.WaitAsync(Token).ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
solver.Search(Config.Iterations / Config.ForkCount, Token);
|
SearchWithIncrement(solver, iterCount);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -294,6 +337,8 @@ public sealed class Solver : IDisposable
|
|||||||
|
|
||||||
(_, state) = sim.Execute(state, chosenAction);
|
(_, state) = sim.Execute(state, chosenAction);
|
||||||
actions.Add(chosenAction);
|
actions.Add(chosenAction);
|
||||||
|
|
||||||
|
IncrementProgressSequence();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new(actions, state);
|
return new(actions, state);
|
||||||
@@ -301,6 +346,8 @@ public sealed class Solver : IDisposable
|
|||||||
|
|
||||||
private Task<SolverSolution> SearchStepwise()
|
private Task<SolverSolution> SearchStepwise()
|
||||||
{
|
{
|
||||||
|
maxProgress = Config.Iterations;
|
||||||
|
|
||||||
var actions = new List<ActionType>();
|
var actions = new List<ActionType>();
|
||||||
var state = State;
|
var state = State;
|
||||||
var sim = new Simulator(Config.MaxStepCount) { State = state };
|
var sim = new Simulator(Config.MaxStepCount) { State = state };
|
||||||
@@ -314,7 +361,7 @@ public sealed class Solver : IDisposable
|
|||||||
var solver = new MCTS(MCTSConfig, state);
|
var solver = new MCTS(MCTSConfig, state);
|
||||||
|
|
||||||
var s = Stopwatch.StartNew();
|
var s = Stopwatch.StartNew();
|
||||||
solver.Search(Config.Iterations, Token);
|
SearchWithIncrement(solver, Config.Iterations);
|
||||||
s.Stop();
|
s.Stop();
|
||||||
OnLog?.Invoke($"{s.Elapsed.TotalMilliseconds:0.00}ms {Config.Iterations / s.Elapsed.TotalSeconds / 1000:0.00} kI/s");
|
OnLog?.Invoke($"{s.Elapsed.TotalMilliseconds:0.00}ms {Config.Iterations / s.Elapsed.TotalSeconds / 1000:0.00} kI/s");
|
||||||
|
|
||||||
@@ -333,6 +380,8 @@ public sealed class Solver : IDisposable
|
|||||||
|
|
||||||
(_, state) = sim.Execute(state, chosenAction);
|
(_, state) = sim.Execute(state, chosenAction);
|
||||||
actions.Add(chosenAction);
|
actions.Add(chosenAction);
|
||||||
|
|
||||||
|
IncrementProgressSequence();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(new SolverSolution(actions, state));
|
return Task.FromResult(new SolverSolution(actions, state));
|
||||||
@@ -340,6 +389,9 @@ public sealed class Solver : IDisposable
|
|||||||
|
|
||||||
private async Task<SolverSolution> SearchOneshotForked()
|
private async Task<SolverSolution> SearchOneshotForked()
|
||||||
{
|
{
|
||||||
|
var iterCount = Config.Iterations / Config.ForkCount;
|
||||||
|
maxProgress = iterCount * Config.ForkCount;
|
||||||
|
|
||||||
using var semaphore = new SemaphoreSlim(0, Config.MaxThreadCount);
|
using var semaphore = new SemaphoreSlim(0, Config.MaxThreadCount);
|
||||||
var tasks = new Task<(float MaxScore, SolverSolution Solution)>[Config.ForkCount];
|
var tasks = new Task<(float MaxScore, SolverSolution Solution)>[Config.ForkCount];
|
||||||
for (var i = 0; i < Config.ForkCount; ++i)
|
for (var i = 0; i < Config.ForkCount; ++i)
|
||||||
@@ -349,7 +401,7 @@ public sealed class Solver : IDisposable
|
|||||||
await semaphore.WaitAsync(Token).ConfigureAwait(false);
|
await semaphore.WaitAsync(Token).ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
solver.Search(Config.Iterations / Config.ForkCount, Token);
|
SearchWithIncrement(solver, iterCount);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -375,8 +427,10 @@ public sealed class Solver : IDisposable
|
|||||||
|
|
||||||
private Task<SolverSolution> SearchOneshot()
|
private Task<SolverSolution> SearchOneshot()
|
||||||
{
|
{
|
||||||
|
maxProgress = Config.Iterations;
|
||||||
|
|
||||||
var solver = new MCTS(MCTSConfig, State);
|
var solver = new MCTS(MCTSConfig, State);
|
||||||
solver.Search(Config.Iterations, Token);
|
SearchWithIncrement(solver, Config.Iterations);
|
||||||
var solution = solver.Solution();
|
var solution = solver.Solution();
|
||||||
foreach (var action in solution.Actions)
|
foreach (var action in solution.Actions)
|
||||||
InvokeNewAction(action);
|
InvokeNewAction(action);
|
||||||
|
|||||||
Reference in New Issue
Block a user