예제 #1
0
        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;
                }
            }
        }
예제 #2
0
        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
            }
        }
예제 #3
0
        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));
        }
예제 #4
0
        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);
        }
예제 #5
0
        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);
        }
예제 #6
0
        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++;
     }
 }
예제 #8
0
        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);
            }
        }
예제 #12
0
        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;
            }
        }
예제 #14
0
        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;
        }
예제 #15
0
        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);
        }
예제 #16
0
        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);
예제 #18
0
        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);
        }
예제 #19
0
 public TreeQueue(SolverNode start, IDebugEventPublisher report) : this(new[] { start }, report)
 {
 }
예제 #20
0
 public virtual bool TryGetPooledInstance(out SolverNode node)
 {
     node = null;
     return(false);
 }
예제 #21
0
 public abstract bool Evaluate(SolverState state, ISolverQueue queue, ISolverPool pool, ISolverPool solutionPool, SolverNode node);
예제 #22
0
 public abstract void       ReturnInstance(SolverNode canBeReused);
예제 #23
0
 public abstract SolverNode CreateInstance(SolverNode parent, VectorInt2 player, VectorInt2 push, IBitmap crateMap, IBitmap moveMap);
예제 #24
0
 protected void InitialiseInstance(SolverNode parent)
 {
     Parent      = parent;
     firstChild  = null;
     nextSibling = null;
 }
예제 #25
0
        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);
        }
예제 #26
0
        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
 }
예제 #28
0
        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);
        }