public Map(int width, int height, T value) { this.Width = width; this.Height = height; data = new T[width, height]; keys = new BinaryMatrix(width, height); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { data[i, j] = value; } } }
// The following code is based on the pseudocode on Wikipedia for A* search algorithm // https://en.wikipedia.org/wiki/A*_search_algorithm#Pseudocode // It's been modified by Dylan Liu for the purpose of this exercise and for better performance. // For example, in the peseudocode, gScore and fScore calculation is done after the neighbor is added to the openSet. // This requires passing a reference of a node to the openSet instead of an instance, so that its fScore can later be modified. // Dylan Liu moved the fScore and gScore calculation ahead of adding the neighbor to the open set to avoid passing reference, // because it is uncommon and negatively affect performance to pass reference in C#. public bool SearchSolution(int solutionLineThickness = 1) { if (sourceImage == null) { Console.WriteLine("Source image: " + sourceImageName + " is not loaded."); return(false); } // find the start and goal pixel from the source image var success = FindStartNGoal(); if (!success) { return(false); } // The set of nodes already evaluated var closedSet = new BinaryMatrix(Width, Height); // The set of currently discovered nodes that are not evaluated yet. var openSet = new BinaryMatrix(Width, Height); // A priority queue to remove the node with th lowest score var queue = new PriorityQueue <Node>(); // For each node, which node it can most efficiently be reached from. // If a node can be reached from many nodes, cameFrom will eventually contain the // most efficient previous step. var cameFrom = new Map <Vector2D>(Width, Height); // For each node, the cost of getting from the start node to that node. var gScore = new Map <double>(Width, Height, double.PositiveInfinity); // The cost of going from start to start is zero. gScore[start] = 0; // For each node, the total cost of getting from the start node to the goal // by passing by that node. That value is partly known, partly heuristic. var fScore = new Map <double>(Width, Height, double.PositiveInfinity); // Add the start into the queue and openSet var startNode = new Node(start, gScore[start] + HeuristicCostEstimate(start, goal)); queue.Enqueue(startNode); openSet[start] = 1; while (!queue.IsEmpty()) { #region Code for showing search progress and debug purposes count++; if (isDebugOn) { percentage = count * 100 / (Width * Height); //sourceImage.DrawPoint(current, Color.Yellow); // draw all the pixels that haven been visited if (percentage - prev_percentage > 1) { Console.WriteLine(percentage.ToString() + "% of the total pixels were processed."); prev_percentage = percentage; } } if (count > Width * Height) // It's impossible to visit all the pixels and yet still no solution. { Console.WriteLine("Pathfinding is terminated due to either repeat visit to the same pixels."); return(false); } #endregion Vector2D current = (queue.Dequeue()).Vector2D; openSet[current] = 0; closedSet[current] = 1; if (current == goal) { if (isDebugOn) { Console.WriteLine("Pathfinding complete."); } ReconstructPath(cameFrom, current, solutionLineThickness); return(true); } var neighbors = current.Neighbors(); foreach (Vector2D neighbor in neighbors) { if (neighbor.IsOutSideBound(0, 0, Width, Height)) { continue; // Ignore the neighbor which is out side the bounds of the image } if (closedSet[neighbor] == 1) { continue; // Ignore the neighbor which is already evaluated. } if (sourceImage.GetPixel(neighbor.x, neighbor.y).IsWall()) { continue; // Ignore the neighbor which is a wall } double tentative_gScore = gScore[current] + 1; // in this case, distance between current and neight is always 1, in other cases use Vector2D.DistanceBetween(current, neighbor); if (tentative_gScore < gScore[neighbor]) { // This path is the best until now. Record it! cameFrom[neighbor] = current; gScore[neighbor] = tentative_gScore; fScore[neighbor] = gScore[neighbor] + HeuristicCostEstimate(neighbor, goal); } // Discover a new node var neighborNode = new Node(neighbor, fScore[neighbor]); if (openSet[neighbor] != 1) { queue.Enqueue(neighborNode); openSet[neighbor] = 1; } } } if (isDebugOn) { Console.WriteLine("Pathfinding failed. There is no solution to this maze."); } return(false); }