Files
Craftimizer/Solver/Solver.cs
T
2023-10-02 13:07:06 -07:00

247 lines
9.6 KiB
C#

using Craftimizer.Simulator;
using Craftimizer.Simulator.Actions;
using System.Diagnostics;
namespace Craftimizer.Solver;
public static class Solver
{
private static SolverSolution SearchStepwiseFurcated(SolverConfig config, SimulationState state, Action<ActionType>? actionCallback, CancellationToken token)
{
var definiteActionCount = 0;
var bestSims = new List<(float Score, SolverSolution Result)>();
var sim = new Simulator(state, config.MaxStepCount);
var activeStates = new List<SolverSolution>() { new(new(), state) };
while (activeStates.Count != 0)
{
if (token.IsCancellationRequested)
break;
var s = Stopwatch.StartNew();
var tasks = new Task<(float MaxScore, int FurcatedActionIdx, SolverSolution Solution)>[config.ForkCount];
for (var i = 0; i < config.ForkCount; i++)
{
var stateIdx = (int)((float)i / config.ForkCount * activeStates.Count);
var st = activeStates[stateIdx];
tasks[i] = Task.Run(() =>
{
var solver = new MCTS(new(config), activeStates[stateIdx].State);
solver.Search(config.Iterations / config.ForkCount, token);
return (solver.MaxScore, stateIdx, solver.Solution());
}, token);
}
Task.WaitAll(tasks, token);
s.Stop();
if (token.IsCancellationRequested)
break;
var bestActions = tasks.Select(t => t.Result).OrderByDescending(r => r.MaxScore).Take(config.FurcatedActionCount).ToArray();
var bestAction = bestActions[0];
if (bestAction.MaxScore >= config.ScoreStorageThreshold)
{
var (maxScore, furcatedActionIdx, solution) = bestAction;
var (activeActions, activeState) = activeStates[furcatedActionIdx];
activeActions.AddRange(solution.Actions);
return solution with { Actions = activeActions };
}
var newStates = new List<SolverSolution>(config.FurcatedActionCount);
for (var i = 0; i < bestActions.Length; ++i)
{
var (maxScore, furcatedActionIdx, (solutionActions, solutionNode)) = bestActions[i];
if (solutionActions.Count == 0)
continue;
var (activeActions, activeState) = activeStates[furcatedActionIdx];
var chosenAction = solutionActions[0];
var newActions = new List<ActionType>(activeActions) { chosenAction };
var newState = sim.Execute(activeState, chosenAction).NewState;
if (sim.IsComplete)
bestSims.Add((maxScore, new(newActions, newState)));
else
newStates.Add(new(newActions, newState));
}
if (bestSims.Count == 0 && newStates.Count != 0)
{
var definiteCount = definiteActionCount;
var equalCount = int.MaxValue;
var refActions = newStates[0].Actions;
for (var i = 1; i < newStates.Count; ++i)
{
var cmpActions = newStates[i].Actions;
var possibleCount = Math.Min(Math.Min(refActions.Count, cmpActions.Count), equalCount);
var completelyEqual = true;
for (var j = definiteCount; j < possibleCount; ++j)
{
if (refActions[j] != cmpActions[j])
{
equalCount = j;
completelyEqual = false;
break;
}
}
if (completelyEqual)
equalCount = possibleCount;
}
if (definiteCount != equalCount)
{
for (var i = definiteCount; i < equalCount; ++i)
actionCallback?.Invoke(refActions[i]);
definiteActionCount = equalCount;
}
}
activeStates = newStates;
Console.WriteLine($"{s.Elapsed.TotalMilliseconds:0.00}ms {config.Iterations / config.ForkCount / s.Elapsed.TotalSeconds / 1000:0.00} kI/s/t");
}
if (bestSims.Count == 0)
return new(new(), state);
var result = bestSims.MaxBy(s => s.Score).Result;
for (var i = definiteActionCount; i < result.Actions.Count; ++i)
actionCallback?.Invoke(result.Actions[i]);
return result;
}
private static SolverSolution SearchStepwiseForked(SolverConfig config, SimulationState state, Action<ActionType>? actionCallback, CancellationToken token)
{
var actions = new List<ActionType>();
var sim = new Simulator(state, config.MaxStepCount);
while (true)
{
if (token.IsCancellationRequested)
break;
if (sim.IsComplete)
break;
var s = Stopwatch.StartNew();
var tasks = new Task<(float MaxScore, SolverSolution Solution)>[config.ForkCount];
for (var i = 0; i < config.ForkCount; ++i)
tasks[i] = Task.Run(() =>
{
var solver = new MCTS(new(config), state);
solver.Search(config.Iterations / config.ForkCount, token);
return (solver.MaxScore, solver.Solution());
}, token);
Task.WaitAll(tasks, token);
s.Stop();
if (token.IsCancellationRequested)
break;
var (maxScore, solution) = tasks.Select(t => t.Result).MaxBy(r => r.MaxScore);
if (maxScore >= config.ScoreStorageThreshold)
{
actions.AddRange(solution.Actions);
return solution with { Actions = actions };
}
var chosenAction = solution.Actions[0];
actionCallback?.Invoke(chosenAction);
Console.WriteLine($"{s.Elapsed.TotalMilliseconds:0.00}ms {config.Iterations / config.ForkCount / s.Elapsed.TotalSeconds / 1000:0.00} kI/s/t");
(_, state) = sim.Execute(state, chosenAction);
actions.Add(chosenAction);
}
return new(actions, state);
}
private static SolverSolution SearchStepwise(SolverConfig config, SimulationState state, Action<ActionType>? actionCallback, CancellationToken token)
{
var actions = new List<ActionType>();
var sim = new Simulator(state, config.MaxStepCount);
while (true)
{
if (token.IsCancellationRequested)
break;
if (sim.IsComplete)
break;
var solver = new MCTS(new(config), state);
var s = Stopwatch.StartNew();
solver.Search(config.Iterations, token);
s.Stop();
var solution = solver.Solution();
if (solver.MaxScore >= config.ScoreStorageThreshold)
{
actions.AddRange(solution.Actions);
return solution with { Actions = actions };
}
var chosenAction = solution.Actions[0];
actionCallback?.Invoke(chosenAction);
Console.WriteLine($"{s.Elapsed.TotalMilliseconds:0.00}ms {config.Iterations / s.Elapsed.TotalSeconds / 1000:0.00} kI/s");
(_, state) = sim.Execute(state, chosenAction);
actions.Add(chosenAction);
}
return new(actions, state);
}
private static SolverSolution SearchOneshotForked(SolverConfig config, SimulationState state, Action<ActionType>? actionCallback, CancellationToken token)
{
var tasks = new Task<(float MaxScore, SolverSolution Solution)>[config.ForkCount];
for (var i = 0; i < config.ForkCount; ++i)
tasks[i] = Task.Run(() =>
{
var solver = new MCTS(new(config), state);
solver.Search(config.Iterations / config.ForkCount, token);
return (solver.MaxScore, solver.Solution());
}, token);
Task.WaitAll(tasks, CancellationToken.None);
var solution = tasks.Select(t => t.Result).MaxBy(r => r.MaxScore).Solution;
foreach (var action in solution.Actions)
actionCallback?.Invoke(action);
return solution;
}
private static SolverSolution SearchOneshot(SolverConfig config, SimulationState state, Action<ActionType>? actionCallback, CancellationToken token)
{
var solver = new MCTS(new(config), state);
solver.Search(config.Iterations, token);
var solution = solver.Solution();
foreach (var action in solution.Actions)
actionCallback?.Invoke(action);
return solution;
}
public static SolverSolution Search(SolverConfig config, SimulationState state, Action<ActionType>? actionCallback, CancellationToken token)
{
Func<SolverConfig, SimulationState, Action<ActionType>?, CancellationToken, SolverSolution> func = config.Algorithm switch
{
SolverAlgorithm.Oneshot => SearchOneshot,
SolverAlgorithm.OneshotForked => SearchOneshotForked,
SolverAlgorithm.Stepwise => SearchStepwise,
SolverAlgorithm.StepwiseForked => SearchStepwiseForked,
SolverAlgorithm.StepwiseFurcated => SearchStepwiseFurcated,
_ => throw new ArgumentOutOfRangeException(nameof(config), config, $"Invalid algorithm: {config.Algorithm}")
};
return func(config, state, actionCallback, token);
}
}