/// <summary> /// Inserts a node into the priority queue based on Priority value of node /// </summary> /// <param name="value"></param> public void Enqueue(Node value) { if (IsEmpty()) nodes.Add(value); else { bool insert = false; for (int i = 0; i < nodes.Count; i++) { if (value.Priority < nodes[i].Priority) { nodes.Insert(i, value); insert = true; break; } if (nodes[i].Priority == value.Priority) { nodes.Insert(i + 1, value); insert = true; break; } } if (!insert) nodes.Add(value); } }
/// <summary> /// Checks if the Node child is present in the list of nodes /// Returns -1 if not present or returns the index if present /// </summary> /// <param name="nodes"></param> /// <param name="child"></param> /// <returns></returns> public static int FindNode(List<Node> nodes, Node child) { int flag = -1; for (int i = 0; i < nodes.Count; i++) { if (nodes[i].Equals(child)) { flag = i; break; } } return flag; }
/// <summary> /// Main method where the program execution starts /// </summary> /// <param name="args"></param> public static void Main(string[] args) { Node startNode = new Node(); Node goalNode = new Node(); //Prompts the user to enter start state and goal state Console.WriteLine("Enter the start state:"); startNode.State = CheckInput(); Console.WriteLine("Enter the goal state:"); goalNode.State = CheckInput(); startNode.PathCost = 0; startNode.Parent = null; Node bestNode = new Node(); //Create an object of AStarProgram and call the AStar algorithm AStarProgram astar = new AStarProgram(); bestNode = astar.AStar(startNode, goalNode); //Used a stack to display the path of solution Stack<Node> result = new Stack<Node>(); if (bestNode != null) { while (bestNode.Parent != null) { result.Push(bestNode); bestNode = bestNode.Parent; } } else { //if bestnode is null then display Console.WriteLine("Solution does not exist"); } //Print the solution, Number of nodes generated and nodes expanded Console.WriteLine("Number of Nodes in solution path : " + result.Count()); Node tempNode = new Node(); startNode.PrintState(); Console.WriteLine(); while (result.Count != 0) { tempNode = result.Pop(); tempNode.PrintState(); Console.WriteLine(); } Console.WriteLine(); Console.WriteLine(); Console.WriteLine("Number of Nodes generated : " + AStarProgram.noOfNodesGenerated); Console.WriteLine("Number of Nodes Expanded : " + AStarProgram.noOfNodesExpanded); Console.Read(); }
/// <summary> /// Moves the blank space to a desired position /// Returns a child node /// </summary> /// <param name="row"></param> /// <param name="col"></param> /// <param name="flag1"></param> /// <param name="flag2"></param> /// <returns></returns> public Node MoveOperation(int row, int col, bool flag1, bool flag2) { Node successor = new Node(); //Copy the current state into a temporary storage int[] value = new int[State.Length]; for (int i = 0; i < State.Length; i++) { value[i] = this.State[i]; } //Find the required block to move based the bool values flag1 and flag2 int a; if (flag2) a = -1; else a = 1; int row1 = row, col1 = col; if (flag1) row1 = row + a; else col1 = col + a; //set the new Node and return it successor.state = value; successor.State[puzzleSize * row1 + col1] = successor.State[puzzleSize * row + col]; successor.State[puzzleSize * row + col] = 0; return successor; }
/// <summary> /// Finds the h-cost which is the Manhattan distance between the calling Node and parameter Node /// </summary> /// <param name="goalNode"></param> /// <returns></returns> public int HCost(Node goalNode) { int distance = 0; for (int i = 0; i < this.State.Length; i++) { for (int j = 0; j < this.State.Length; j++) { if (this.State[i] != 0 && this.State[i] == goalNode.State[j]) { int x1 = (i) / puzzleSize; int y1 = (i) % puzzleSize; int x2 = j / puzzleSize; int y2 = j % puzzleSize; distance += Math.Abs(x1 - x2) + Math.Abs(y1 - y2); } } } return distance; }
/// <summary> /// Compares the states of two Nodes and returns true if they are same /// </summary> /// <param name="current"></param> /// <returns></returns> public bool Equals(Node current) { for (int i = 0; i < this.State.Length; i++) { if (this.State[i] != current.State[i]) return false; } return true; }
/// <summary> /// Implementation of A* Algorithm /// Takes the start node and goal node as input and returns the node which matches the goal node /// </summary> /// <param name="startNode"></param> /// <param name="goalNode"></param> /// <returns></returns> public Node AStar(Node startNode, Node goalNode) { Node INIT = startNode; //Create a list to track the visited nodes List<Node> visitedNodes = new List<Node>(); //Create a Queue for the open nodes AlgoImpl.PriorityQueue queue = new AlgoImpl.PriorityQueue(); //Add the start node to the queue INIT.Priority = INIT.HCost(goalNode) + INIT.PathCost; queue.Enqueue(INIT); //Check the queue for nodes, if empty then return null while (queue.nodes.Count != 0) { //Get the best node with minimum f value from the queue Node current = queue.Dequeue(); //Add the node to visited nodes visitedNodes.Add(current); //Check if the node is goal node and return the node if true else expand its child nodes if (current.Equals(goalNode)) { return current; } else { //Increment the expanded nodes global variable ++noOfNodesExpanded; //Generated the successor nodes List<Node> childNodes = current.ChildrenNodes(); //Traverse the nodes foreach (Node successor in childNodes) { int distanceFromNewNode = successor.HCost(goalNode); //Compute the estimated g-cost of the successor int estGCost = current.GCost() + Math.Abs(distanceFromNewNode - current.HCost(goalNode)); int findVisited = FindNode(visitedNodes, successor); //check if the successor is already visited or not, if visted then discard it if (findVisited == -1) { //check if the successor is already in the queue int found = FindNode(queue.nodes, successor); //if true if (found != -1) { //if f cost of old node is greater than f cost of successor node, delete it from the queue //else discard the successor node if ((queue.nodes.ElementAt(found).PathCost > estGCost + distanceFromNewNode)) queue.Delete(found); else continue; } //Add the node to the queue and set its parent to curren successor.Parent = current; successor.PathCost = estGCost; successor.Priority = successor.PathCost + distanceFromNewNode; queue.Enqueue(successor); ++noOfNodesGenerated; } else { if (estGCost < successor.GCost()) { visitedNodes.RemoveAt(findVisited); continue; } } } } if (queue.nodes.Count == 100000) break; } return null; }