More heuristic improvements
This commit is contained in:
@@ -46,22 +46,22 @@ internal static class Program
|
|||||||
|
|
||||||
var config = new SolverConfig()
|
var config = new SolverConfig()
|
||||||
{
|
{
|
||||||
Iterations = 30_000,
|
Iterations = 300_000,
|
||||||
ThreadCount = 8,
|
ForkCount = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
Debugger.Break();
|
Debugger.Break();
|
||||||
var s = Stopwatch.StartNew();
|
var s = Stopwatch.StartNew();
|
||||||
if (true) {
|
if (true) {
|
||||||
(_, var state) = Solver.Crafty.Solver.SearchStepwise(config, input, a => Console.WriteLine(a));
|
(_, var state) = Solver.Crafty.Solver.SearchStepwiseForked(config, input, a => Console.WriteLine(a));
|
||||||
Console.WriteLine($"Qual: {state.Quality}/{state.Input.Recipe.MaxQuality}");
|
Console.WriteLine($"Qual: {state.Quality}/{state.Input.Recipe.MaxQuality}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//(var actions, _) = SolverUtils.SearchOneshot<SolverConcurrent>(config, input);
|
var (actions, state) = Solver.Crafty.Solver.SearchOneshotForked(config, input);
|
||||||
//foreach (var action in actions)
|
foreach (var action in actions)
|
||||||
// Console.Write($">{action.IntName()}");
|
Console.WriteLine(action);
|
||||||
//Console.WriteLine();
|
Console.WriteLine($"Qual: {state.Quality}/{state.Input.Recipe.MaxQuality}");
|
||||||
}
|
}
|
||||||
s.Stop();
|
s.Stop();
|
||||||
Console.WriteLine($"{s.Elapsed.TotalMilliseconds:0.00}");
|
Console.WriteLine($"{s.Elapsed.TotalMilliseconds:0.00}");
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace Craftimizer.Solver.Crafty;
|
|||||||
|
|
||||||
public struct ActionSet
|
public struct ActionSet
|
||||||
{
|
{
|
||||||
private const bool IsDeterministic = true;
|
private const bool IsDeterministic = false;
|
||||||
|
|
||||||
private uint bits;
|
private uint bits;
|
||||||
|
|
||||||
|
|||||||
@@ -95,9 +95,9 @@ public sealed class Simulator : SimulatorNoRandom
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// use First Turn actions if it's available and the craft is difficult
|
// use First Turn actions if it's available and the craft is difficult
|
||||||
if (baseAction.Category != ActionCategory.FirstTurn &&
|
if (IsFirstStep &&
|
||||||
Input.Recipe.ClassJobLevel == 90 &&
|
Input.Recipe.ClassJobLevel == 90 &&
|
||||||
StepCount == 1 &&
|
baseAction.Category != ActionCategory.FirstTurn &&
|
||||||
CP > 10)
|
CP > 10)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -107,6 +107,11 @@ public sealed class Simulator : SimulatorNoRandom
|
|||||||
baseAction.IncreasesQuality)
|
baseAction.IncreasesQuality)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// don't allow pure quality moves when it won't be able to finish the craft
|
||||||
|
if (baseAction.IncreasesQuality &&
|
||||||
|
CalculateDurabilityCost(baseAction.DurabilityCost) > Durability)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (baseAction.IncreasesProgress)
|
if (baseAction.IncreasesProgress)
|
||||||
{
|
{
|
||||||
var progressIncrease = CalculateProgressGain(baseAction.Efficiency(this));
|
var progressIncrease = CalculateProgressGain(baseAction.Efficiency(this));
|
||||||
|
|||||||
+69
-36
@@ -1,5 +1,6 @@
|
|||||||
using Craftimizer.Simulator.Actions;
|
|
||||||
using Craftimizer.Simulator;
|
using Craftimizer.Simulator;
|
||||||
|
using Craftimizer.Simulator.Actions;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -16,7 +17,7 @@ public sealed class Solver
|
|||||||
|
|
||||||
public float MaxScore => rootScores.MaxScore;
|
public float MaxScore => rootScores.MaxScore;
|
||||||
|
|
||||||
public Solver(SolverConfig config, SimulationState state, bool strict)
|
public Solver(SolverConfig config, SimulationState state)
|
||||||
{
|
{
|
||||||
this.config = config;
|
this.config = config;
|
||||||
var sim = new Simulator(state, config.MaxStepCount);
|
var sim = new Simulator(state, config.MaxStepCount);
|
||||||
@@ -24,7 +25,7 @@ public sealed class Solver
|
|||||||
state,
|
state,
|
||||||
null,
|
null,
|
||||||
sim.CompletionState,
|
sim.CompletionState,
|
||||||
sim.AvailableActionsHeuristic(strict)
|
sim.AvailableActionsHeuristic(config.StrictActions)
|
||||||
));
|
));
|
||||||
rootScores = new();
|
rootScores = new();
|
||||||
}
|
}
|
||||||
@@ -206,16 +207,18 @@ public sealed class Solver
|
|||||||
var currentCompletionState = expandedNode.State.SimulationCompletionState;
|
var currentCompletionState = expandedNode.State.SimulationCompletionState;
|
||||||
var currentActions = expandedNode.State.AvailableActions;
|
var currentActions = expandedNode.State.AvailableActions;
|
||||||
|
|
||||||
|
|
||||||
byte actionCount = 0;
|
byte actionCount = 0;
|
||||||
Span<ActionType> actions = stackalloc ActionType[Math.Min(config.MaxStepCount - currentState.ActionCount, config.MaxRolloutStepCount)];
|
Span<ActionType> actions = stackalloc ActionType[Math.Min(config.MaxStepCount - currentState.ActionCount, config.MaxRolloutStepCount)];
|
||||||
while (actionCount < actions.Length)
|
while (SimulationNode.GetCompletionState(currentCompletionState, currentActions) == CompletionState.Incomplete &&
|
||||||
|
actionCount < actions.Length)
|
||||||
{
|
{
|
||||||
if (SimulationNode.GetCompletionState(currentCompletionState, currentActions) != CompletionState.Incomplete)
|
|
||||||
break;
|
|
||||||
var nextAction = currentActions.SelectRandom(random);
|
var nextAction = currentActions.SelectRandom(random);
|
||||||
actions[actionCount++] = nextAction;
|
actions[actionCount++] = nextAction;
|
||||||
(_, currentState) = simulator.Execute(currentState, nextAction);
|
(_, currentState) = simulator.Execute(currentState, nextAction);
|
||||||
currentCompletionState = simulator.CompletionState;
|
currentCompletionState = simulator.CompletionState;
|
||||||
|
if (currentCompletionState != CompletionState.Incomplete)
|
||||||
|
break;
|
||||||
currentActions = simulator.AvailableActionsHeuristic(true);
|
currentActions = simulator.AvailableActionsHeuristic(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,11 +251,11 @@ public sealed class Solver
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void Search(CancellationToken token)
|
private void Search(CancellationToken token, int iterations)
|
||||||
{
|
{
|
||||||
Simulator simulator = new(rootNode.State.State, config.MaxStepCount);
|
Simulator simulator = new(rootNode.State.State, config.MaxStepCount);
|
||||||
var random = rootNode.State.State.Input.Random;
|
var random = rootNode.State.State.Input.Random;
|
||||||
for (var i = 0; i < config.Iterations; i++)
|
for (var i = 0; i < iterations; i++)
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested)
|
if (token.IsCancellationRequested)
|
||||||
break;
|
break;
|
||||||
@@ -264,43 +267,48 @@ public sealed class Solver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (List<ActionType> Actions, SimulationState State) SearchStepwiseForked(SolverConfig config, int forkCount, SimulationInput input, Action<ActionType>? actionCallback, CancellationToken token = default) =>
|
public static (List<ActionType> Actions, SimulationState State) SearchStepwiseForked(SolverConfig config, SimulationInput input, Action<ActionType>? actionCallback, CancellationToken token = default) =>
|
||||||
SearchStepwiseForked(config, forkCount, new SimulationState(input), actionCallback, token);
|
SearchStepwiseForked(config, new SimulationState(input), actionCallback, token);
|
||||||
|
|
||||||
public static (List<ActionType> Actions, SimulationState State) SearchStepwiseForked(SolverConfig config, int forkCount, SimulationState state, Action<ActionType>? actionCallback, CancellationToken token = default)
|
public static (List<ActionType> Actions, SimulationState State) SearchStepwiseForked(SolverConfig config, SimulationState state, Action<ActionType>? actionCallback, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var actions = new List<ActionType>();
|
var actions = new List<ActionType>();
|
||||||
var sim = new Simulator(state, config.MaxStepCount);
|
var sim = new Simulator(state, config.MaxStepCount);
|
||||||
while (!sim.IsComplete)
|
while (true)
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested)
|
if (token.IsCancellationRequested)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
var tasks = new Task<(float score, List<ActionType> actions, SimulationState state)>[forkCount];
|
if (sim.IsComplete)
|
||||||
for (var i = 0; i < forkCount; ++i)
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
var s = Stopwatch.StartNew();
|
||||||
|
var tasks = new Task<(float MaxScore, (List<ActionType> Actions, SimulationNode Node) Solution)>[config.ForkCount];
|
||||||
|
for (var i = 0; i < config.ForkCount; ++i)
|
||||||
tasks[i] = Task.Run(() =>
|
tasks[i] = Task.Run(() =>
|
||||||
{
|
{
|
||||||
var solver = new Solver(config, state, true);
|
var solver = new Solver(config, state);
|
||||||
solver.Search(token);
|
solver.Search(token, config.Iterations / config.ForkCount);
|
||||||
var (solution_actions, solution_node) = solver.Solution();
|
return (solver.MaxScore, solver.Solution());
|
||||||
|
|
||||||
return (solver.MaxScore, solution_actions, solution_node.State);
|
|
||||||
}, token);
|
}, token);
|
||||||
Task.WaitAll(tasks, CancellationToken.None);
|
Task.WaitAll(tasks, CancellationToken.None);
|
||||||
|
s.Stop();
|
||||||
|
|
||||||
var (score, solution_actions, solution_state) = tasks.Select(t => t.Result).MaxBy(r => r.score);
|
var (maxScore, (solutionActions, solutionNode)) = tasks.Select(t => t.Result).MaxBy(r => r.MaxScore);
|
||||||
|
|
||||||
if (score >= 1.0)
|
if (maxScore >= config.ScoreStorageThreshold)
|
||||||
{
|
{
|
||||||
actions.AddRange(solution_actions);
|
actions.AddRange(solutionActions);
|
||||||
return (actions, solution_state);
|
return (actions, solutionNode.State);
|
||||||
}
|
}
|
||||||
|
|
||||||
var chosen_action = solution_actions[0];
|
var chosen_action = solutionActions[0];
|
||||||
|
actionCallback?.Invoke(chosen_action);
|
||||||
|
Console.WriteLine($"{s.Elapsed.TotalMilliseconds:0.00}ms {config.Iterations / s.Elapsed.TotalSeconds / 1000:0.00} kI/s");
|
||||||
|
|
||||||
(_, state) = sim.Execute(state, chosen_action);
|
(_, state) = sim.Execute(state, chosen_action);
|
||||||
actions.Add(chosen_action);
|
actions.Add(chosen_action);
|
||||||
|
|
||||||
actionCallback?.Invoke(chosen_action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (actions, state);
|
return (actions, state);
|
||||||
@@ -313,40 +321,65 @@ public sealed class Solver
|
|||||||
{
|
{
|
||||||
var actions = new List<ActionType>();
|
var actions = new List<ActionType>();
|
||||||
var sim = new Simulator(state, config.MaxStepCount);
|
var sim = new Simulator(state, config.MaxStepCount);
|
||||||
var solver = new Solver(config, state, true);
|
while (true)
|
||||||
while (!sim.IsComplete)
|
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested)
|
if (token.IsCancellationRequested)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
solver.Search(token);
|
if (sim.IsComplete)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var solver = new Solver(config, state);
|
||||||
|
|
||||||
|
var s = Stopwatch.StartNew();
|
||||||
|
solver.Search(token, config.Iterations);
|
||||||
|
s.Stop();
|
||||||
|
|
||||||
var (solution_actions, solution_node) = solver.Solution();
|
var (solution_actions, solution_node) = solver.Solution();
|
||||||
|
|
||||||
if (solver.MaxScore >= 1.0)
|
if (solver.MaxScore >= config.ScoreStorageThreshold)
|
||||||
{
|
{
|
||||||
actions.AddRange(solution_actions);
|
actions.AddRange(solution_actions);
|
||||||
return (actions, solution_node.State);
|
return (actions, solution_node.State);
|
||||||
}
|
}
|
||||||
|
|
||||||
var chosen_action = solution_actions[0];
|
var chosen_action = solution_actions[0];
|
||||||
|
actionCallback?.Invoke(chosen_action);
|
||||||
|
Console.WriteLine($"{s.Elapsed.TotalMilliseconds:0.00}ms {config.Iterations / s.Elapsed.TotalSeconds / 1000:0.00} kI/s");
|
||||||
|
|
||||||
(_, state) = sim.Execute(state, chosen_action);
|
(_, state) = sim.Execute(state, chosen_action);
|
||||||
actions.Add(chosen_action);
|
actions.Add(chosen_action);
|
||||||
|
|
||||||
actionCallback?.Invoke(chosen_action);
|
|
||||||
|
|
||||||
solver = new Solver(config, state, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (actions, state);
|
return (actions, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static (List<ActionType> Actions, SimulationState State) SearchOneshotForked(SolverConfig config, SimulationInput input, CancellationToken token = default) =>
|
||||||
|
SearchOneshotForked(config, new SimulationState(input), token);
|
||||||
|
|
||||||
|
public static (List<ActionType> Actions, SimulationState State) SearchOneshotForked(SolverConfig config, SimulationState state, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var tasks = new Task<(float MaxScore, (List<ActionType> Actions, SimulationNode Node) Solution)>[config.ForkCount];
|
||||||
|
for (var i = 0; i < config.ForkCount; ++i)
|
||||||
|
tasks[i] = Task.Run(() =>
|
||||||
|
{
|
||||||
|
var solver = new Solver(config, state);
|
||||||
|
solver.Search(token, config.Iterations / config.ForkCount);
|
||||||
|
return (solver.MaxScore, solver.Solution());
|
||||||
|
}, token);
|
||||||
|
Task.WaitAll(tasks, CancellationToken.None);
|
||||||
|
|
||||||
|
var (solutionActions, solutionNode) = tasks.Select(t => t.Result).MaxBy(r => r.MaxScore).Solution;
|
||||||
|
return (solutionActions, solutionNode.State);
|
||||||
|
}
|
||||||
|
|
||||||
public static (List<ActionType> Actions, SimulationState State) SearchOneshot(SolverConfig config, SimulationInput input, CancellationToken token = default) =>
|
public static (List<ActionType> Actions, SimulationState State) SearchOneshot(SolverConfig config, SimulationInput input, CancellationToken token = default) =>
|
||||||
SearchOneshot(config, new SimulationState(input), token);
|
SearchOneshot(config, new SimulationState(input), token);
|
||||||
|
|
||||||
public static (List<ActionType> Actions, SimulationState State) SearchOneshot(SolverConfig config, SimulationState state, CancellationToken token = default)
|
public static (List<ActionType> Actions, SimulationState State) SearchOneshot(SolverConfig config, SimulationState state, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var solver = new Solver(config, state, false);
|
var solver = new Solver(config, state);
|
||||||
solver.Search(token);
|
solver.Search(token, config.Iterations);
|
||||||
var (solution_actions, solution_node) = solver.Solution();
|
var (solution_actions, solution_node) = solver.Solution();
|
||||||
return (solution_actions, solution_node.State);
|
return (solution_actions, solution_node.State);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ public readonly record struct SolverConfig
|
|||||||
public float ExplorationConstant { get; init; }
|
public float ExplorationConstant { get; init; }
|
||||||
public int MaxStepCount { get; init; }
|
public int MaxStepCount { get; init; }
|
||||||
public int MaxRolloutStepCount { get; init; }
|
public int MaxRolloutStepCount { get; init; }
|
||||||
public int ThreadCount { get; init; }
|
public int ForkCount { get; init; }
|
||||||
|
public bool StrictActions { get; init; }
|
||||||
|
|
||||||
public SolverConfig()
|
public SolverConfig()
|
||||||
{
|
{
|
||||||
@@ -21,6 +22,7 @@ public readonly record struct SolverConfig
|
|||||||
ExplorationConstant = 4;
|
ExplorationConstant = 4;
|
||||||
MaxStepCount = 25;
|
MaxStepCount = 25;
|
||||||
MaxRolloutStepCount = MaxStepCount;
|
MaxRolloutStepCount = MaxStepCount;
|
||||||
ThreadCount = Environment.ProcessorCount;
|
ForkCount = Environment.ProcessorCount;
|
||||||
|
StrictActions = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user