Implementation of Cocos2d-x map walking 3: A * algorithm, cocos2d-x Algorithm
In the previous section, implementation of Cocos2d-x map walking 2: SPFA algorithm:
Http://blog.csdn.net/stevenkylelee/article/details/38440663
If you forget the previous implementation of Dijkstra, please refer to the first article to review it. Why? Because the algorithm A * mentioned in this section is actually an improvement of Dijkstra. Only by understanding Dijkstra can we better understand *.
In this section, we first modify the previous Dijkstra implementation to make it more like A * structure. Then, change Dijkstra to *.
1. Review and modify the previous Dijkstra implementation.
Review previous Dijkstra implementations. Dijkstra needs to select a vertex with the minimum path cost from a table Q. Our previous implementation was to put all vertices in this table Q at the beginning. After careful consideration, we will find that those vertices whose values are initialized to the maximum path cost, 0x0FFFFFFF, cannot be selected and do not need to be traversed for these vertices. The vertex with the smallest cost of the path retrieved from the table. If you retrieve one, the shortest path from the starting point is found to the vertex. These vertices do not need to be put back in the list.
We can make such a small Optimization for Dijkstra. Although it is still O (N ^ 2), the time complexity has not changed:
At the beginning, only the starting vertex is placed in the table.
If the relaxation succeeds, the vertex pointing to the Edge End is placed in the table.
In this case, Relax will return the result.
The implementation code is as follows:
Void Dijkstra: Execute (const Graph & Graph, const string & VetexId) {m_Ret.PathTree.clear (); const auto & Vertexes = Graph. getVertexes (); Vertex * pVertexStart = Vertexes. find (VetexId)-> second; vector <Vertex *> Q; // initialize the Vertex for (auto & it: Vertexes) {it. second-> PathfindingData. cost = 0x0FFFFFFF; pVertexStart-> PathfindingData. pParent = 0;} // initialize the start vertex pVertexStart-> PathfindingData. cost = 0; pVert ExStart-> PathfindingData. pParent = 0; // Add the starting vertex to the list q. push_back (pVertexStart); pVertexStart-> PathfindingData. flag = true; for (; Q. size ()> 0;) {// select the vertex auto v = ExtractMin (Q) for the minimum path estimation; v-> PathfindingData. flag = false; // "relax" const auto & EO = v-> GetEdgesOut (); for (auto & it: EO) {Edge * pEdge = it. second; Vertex * pVEnd = pEdge-> GetEndVertex (); bool bRet = Relax (v, pVEnd, pEdge -> GetWeight (); // If the relaxation succeeds, add it to the list. If (bRet & pVEnd-> PathfindingData. flag = false) {Q. push_back (pVEnd); pVEnd-> PathfindingData. flag = true ;}// end for} // end}
Dijkstra is smarter than BFS. BFS just "blindly" extracts elements from the queue for extension. Dijkstra knows that node extension should be selected with the lowest path cost each time.
2. A * Algorithm
Dijkstra is smarter than BFS, while A * is smarter and faster than Dijkstra. A * improve the extension rules by calling A heuristic function. It tries its best to avoid extending other useless vertices. Its goal is to go straight to the destination. In this case, it seems that A * has long eyes and can see how far the current position is from the target point. The biggest difference between A * And Dijkstra is that they have "Eyes": heuristic functions.
The heuristic function will tell A * which vertex should be extended first. What is a heuristic function? Formula: F = G + H. Simply put, the path cost of the current vertex (G) + the estimated cost of the current vertex from the target vertex (F)
Previously, Dijkstra was modified and optimized to make it more like the * algorithm. Here, we change the heuristic data of Dijkstra from the vertex with the minimum path cost to the vertex with the minimum F value (the value of the heuristic function) to *. How to Design the valuation function H? Here, we can take the distance from the vertex to the target vertex.
We need to make the following changes to the Dijkstra and data structure:
1. Add a Heuristic field to the data structure of the vertex class. This field is used by the * algorithm to save the value calculated by the heuristic function. As follows:
Class Vertex {//... omitting some irrelevant functions and fields // The precursor Vertex of the data struct Pathfinding {// required by the public: // Pathfinding algorithm as before. Vertex * pParent; // path Cost estimation int Cost; // identifier int Flag; // The value calculated by the Heuristic function int Heuristic; Pathfinding () {pParent = 0; cost = 0; Flag = 0; Heuristic = 0;} PathfindingData ;}
2. Change Dijkstra's Relax relaxation function to limit the value of heuristic function F. If the calculated F value is smaller than the original F value of the vertex, the parent node, actual path cost, and F value of the vertex will be updated.
3. Each cycle determines whether the vertex with the minimum F value is the target vertex. If the target vertex is located, the path is found and the algorithm ends.
The A * pseudocode used here is as follows:
AStar (graph G, start vertex S, target vertex T) {put start vertex S in Open table while (Open table is not empty) {retrieve the vertex with the smallest valuation value F from the Open Table v Mark v is not in the Open table if (v equals to the target vertex T) {// path retrun found ;} foreach (vEnd of all outbound vertices of v) {Relax (weight of v, vEnd, edge) if (Relax successfully and vertex vEnd is not in Open table) {put vEnd in the Open table; Mark vEnd in the Open table; }}} bool Relax (vertex from, vertex to, edge weight) {// A * heuristic function compute F = G + h g = the path cost of the vertex from + the weight on the edge; H = the estimated cost of the vertex to the target vertex T; F = G + H; if (F <F evaluation value of vertex to) {records the parent path of to as from; the path cost value of vertex to is updated to G; the heuristic evaluation value F of vertex to is updated to F; return true;} return false ;}
We can see that A * is very similar to the Dijkstra algorithm we have transformed. If we keep the H of the heuristic function F = G + H of A * returning 0, it is A Dijkstra. To change A * To Dijkstra, you only need to modify one sentence.
The following is my implemented A * algorithm.
AStar. h
# Pragma once # include "GraphPathfinding. h" # include <functional> class AStar: public GraphPathfinding {public: AStar ();~ AStar (); public: // Estimate the cost of the Vertex to the target Vertex std: function <int (const Vertex * pVCurrent, const Vertex * pVTarget)> Estimate; public: virtual void Execute (const Graph & Graph, const string & VetexId) override; private: // extract the Vertex inline Vertex * ExtractMin (vector <Vertex *> & Q) with minimum path valuation ); // Relax inline bool Relax (Vertex * v1, Vertex * v2, int Weight); public: void SetTarget (Vertex * pVTarget) {m_pVTarget = pVTarget;} private: vertex * m_pVTarget ;};
AStar. cpp
# Include "AStar. h" AStar: AStar () {} AStar ::~ AStar () {} void AStar: Execute (const Graph & Graph, const string & VetexId) {const auto & Vertexes = Graph. getVertexes (); Vertex * pVertexStart = Vertexes. find (VetexId)-> second; vector <Vertex *> Q; // initialize the Vertex for (auto & it: Vertexes) {Vertex * pV = it. second; pV-> PathfindingData. cost = 0; pV-> PathfindingData. pParent = 0; pV-> PathfindingData. heuristic = 0x0FFFFFFF; pV-> PathfindingData. flag = false;} // Initialize the start vertex pVertexStart-> PathfindingData. pParent = 0; pVertexStart-> PathfindingData. cost = 0; pVertexStart-> PathfindingData. heuristic = Estimate (pVertexStart, m_pVTarget); // puts the starting vertex in the list Q. push_back (pVertexStart); pVertexStart-> PathfindingData. flag = true; for (; Q. size ()> 0;) {// select the vertex auto v = ExtractMin (Q) for the minimum path estimation; v-> PathfindingData. flag = false; if (v = m_pVTarget) {return;} // All Perform "relaxation" const auto & EO = v-> GetEdgesOut (); for (auto & it: EO) {Edge * pEdge = it. second; Vertex * pVEnd = pEdge-> GetEndVertex (); bool bRet = Relax (v, pVEnd, pEdge-> GetWeight (); // If the relaxation succeeds, add it to the list. If (bRet & pVEnd-> PathfindingData. flag = false) {Q. push_back (pVEnd); pVEnd-> PathfindingData. flag = true ;}// end for} // end for} Vertex * AStar: ExtractMin (vector <Vertex *> & Q) {Vertex * Ret = 0; ret = Q [0]; int pos = 0; for (int I = 1, size = Q. size (); I <size; ++ I) {if (Ret-> PathfindingData. heuristic> Q [I]-> PathfindingData. heuristic) {Ret = Q [I]; pos = I ;}} Q. erase (Q. begin () + pos); return Ret;} bool AStar: Relax (Vertex * v1, Vertex * v2, int Weight) {// here is the heuristic function int G = v1-> PathfindingData. cost + Weight; // get the actual path price from V1 to V2 int H = Estimate (v2, m_pVTarget ); // estimate the path cost of V2 to the target node. int nHeuristic = G + H; // actual + estimation = value of the heuristic function // if this path reaches the target, it will be calculated shorter, and if (nHeuristic <v2-> PathfindingData will be updated. heuristic) {v2-> PathfindingData. cost = G; v2-> PathfindingData. pParent = v1; v2-> PathfindingData. heuristic = nHeuristic; return true;} return false ;}
H function (estimate the cost of the current vertex to the target vertex) "outsourced" to external execution. Because the AStart class does not know the existence of the MapWalkVertex vertex class. Why is it necessary to "outsource" the execution instead of in the AStar class? To do this in the AStar class, you need to know the geometric position of each vertex, And the geometric position of the vertex is the attribute of the Node class of the Cocos2D-x. The AStar class should not be coupled with other things. In order to be "independent" and "General", the calculation H will be executed in the Observer mode and "outsourcing.
The usage of the AStar class is as follows:
// A *'s H valuation function auto Estimate = [] (const Vertex * pVCurrent, const Vertex * pVTarget)-> int {map1_vertex * pMwv1 = (map1_vertex *) pVCurrent-> UserData. find ("mwv")-> second; map1_vertex * pMwv2 = (map1_vertex *) pVTarget-> UserData. find ("mwv")-> second; Point v = pMwv1-> getPosition ()-pMwv2-> getPosition (); int H = v. getLength (); return H ;}; AStar; // set the destination vertex AStar. setTarget (pVertexTarget); // sets the H valuation function AStar. estimate = Estimate; // start to execute AStar. execute (* m_pGraph, pMwvStart-> GetGraphVertex ()-> GetId ());
OK, A * is finished. Run the test:
Tested. Our A * can find the shortest path. And the execution speed is faster than Dijkstra and Spfa.
4. Summary of Djikstra, SPFA, A * Algorithm
Dijsktra: select a vertex with the minimum path cost and relax all its edges.
SPFA: stores vertices in a queue, extracts the first vertex from the queue, and relaxes all its edges. If the relaxation succeeds, the edges are added to the queue.
A *: it is the release version of Djikstra. Select the vertex with the smallest heuristic function value and relax all its edges.
4. Download the source code project in this article:
Http://download.csdn.net/detail/stevenkylelee/7734787
Urgent: first recognized the source code of A * Algorithm
# Include <iostream>
# Include <cmath>
Using namespace std;
Struct tnode {
Int gvalue; // The following three parameters are the estimation functions.
Int hvalue;
Int fvalue;
Tnode * parent; // not the parent node, but the current node
Tnode * next; // point to the next node of the linked list
Int pass;
Int nodevalue; // uniquely identifies a node.
};
Tnode table [5] [5]; // store map 5*5
Int startx, starty, endx, endy;
Tnode openlist, closelist;
Void computervalue (const int curx, const int cury );
Int intsearch (tnode * plist, int value); // int is required for C ++
Void addopenlist (int hx, int hy );
Void handlenode (int hx, int hy, int curx, int cury );
Void main ()
{
Tnode * pp;
Int x, y;
Int I, j; // to be defined
For (I = 0; I <= 4; I ++) // Initialization
For (j = 0; j <= 4; j ++)
{
Table [I] [j]. gvalue = 0;
Table [I] [j]. hvalue = 0;
Table [I] [j]. fvalue = 0;
Table [I] [j]. parent = 0;
Table [I] [j]. next = 0;
Table [I] [j]. pass = 1;
Table [I] [j]. nodevalue = (I + 1) * (j + 1 );
}
Cout <"the input cannot pass, in the format of coordinate X coordinate Y" <endl;
For (I = 1; I <= 3; I ++)
{
Cin> x> y;
Table [x] [y]. pass = 0;
}
Cout <"start position of the input, in the format of coordinate X coordinate Y" <endl;
Cin> startx> starty;
Cout <"end position of the input, in the format of coordinate X coordinate Y" <endl;
Cin> endx> endy;
For (I = 0; I <= 4; I ++) & #4 ...... the remaining full text>
How to set the path of a hero in cocos2d-x
Optimization Path? Use a * Algorithm