diff --git a/Benchmark/Craftimizer.Benchmark.csproj b/Benchmark/Craftimizer.Benchmark.csproj index 1fc9825..f4fbfb0 100644 --- a/Benchmark/Craftimizer.Benchmark.csproj +++ b/Benchmark/Craftimizer.Benchmark.csproj @@ -9,7 +9,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Benchmark/Program.cs b/Benchmark/Program.cs index 9cee854..c4442d0 100644 --- a/Benchmark/Program.cs +++ b/Benchmark/Program.cs @@ -45,10 +45,11 @@ internal static class Program 0 ); + var threads = 1; var config = new SolverConfig() { - Iterations = 30_000 / 1, - ThreadCount = 1, + Iterations = 30_000 / threads, + ThreadCount = threads, }; Debugger.Break(); diff --git a/Craftimizer/Craftimizer.csproj b/Craftimizer/Craftimizer.csproj index 930b9fc..a2ad4da 100644 --- a/Craftimizer/Craftimizer.csproj +++ b/Craftimizer/Craftimizer.csproj @@ -28,7 +28,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Craftimizer/packages.lock.json b/Craftimizer/packages.lock.json index 5dc997b..aae1cef 100644 --- a/Craftimizer/packages.lock.json +++ b/Craftimizer/packages.lock.json @@ -10,9 +10,9 @@ }, "Meziantou.Analyzer": { "type": "Direct", - "requested": "[2.0.61, )", - "resolved": "2.0.61", - "contentHash": "DhiEScqTxQb8R7s9EWMjs5F5EA7AD+JO5upb88QqPwPQUsAOm2gN5AjeQon3XLrquw1G5r+C9Yxct+rFxwuMZg==" + "requested": "[2.0.62, )", + "resolved": "2.0.62", + "contentHash": "uG2CiDIm97q8KrUt8B34WdElpEDDLOe4YzrLWpwlQmesXrSX2WuJZ+HwIGWrJgDBBMi2a3tVjeF8oKjV+AhUdA==" }, "craftimizer.simulator": { "type": "Project" diff --git a/Simulator/Craftimizer.Simulator.csproj b/Simulator/Craftimizer.Simulator.csproj index b1f8f15..a921291 100644 --- a/Simulator/Craftimizer.Simulator.csproj +++ b/Simulator/Craftimizer.Simulator.csproj @@ -7,7 +7,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Solver/Craftimizer.Solver.csproj b/Solver/Craftimizer.Solver.csproj index f50b072..dd73ca2 100644 --- a/Solver/Craftimizer.Solver.csproj +++ b/Solver/Craftimizer.Solver.csproj @@ -8,7 +8,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Solver/Crafty/ActionSet.cs b/Solver/Crafty/ActionSet.cs index 8494fe6..b558030 100644 --- a/Solver/Crafty/ActionSet.cs +++ b/Solver/Crafty/ActionSet.cs @@ -1,4 +1,5 @@ using Craftimizer.Simulator.Actions; +using System; using System.Diagnostics.Contracts; using System.Numerics; using System.Runtime.CompilerServices; @@ -54,6 +55,54 @@ public struct ActionSet //public readonly ActionType SelectRandom(Random random) => ElementAt(random.Next(Count)); public readonly ActionType SelectRandom(Random random) => First(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ActionType? PopRandom(Random random) => PopFirst(); + /*public ActionType? PopRandom(Random random) + { + uint snapshot; + uint newValue; + ActionType action; + do + { + snapshot = bits; + if (snapshot == 0) + return null; + + var count = BitOperations.PopCount(snapshot); + var index = random.Next(count); + + action = ToAction(Intrinsics.NthBitSet(snapshot, index) - 1); + newValue = snapshot & ~ToMask(action); + } + while (Interlocked.CompareExchange(ref bits, newValue, snapshot) != snapshot); + return action; + }*/ + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ActionType? PopFirst() + { + uint snapshot; + uint newValue; + ActionType action; + int i = 0; + do + { + ++i; + snapshot = bits; + if (snapshot == 0) + return null; + + var index = 0; + + action = ToAction(Intrinsics.NthBitSet(snapshot, index) - 1); + newValue = snapshot & ~ToMask(action); + } + while (Interlocked.CompareExchange(ref bits, newValue, snapshot) != snapshot); + //if (i != 1) + //Console.WriteLine($"Retried {i-1} times"); + return action; + } + [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly ActionType First() => ElementAt(0); diff --git a/Solver/Crafty/Solver.cs b/Solver/Crafty/Solver.cs index 6ff0a70..3bed82d 100644 --- a/Solver/Crafty/Solver.cs +++ b/Solver/Crafty/Solver.cs @@ -19,7 +19,7 @@ public class Solver public Solver(SolverConfig config, SimulationState state, bool strict) { Config = config; - Simulator sim = new(state, config.MaxStepCount); + var sim = new Simulator(state, config.MaxStepCount); RootNode = new(new( state, null, @@ -98,7 +98,12 @@ public class Solver [MethodImpl(MethodImplOptions.AggressiveInlining)] private Node EvalBestChild(float parentVisits, ReadOnlySpan children) { - var length = children.Length; + if (parentVisits == 0) + { + Console.WriteLine("no visits"); + return null; + } + var vecLength = Vector.Count; var C = MathF.Sqrt(Config.ExplorationConstant * MathF.Log(parentVisits)); @@ -152,30 +157,28 @@ public class Solver var node = RootNode; while (true) { - if (!Monitor.TryEnter(node, 5)) - return Select(); - var expandable = node.State.AvailableActions.Count != 0; + var expandable = !node.State.AvailableActions.IsEmpty; var likelyTerminal = node.Children.Count == 0; if (expandable || likelyTerminal) return node; // select the node with the highest score - var n = EvalBestChild(node.State.Scores.Visits, CollectionsMarshal.AsSpan(node.Children)); - Monitor.Exit(node); - node = n; + // if null (current node is invalid & not backpropagated just yet), try again from root + node = EvalBestChild(node.State.Scores.Visits, node.Children) ?? RootNode; } } - public (Node ExpandedNode, CompletionState State, float Score) ExpandAndRollout(Simulator simulator, Node initialNode) + public (Node ExpandedNode, float Score)? ExpandAndRollout(Simulator simulator, Node initialNode) { ref var initialState = ref initialNode.State; // expand once if (initialState.IsComplete) - return (initialNode, initialState.CompletionState, initialState.CalculateScore(Config.MaxStepCount) ?? 0); + return (initialNode, initialState.CalculateScore(Config.MaxStepCount) ?? 0); - var randomAction = initialState.AvailableActions.SelectRandom(Random); - initialState.AvailableActions.RemoveAction(randomAction); - var expandedNode = initialNode.Add(Execute(simulator, initialState.State, randomAction, true)); + var poppedAction = initialState.AvailableActions.PopRandom(Random); + if (!poppedAction.HasValue) + return null; + var expandedNode = initialNode.Add(Execute(simulator, initialState.State, poppedAction.Value, true)); // playout to a terminal state var currentState = expandedNode.State.State; @@ -188,9 +191,9 @@ public class Solver { if (SimulationNode.GetCompletionState(currentCompletionState, currentActions) != CompletionState.Incomplete) break; - randomAction = currentActions.SelectRandom(Random); - actions[actionCount++] = randomAction; - (_, currentState) = simulator.Execute(currentState, randomAction); + var nextAction = currentActions.SelectRandom(Random); + actions[actionCount++] = nextAction; + (_, currentState) = simulator.Execute(currentState, nextAction); currentCompletionState = simulator.CompletionState; currentActions = simulator.AvailableActionsHeuristic(true); } @@ -202,10 +205,10 @@ public class Solver if (score >= Config.ScoreStorageThreshold && score >= RootNode.State.Scores.MaxScore) { (var terminalNode, _) = ExecuteActions(simulator, expandedNode, actions[..actionCount], true); - return (terminalNode, currentCompletionState, score); + return (terminalNode, score); } } - return (expandedNode, currentCompletionState, score); + return (expandedNode, score); } public void Backpropagate(Node startNode, float score) @@ -230,9 +233,17 @@ public class Solver break; var selectedNode = Select(); - var (endNode, _, score) = ExpandAndRollout(simulator, selectedNode); - Monitor.Exit(selectedNode); + var rolledOut = ExpandAndRollout(simulator, selectedNode); + //Monitor.Exit(selectedNode); + if (!rolledOut.HasValue) + { + Console.WriteLine("Retry"); + // Retry, count this iteration as moot + i--; + continue; + } + var (endNode, score) = rolledOut.Value; Backpropagate(endNode, score); } }