public void Add(SolverNode kid) { lock (locker) { if (HasChildren) { foreach (var existing in Children) { if (kid.Equals(existing)) { throw new InvalidDataException("Dup"); } } } ((SolverNodeTreeFeatures)kid).Parent = (SolverNode)this; if (firstChild == null) { firstChild = kid; } else { var next = firstChild; while (next.nextSibling != null) { next = next.nextSibling; } next.nextSibling = kid; } } }
public void InitialiseInstance(SolverNode parent, VectorInt2 playerBefore, VectorInt2 push, IBitmap crateMap, IBitmap moveMap) { base.Parent = parent; base.Clear(); // Check init/use should have a NEW id to avoid same-ref bugs; it is effectively a new instance solverNodeId = Interlocked.Increment(ref nextId); this.playerBefore = new VectorByte2(playerBefore); this.push = push switch { (0, 0) => (byte)0, (0, -1) => (byte)1, (0, 1) => (byte)2, (-1, 0) => (byte)3, (1, 0) => (byte)4, _ => throw new ArgumentOutOfRangeException(push.ToString()) }; this.crateMap = crateMap; this.moveMap = moveMap; this.status = (byte)SolverNodeStatus.UnEval; unchecked { var hashCrate = CrateMap.GetHashCode(); var hashMove = MoveMap.GetHashCode(); #if NET47 hash = hashCrate ^ (hashMove << (MoveMap.Width / 2)); #else hash = HashCode.Combine(hashCrate, hashMove); #endif } }
public SolverNode CreateFromPull( SolverNode parent, IBitmap nodeCrateMap, IBitmap walls, VectorInt2 pc, VectorInt2 p, VectorInt2 pp) { if (TryGetPooledInstance(out var fromPool)) { var eCrate = fromPool.CrateMap; var eMove = fromPool.MoveMap; eMove.Fill(false); // Reuse the nodes, resetting the values eCrate.Set(nodeCrateMap); eCrate[pc] = false; eCrate[p] = true; SolverHelper.FloodFillUsingWallAndCratesInline(walls, eCrate, pp, eMove); fromPool.InitialiseInstance(parent, p, pp - p, eCrate, eMove, true); return(fromPool); } var newCrate = CreateBitmap(nodeCrateMap); newCrate[pc] = false; newCrate[p] = true; var newMove = CreateBitmap(nodeCrateMap.Size); SolverHelper.FloodFillUsingWallAndCratesInline(walls, newCrate, pp, newMove); return(CreateInstance(parent, p, pp - p, newCrate, newMove)); }
protected SolverNode?ConfirmDupLookup(ISolverPool pool, SolverNode node, List <SolverNode> toEnqueue, SolverNode newKid) { if (SafeMode) { var root = node.Root(); foreach (var nn in root.Recurse()) { if (nn.Equals(newKid)) { if (nn.CompareTo(newKid) != 0) { throw new InvalidOperationException(); } var sizes = $"Tree:{root.CountRecursive()} vs. Pool:{pool.Statistics.TotalNodes}"; var shouldExist = pool.FindMatch(nn); var shoudNotBeFound_ButWeWantItToBe = pool.FindMatch(newKid); var message = $"This is an indication the Pool is not threadsafe/or has a bad binarySearch\n" + $"{sizes}\n" + $"Dup:{toEnqueue.Count()}: ({nn}; pool={shouldExist}) <-> ({newKid}) != {shoudNotBeFound_ButWeWantItToBe} [{pool.TypeDescriptor}]"; //throw new Exception(message); return(nn); } } return(null); } return(null); }
public override bool Evaluate( SolverState state, ISolverQueue queue, ISolverPool pool, ISolverPool solutionPool, SolverNode node) { if (node.HasChildren) { throw new InvalidOperationException(); } node.Status = SolverNodeStatus.Evaluting; var toEnqueue = new List <SolverNode>(); // TODO: Could be reused var toPool = new List <SolverNode>(); // TODO: Could be reused var solution = false; foreach (var move in node.MoveMap.TruePositions()) { foreach (var dir in VectorInt2.Directions) { var p = move; var pp = p + dir; var ppp = pp + dir; if (node.CrateMap[pp] && // crate to push state.StaticMaps.FloorMap[ppp] && !node.CrateMap[ppp] && // into free space? !state.StaticMaps.DeadMap[ppp]) // Valid Push { EvaluateValidPush(state, pool, solutionPool, node, pp, ppp, p, dir, toEnqueue, toPool, ref solution); } } } if (solution) { node.Status = SolverNodeStatus.SolutionPath; } else if (node.HasChildren) { node.Status = SolverNodeStatus.Evaluted; state.Statistics.TotalDead += node.CheckDead(); // Children may be evaluated as dead already } else { node.Status = SolverNodeStatus.Dead; state.Statistics.TotalDead++; if (node.Parent != null) { state.Statistics.TotalDead += node.Parent.CheckDead(); } } queue.Enqueue(toEnqueue); pool.Add(toPool); return(solution); }
public bool Evaluate(SolverState state, ISolverQueue queue, ISolverPool myPool, ISolverPool solutionPool, SolverNode node) { if (node.HasChildren) { throw new InvalidOperationException(); } node.Status = SolverNodeStatus.Evaluting; var solution = false; var toEnqueue = new List <SolverNode>(); foreach (var move in node.MoveMap.TruePositions()) { foreach (var dir in VectorInt2.Directions) { var p = move; var pc = p + dir; var pp = p - dir; if (node.CrateMap[pc] && // crate to push state.StaticMaps.FloorMap[pp] && !node.CrateMap[p] && !CheckDeadReverse(state, pp)) { EvaluateValidPull(state, myPool, solutionPool, node, pc, p, pp, toEnqueue, ref solution); } } } if (solution) { node.Status = SolverNodeStatus.SolutionPath; } else if (node.HasChildren) { node.Status = SolverNodeStatus.Evaluted; state.Statistics.TotalDead += node.CheckDead(); // Children may be evaluated as dead already } else { node.Status = SolverNodeStatus.Dead; state.Statistics.TotalDead++; if (node.Parent != null) { state.Statistics.TotalDead += node.Parent.CheckDead(); } } queue.Enqueue(toEnqueue); myPool.Add(toEnqueue); return(solution); }
public override void ReturnInstance(SolverNode canBeReused) { if (pool.Count < MaxPool) { pool.Add(canBeReused); } else { refused++; } }
private bool CheckValidSolutions(SolverState state, SolverNode posibleSolution) { var b = state.StaticMaps.WallMap.BitwiseOR(state.StaticMaps.CrateStart); var f = state.Command.Puzzle.Player.Position; var path = posibleSolution.PathToRoot(); path.Reverse(); var start = path[0]; var t = start.PlayerAfter; var first = PathFinder.Find(b, f, t); return(first != null); }
public override SolverNode CreateInstance(SolverNode parent, VectorInt2 player, VectorInt2 push, IBitmap crateMap, IBitmap moveMap) { if (pool.Count > 0) { if (pool.TryTake(out var inst)) { hit++; inst.InitialiseInstance(parent, player, push, crateMap, moveMap, true); return(inst); } } miss++; return(new SolverNode(parent, player, push, crateMap, moveMap)); }
public override bool TryGetPooledInstance(out SolverNode node) { if (pool.Count > 0) { if (pool.TryTake(out var inst)) { hit++; node = inst; return(true); } } node = null; return(false); }
public override void ReturnInstance(SolverNode canBeReused) { while (readSpinLock) { } var next = Interlocked.Increment(ref bufferIndex); if (next >= 0 && next < MaxPool) { buffer[next] = canBeReused; } else { Interlocked.Increment(ref refused); } }
public static List <DepthLineItem> ReportDepth(SolverNode root) { var res = new List <DepthLineItem>(); foreach (var n in root.Recurse()) { var d = n.GetDepth(); while (res.Count <= d) { var dd = new DepthLineItem(); res.Add(dd); dd.Depth = res.IndexOf(dd); // safer } var line = res[d]; line.Total++; if (n.IsClosed) { line.Closed++; } if (n.Status == SolverNodeStatus.UnEval) { line.UnEval++; line.LastUnEval = n; } if (n.Status == SolverNodeStatus.Duplicate) { line.Dups++; } line.Last = n; } foreach ((DepthLineItem a, DepthLineItem b) in res.PairUp()) { if (b != null) { b.GrowthRate = (float)b.Total / (float)a.Total; } } return(res); }
public override SolverNode CreateInstance(SolverNode parent, VectorInt2 player, VectorInt2 push, IBitmap crateMap, IBitmap moveMap) { while (readSpinLock) { } readSpinLock = true; try { var h = Interlocked.Decrement(ref bufferIndex); if (h < 0) { bufferIndex = -1; // reset to start state (empty buffer) return(new SolverNode(parent, player, push, crateMap, moveMap)); } else { if (h >= MaxPool - 1) { return(new SolverNode(parent, player, push, crateMap, moveMap)); } if (buffer[h] == null) { // Thread contention? // Could try again, but that may cause StackOverflow, // So safer to just issue a new obj return(new SolverNode(parent, player, push, crateMap, moveMap)); } var reuse = buffer[h]; reuse.InitialiseInstance(parent, player, push, crateMap, moveMap, true); buffer[h] = null; return(reuse); } } finally { readSpinLock = false; } }
private void NewSolutionChain(SolverState state, out bool solution, SolverNode newKid, SolverNode match) { solution = true; state.SolutionsNodesReverse ??= new List <SolutionChain>(); var pair = new SolutionChain { ForwardNode = newKid, ReverseNode = match, FoundUsing = this }; state.SolutionsNodesReverse.Add(pair); state.Command.Debug.Raise(this, SolverDebug.Solution, pair); foreach (var n in newKid.PathToRoot().Union(match.PathToRoot())) { n.Status = SolverNodeStatus.SolutionPath; } newKid.Status = SolverNodeStatus.Solution; match.Status = SolverNodeStatus.Solution; }
public static Path ConvertForwardNodeToPath(SolverNode node, IBitmap walls) { var pathToRoot = node.PathToRoot(); var offset = GeneralHelper.OffsetWalk(pathToRoot); var r = new Path(); foreach (var pair in offset) { var boundry = walls.BitwiseOR(pair.Item1.CrateMap); var start = pair.Item1.PlayerAfter; var end = pair.Item2.PlayerBefore; var walk = PathFinder.Find(boundry, start, end); if (walk == null) { throw new Exception("bad Path\n"); // Not solution } r.AddRange(walk); r.Add(pair.Item2.PlayerAfter - pair.Item2.PlayerBefore); } return(r); }
public static Path ConvertReverseNodeToPath(SolverNode node, IBitmap walls) { var pathToRoot = node.PathToRoot(); pathToRoot.Reverse(); pathToRoot.RemoveAt(pathToRoot.Count - 1); var offset = GeneralHelper.OffsetWalk(pathToRoot); var r = new Path(); var cc = 0; foreach (var pair in offset) { r.Add(pair.Item1.PlayerBefore - pair.Item1.PlayerAfter); var boundry = walls.BitwiseOR(pair.Item2.CrateMap); var start = pair.Item1.PlayerBefore; var end = pair.Item2.PlayerAfter; var walk = PathFinder.Find(boundry, start, end); if (walk == null) { throw new Exception(string.Format("Bad Path at {0}, path={1}", cc, r)); // Not solution } r.AddRange(walk); cc++; } if (pathToRoot.Count > 1) { var last = pathToRoot[pathToRoot.Count - 1]; r.Add(last.CrateBefore - last.CrateAfter); } return(r); }
public override SolverNode CreateInstance(SolverNode parent, VectorInt2 player, VectorInt2 push, IBitmap crateMap, IBitmap moveMap) => new SolverNode(parent, player, push, crateMap, moveMap);
private bool EvaluateValidPull( SolverState state, ISolverPool pool, ISolverPool solutionPool, SolverNode node, VectorInt2 pc, VectorInt2 p, VectorInt2 pp, List <SolverNode> toEnqueue, List <SolverNode> toPool, ref bool solution) { state.Statistics.TotalNodes++; var newKid = nodeFactory.CreateFromPull(node, node.CrateMap, state.StaticMaps.WallMap, pc, p, pp); if (state.Command.Inspector != null && state.Command.Inspector(newKid)) { state.Command.Report?.WriteLine(newKid.ToString()); } // Cycle Check: Does this node exist already? var dup = pool.FindMatch(newKid); if (SafeMode && dup == null) { dup = ConfirmDupLookup(pool, node, toEnqueue, newKid); // Fix or Throw } if (dup != null) { if (object.ReferenceEquals(dup, newKid)) { throw new InvalidDataException(); } if (dup.SolverNodeId == newKid.SolverNodeId) { throw new InvalidDataException(); } // Duplicate newKid.Status = SolverNodeStatus.Duplicate; state.Statistics.Duplicates++; if (state.Command.DuplicateMode == DuplicateMode.AddAsChild) { node.Add(newKid); if (newKid is ISolverNodeDuplicateLink dupLink) { dupLink.Duplicate = dup; } } else if (state.Command.DuplicateMode == DuplicateMode.ReuseInPool) { nodeFactory.ReturnInstance(newKid); // Add to pool for later re-use? } else // DuplicateMode.Discard { } } else { // These two should always be the same node.Add(newKid); toPool.Add(newKid); // If there is a reverse solver, checks its pool for a match, hence a Forward <-> Reverse chain, hence a solution var match = solutionPool?.FindMatch(newKid); if (match != null) { // Solution state.SolutionsNodesReverse ??= new List <SolutionChain>(); var pair = new SolutionChain { ForwardNode = match, ReverseNode = newKid, FoundUsing = this }; state.SolutionsNodesReverse.Add(pair); solution = true; state.Command.Debug.Raise(this, SolverDebug.Solution, pair); foreach (var n in newKid.PathToRoot().Union(match.PathToRoot())) { n.Status = SolverNodeStatus.SolutionPath; } newKid.Status = SolverNodeStatus.Solution; match.Status = SolverNodeStatus.Solution; if (state.Command.ExitConditions.StopOnSolution) { return(true); } } else { if (DeadMapAnalysis.DynamicCheck(state.StaticMaps, node)) { newKid.Status = SolverNodeStatus.Dead; } else { toEnqueue.Add(newKid); if (newKid.IsSolutionReverse(state.StaticMaps)) { // Possible Solution: Did we start in a valid position if (CheckValidSolutions(state, newKid)) { state.SolutionsNodes.Add(newKid); state.Command.Debug.Raise(this, SolverDebug.Solution, newKid); solution = true; foreach (var n in newKid.PathToRoot()) { n.Status = SolverNodeStatus.SolutionPath; } newKid.Status = SolverNodeStatus.Solution; } else { // We started in the wrong place; ignore and continue } } } } } return(false); }
public TreeQueue(SolverNode start, IDebugEventPublisher report) : this(new[] { start }, report) { }
public virtual bool TryGetPooledInstance(out SolverNode node) { node = null; return(false); }
public abstract bool Evaluate(SolverState state, ISolverQueue queue, ISolverPool pool, ISolverPool solutionPool, SolverNode node);
public abstract void ReturnInstance(SolverNode canBeReused);
public abstract SolverNode CreateInstance(SolverNode parent, VectorInt2 player, VectorInt2 push, IBitmap crateMap, IBitmap moveMap);
protected void InitialiseInstance(SolverNode parent) { Parent = parent; firstChild = null; nextSibling = null; }
private bool EvaluateValidPull( SolverState state, ISolverPool myPool, ISolverPool solutionPool, SolverNode node, VectorInt2 pc, VectorInt2 p, VectorInt2 pp, List <SolverNode> toEnqueue, ref bool solution) { state.Statistics.TotalNodes++; var newKid = nodeFactory.CreateFromPull(node, node.CrateMap, state.StaticMaps.WallMap, pc, p, pp); // Cycle Check: Does this node exist already? var dup = myPool.FindMatch(newKid); if (dup != null) { if (object.ReferenceEquals(dup, newKid)) { throw new InvalidDataException(); } if (dup.SolverNodeId == newKid.SolverNodeId) { throw new InvalidDataException(); } // Duplicate newKid.Status = SolverNodeStatus.Duplicate; state.Statistics.Duplicates++; if (state.Command.DuplicateMode == DuplicateMode.AddAsChild) { node.Add(newKid); newKid.Duplicate = dup; } else if (state.Command.DuplicateMode == DuplicateMode.ReuseInPool) { nodeFactory.ReturnInstance(newKid); // Add to pool for later re-use? } else // DuplicateMode.Discard { } } else { var match = solutionPool?.FindMatch(newKid); if (match != null) { // Add to tree / itterator node.Add(newKid); // Solution state.SolutionsNodesReverse ??= new List <SolutionChain>(); var pair = new SolutionChain { ForwardNode = match, ReverseNode = newKid, FoundUsing = this }; state.SolutionsNodesReverse.Add(pair); solution = true; state.Command.Debug.Raise(this, SolverDebug.Solution, pair); foreach (var n in newKid.PathToRoot().Union(match.PathToRoot())) { n.Status = SolverNodeStatus.SolutionPath; } newKid.Status = SolverNodeStatus.Solution; match.Status = SolverNodeStatus.Solution; if (state.Command.ExitConditions.StopOnSolution) { return(true); } } else { // Add to tree / iterator node.Add(newKid); // Thread-safe: As all kids get created in this method (forward / reverse) if (DeadMapAnalysis.DynamicCheck(state.StaticMaps, node)) { newKid.Status = SolverNodeStatus.Dead; } else { toEnqueue.Add(newKid); if (newKid.IsSolutionReverse(state.StaticMaps)) { // Possible Solution: Did we start in a valid position if (CheckValidSolutions(state, newKid)) { state.SolutionsNodes.Add(newKid); state.Command.Debug.Raise(this, SolverDebug.Solution, newKid); solution = true; foreach (var n in newKid.PathToRoot()) { n.Status = SolverNodeStatus.SolutionPath; } newKid.Status = SolverNodeStatus.Solution; } else { // We started in the wrong place; ignore and continue } } } } } return(false); }
public static Path ConvertSolutionNodeToPath(SolverNode node, IBitmap walls, Puzzle puzzle) { if (node.Evaluator.GetType() == typeof(ReverseEvaluator)) { var pathToRoot = node.PathToRoot(); pathToRoot.Reverse(); var offset = GeneralHelper.OffsetWalk(pathToRoot).ToList(); var r = new Path(); var cc = 0; // PuzzleStart to First Push var b = walls.BitwiseOR(puzzle.ToMap(puzzle.Definition.AllCrates)); var f = puzzle.Player.Position; var t = pathToRoot[0].PlayerAfter; var first = PathFinder.Find(b, f, t); if (first == null) { //throw new Exception(string.Format("Bad Path at INIT. {0} => {1}. This is an indicator or a FALSE positive. Ie. An invalid start position.\n{2}", f, t, b)); // Not solution return(null); } r.AddRange(first); foreach (var pair in offset) { if (pair.Item2.Parent == null) { break; } r.Add(pair.Item1.PlayerBefore - pair.Item1.PlayerAfter); var boundry = walls.BitwiseOR(pair.Item2.CrateMap); var start = pair.Item1.PlayerBefore; var end = pair.Item2.PlayerAfter; var walk = PathFinder.Find(boundry, start, end); if (walk == null) { throw new Exception($"Bad Path at step {cc}\n"); // Not solution } r.AddRange(walk); //Console.WriteLine("PAIR: {0}", cc); //Console.WriteLine("{0} => {1}", pair.Item1.PlayerBefore, pair.Item1.PlayerAfter); //Console.WriteLine(pair.Item1.ToStringDebug()); //Console.WriteLine("{0} => {1}", pair.Item2.PlayerBefore, pair.Item2.PlayerAfter); //Console.WriteLine(pair.Item2.ToStringDebug()); //var debug = boundry.ToCharMap(); //debug[start] = 'S'; //debug[end] = 'E'; //var d = debug.ToString(); //Console.WriteLine(d); //Console.WriteLine(r); cc++; } if (pathToRoot.Count > 1) { var last = pathToRoot[pathToRoot.Count - 2]; r.Add(last.PlayerBefore - last.PlayerAfter); } return(r); } return(ConvertForwardNodeToPath(node, walls)); }
public override void ReturnInstance(SolverNode canBeReused) { // Do Nothing }
private bool EvaluateValidPush( SolverState state, ISolverPool pool, ISolverPool reversePool, SolverNode node, VectorInt2 pp, VectorInt2 ppp, VectorInt2 p, VectorInt2 push, List <SolverNode> toEnqueue, ref bool solution) { state.Statistics.TotalNodes++; var newKid = nodeFactory.CreateFromPush(node, node.CrateMap, state.StaticMaps.WallMap, p, pp, ppp, push); // Cycle Check: Does this node exist already? var dup = pool.FindMatch(newKid); if (dup != null) { if (object.ReferenceEquals(dup, newKid)) { throw new InvalidDataException(); } if (dup.SolverNodeId == newKid.SolverNodeId) { throw new InvalidDataException(); } // Duplicate newKid.Status = SolverNodeStatus.Duplicate; state.Statistics.Duplicates++; if (state.Command.DuplicateMode == DuplicateMode.AddAsChild) { node.Add(newKid); newKid.Duplicate = dup; } else if (state.Command.DuplicateMode == DuplicateMode.ReuseInPool) { nodeFactory.ReturnInstance(newKid); // Add to pool for later re-use? } else // DuplicateMode.Discard { } } else { node.Add(newKid); // If there is a reverse solver, checks its pool for a match, hence a Forward <-> Reverse chain, hence a solution var match = reversePool?.FindMatch(newKid); if (match != null) { // Solution! NewSolutionChain(state, out solution, newKid, match); if (state.Command.ExitConditions.StopOnSolution) { return(true); } } else { if (DeadMapAnalysis.DynamicCheck(state.StaticMaps, node)) { newKid.Status = SolverNodeStatus.Dead; state.Statistics.TotalDead++; } else { toEnqueue.Add(newKid); if (newKid.IsSolutionForward(state.StaticMaps)) { // Solution solution = true; state.SolutionsNodes.Add(newKid); state.Command.Debug.Raise(this, SolverDebug.Solution, newKid); foreach (var n in newKid.PathToRoot()) { n.Status = SolverNodeStatus.SolutionPath; } newKid.Status = SolverNodeStatus.Solution; if (state.Command.ExitConditions.StopOnSolution) { return(true); } } } } } return(false); }