/// <param name="candidateNode">The node to be checked for duplication</param>
 /// <returns>True if the candidate node already exists in either the open or closed sets</returns>
 private bool IsDuplicate(Node candidateNode)
 {
     return OpenSet.Concat(ClosedSet)
                 .Where(node => node.FValue == candidateNode.FValue)
                 .Any(node => node.State.SequenceEqual(candidateNode.State));
 }
        /// <param name="goal">The goal node that will be used to trace back up to the initial node</param>
        /// <returns>The list of nodes from goal node to initial node</returns>
        private LinkedList<Node> GetSolutionPath(Node goal)
        {
            if (goal == null) return null;

            var solution = new LinkedList<Node>();
            var current = goal;
            while (current != null)
            {
                _lengthOfSolutionPath++;
                solution.AddFirst(current);
                current = current.BackPtr;
            }
            return solution;
        }
 /// <param name="parent"> The parent node of successor</param>
 /// <param name="prev"> The position of space in the parent </param>
 /// <param name="next"> The position the space is to be moved to</param>
 /// <returns>The successor node resulting from moving the space in the parent state from prev to next </returns>
 private Node CreateSuccessorNode(Node parent, int prev, int next)
 {
     var child = new Node();
     child.State = new List<int>(parent.State);
     child.State[prev] = parent.State[next];
     child.State[next] = parent.State[prev];
     child.FValue = Heuristic(child.State);
     child.BackPtr = parent;
     return child;
 }
        /// <param name="parent">The parent node whose successors will be calculated</param>
        /// <returns>The successor nodes for the given parent</returns>
        private LinkedList<Node> GenerateSuccessors(Node parent)
        {
            var successors = new LinkedList<Node>();
            var sideLength = (int)Math.Sqrt(parent.State.Count);
            var i = parent.State.TakeWhile(value => value != Node.SPACE).Count();

            if (IsMovePossible(i, i - 1, sideLength)) // move space left
                successors.AddFirst(CreateSuccessorNode(parent, i, i - 1));
            if (IsMovePossible(i, i + 1, sideLength)) // move space right
                successors.AddFirst(CreateSuccessorNode(parent, i, i + 1));
            if (IsMovePossible(i, i - sideLength, sideLength)) // move space up
                successors.AddFirst(CreateSuccessorNode(parent, i, i - sideLength));
            if (IsMovePossible(i, i + sideLength, sideLength)) // move space down
                successors.AddFirst(CreateSuccessorNode(parent, i, i + sideLength));

            return successors;
        }
        /// <summary>
        /// Best first search implementation
        /// </summary>
        /// <param name="initialNode">The initial node, representing the puzzle to be solved</param>
        /// <returns>The solution path from inital node to goal node</returns>
        public LinkedList<Node> Search(Node initialNode)
        {
            // Check for valid params
            if (initialNode == null) throw new ArgumentNullException("initialNode");
            if (Heuristic == null)
                throw new NullReferenceException("Heuristic function not specified, this search requires a heuristic");

            var watch = new Stopwatch();
            watch.Start();
            Node goalNode = null;
            var depth = 0;

            // Calculate h(start)
            initialNode.FValue = Heuristic(initialNode.State);

            // Put IS on open
            OpenSet.AddFirst(initialNode);

            // Loop until |open| = 0 or goalState found
            while (OpenSet.Count > 0 && goalNode == null)
            {
                var currentNode = OpenSet.First; // First is the lowest F value, because OpenSet is sorted by F Value
                OpenSet.Remove(currentNode);
                ClosedSet.AddFirst(currentNode);
                var successors = GenerateSuccessors(currentNode.Value);
                _numNodesGenerated += successors.Count;
                AddToOpenSet(successors);
                goalNode = FindGoal(successors);
                depth++;
            }
            _branchingFactor = _numNodesGenerated/depth;
            watch.Stop();
            _executionTime = watch.Elapsed;

            // If goal found then return the solution path, else return null
            return goalNode == null ? null : GetSolutionPath(goalNode);
        }