Exemplo n.º 1
0
        /// <summary>
        /// Dijkstra's algorithm, shortest paths between nodes in a graph
        /// https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
        /// Computes the distance table between two nodes in a graph
        /// All positions have 8 degrees of freedom, and the cost of moving between positions are equal to
        /// 1 + the absolute difference in value.
        /// </summary>
        /// <param name="items">2D value array</param>
        /// <param name="initial">Start position</param>
        /// <param name="end">End positions</param>
        public static int[,] Dijkstra(IMoveCost[,] items, Position initial, Position end)
        {
            // Let the node at which we are starting be called the initial node. Let the distance of node Y be the distance from the initial node to Y.
            // Dijkstra's algorithm will assign some initial distance values and will try to improve them step by step.
            int W = items.GetLength(0);
            int H = items.GetLength(1);

            int[,] distance = new int[W, H];
            bool[,] visited = new bool[W, H];

            // 1. Assign to every node a tentative distance value: set it to zero for our initial node and to infinity for all other nodes.
            // 2. Set the initial node as current. Mark all other nodes unvisited. Create a set of all the unvisited nodes called the unvisited set.
            List<Position> unvisited = new List<Position>();
            for (int x = 0; x < W; x++)
                for (int y = 0; y < H; y++)
                {
                    distance[x, y] = int.MaxValue;
                    visited[x, y] = false;
                    if (!(initial.x == x && initial.y == y))
                        unvisited.Add(new Position(x,y));
                }

            distance[initial.x, initial.y] = 0;

            Position current = initial;

            while (true) {
                // For the current node, consider all of its unvisited neighbors and calculate their tentative distances.
                // Compare the newly calculated tentative distance to the current assigned value and assign the smaller one.
                // For example, if the current node A is marked with a distance of 6, and the edge connecting it with a neighbor B
                // has length 2, then the distance to B (through A) will be 6 + 2 = 8. If B was previously marked with a distance
                // greater than 8 then change it to 8. Otherwise, keep the current value.
                var neighbors = GetUnvisitedNeighbors(visited, current);

                foreach (Position neighbor in neighbors)
                {
                    // get the cost of moving to the neighbor
                    int cost = items[neighbor.x, neighbor.y].MoveCost(items[current.x, current.y]);

                    // if the path is shorter than a previous path to the neighbor, update it
                    int path = MOVE_PENALTY + cost + distance[current.x, current.y];
                    if (path < distance[neighbor.x, neighbor.y])
                        distance[neighbor.x, neighbor.y] = path;
                }

                // When we are done considering all of the neighbors of the current node, mark the current node as visited
                // and remove it from the unvisited set. A visited node will never be checked again.
                unvisited.Remove(current);
                visited[current.x, current.y] = true;

                // If the destination node has been marked visited (when planning a route between two specific nodes)
                // or if the smallest tentative distance among the nodes in the unvisited set is infinity
                // (when planning a complete traversal; occurs when there is no connection between the initial node and
                // remaining unvisited nodes), then stop. The algorithm has finished.
                if (visited[end.x, end.y])
                    break;

                // Otherwise, select the unvisited node that is marked with the smallest tentative distance,
                // set it as the new "current node", and go back to step 3.
                current = (from p in unvisited
                                 orderby distance[p.x, p.y] ascending
                                 select new Position(p.x, p.y)).First();

            }

            return distance;
        }
Exemplo n.º 2
0
        /// <summary>
        ///  Return a list of all the unvisited positions around the given position 
        /// </summary>
        /// <param name="visited">An bool array of visited positions</param>
        /// <param name="current">Position to get neighbors of</param>
        /// <returns></returns>
        internal static List<Position> GetUnvisitedNeighbors(bool[,] visited, Position current)
        {
            // get a list of all neighbors
            var list = GetNeighbors(current, visited.GetLength(0), visited.GetLength(1));

            // return the list without all visited neighbors
            return list.Where(p => !visited[p.x, p.y]).ToList();
        }
Exemplo n.º 3
0
        /// <summary>
        /// Computes the shortest path between two positions, in a 2D distance table. 
        /// All positions have 8 degrees of freedom
        /// </summary>
        /// <param name="values">2D Integer distance array</param>
        /// <param name="initial">Start position</param>
        /// <param name="end">End positions</param>
        /// <returns></returns>
        public static List<Position> GetShortestPathFromDistance(int[,] dist, Position initial, Position end)
        {
            // width and height of the given array
            int width = dist.GetLength(0);
            int height = dist.GetLength(1);

            // We have to make sure we don't backtrack when moving between neighbors.
            // This can be ignored if we make the assumption of movement cost to be at least 1.
            bool[,] visited = new bool[width, height];
            for (int x = 0; x < width; x++)
                for (int y = 0; y < height; y++)
                    visited[x, y] = false;

            List<Position> path = new List<Position>();

            // We start at the end
            Position current = end;

            // while we are no in the initial position,
            while (!(current.x == initial.x && current.y == initial.y))
            {

                // add the position to the list
                path.Add(current);

                // set the position as visited
                visited[current.x, current.y] = true;

                // find all unvisited neighbors
                var list = GetUnvisitedNeighbors(visited, current);

                // the next position is the neighbor with the lowest value
                current = (from item in list
                           orderby dist[item.x, item.y] ascending
                           select new Position(item.x, item.y)).First();

            }

            path.Add(current);
            path.Reverse();
            return path;
        }
Exemplo n.º 4
0
        /// <summary>
        ///  Return a list of all the positions around the given position 
        /// </summary>
        /// <param name="visited">An bool array of visited positions</param>
        /// <param name="current">Position to get neighbors of</param>
        /// <returns></returns>
        internal static List<Position> GetNeighbors(Position current, int width, int height)
        {
            // list of neighbors
            List<Position> neighbors = new List<Position>();

            // loop over a 3x3 square with the current position in the middle
            for (int x = current.x - 1; x <= current.x + 1; x++)
            {
                for (int y = current.y - 1; y <= current.y + 1; y++)
                {
                    // skip position if it's an edge or middle position
                    if (x < 0 || x >= width || y < 0 || y >= height || (current.x == x && current.y == y))
                        continue;

                    neighbors.Add(new Position(x, y));
                }
            }

            return neighbors;
        }
Exemplo n.º 5
0
 /// <summary>
 /// Computes the shortest path between two positions, in a 2D table. 
 /// All positions have 8 degrees of freedom, and the cost of moving between positions are equal to
 /// 1 + the absolute difference in value.
 /// </summary>
 /// <param name="values">2D Cell array</param>
 /// <param name="initial">Start position</param>
 /// <param name="end">End positions</param>
 /// <returns></returns>
 public static List<Position> GetShortestPath(IMoveCost[,] values, Position initial, Position end)
 {
     // Use Dijkstra's algorithm to compute the distance table for the given values
     int[,] dist = Dijkstra(values, initial, end);
     // traverse the shortest path and return list
     return GetShortestPathFromDistance(dist, initial, end);
 }