/// <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); }