Cocos2d-x 3.1.1 Learning Log 16--A Star Algorithm (A * search algorithm) Learning
A * search algorithm is commonly known as the astar algorithm. This is an algorithm with multiple node paths on the graphic plane to find the lowest cost. It is often used in the mobile computing of the NPC in the game or the mobile computing of the BOT in the online game.
First: 1. Set any two points in the Map, the start point and the end point.
2. First, determine whether the vertex cannot be crossed or whether it is being closed.
3. If Step 2 is true and nothing is done, if it is false, we will add
4. If the node does not exist in open when you add the node, we can directly add the node and view it as the current node. If the node exists in open, we will compare the value of G, if you find that the G from the current node to this node is less than the original G, You can reset the G and F values and set this node as the current node.
5. After adding judgment and play, add four adjacent contacts to cycle through steps 1-4. Until the query is found or null in open, the query is completed.
The Code is as follows:
#include
#include
#include "AStartMap.h"using namespace std;int main() { AstartMap *gameMap = new AstartMap; gameMap->initMap(); if(gameMap != 0) { delete gameMap; gameMap = 0; } return 0;}#ifndef ASTARTNODE_H_#define ASTARTNODE_H_class AStartNode{public: AStartNode(); ~AStartNode();public: void setPos(int icol, int irow); void setG(int iG); int getG(); void setH(int iH); int getH(); void setF(int iF); void setFID(int iFID); int getFID(); int getF(); int getCol(); int getRow(); private: int m_Col; int m_Row; int m_G; int m_H; int m_F; int m_FID;};// end of AStartNode#endif // end of ASTARTNODE_H_#include "AStartNode.h"AStartNode::AStartNode() : m_Col(0), // m_Row(0), m_G(0), m_H(0), m_F(0), m_FID(0){}AStartNode::~AStartNode() {}void AStartNode::setPos(int icol, int irow) { m_Col = icol; m_Row = irow;}void AStartNode::setG(int iG) { m_G = iG;}int AStartNode::getG() { return m_G;}void AStartNode::setH(int iH) { m_H = iH;}int AStartNode::getH() { return m_H;}void AStartNode::setF(int iF) { m_F = iF;}int AStartNode::getF() { return m_F;}int AStartNode::getCol() { return m_Col;}int AStartNode::getRow() { return m_Row;}void AStartNode::setFID(int iFID) { m_FID = iFID;}int AStartNode::getFID() { return m_FID;}#ifndef ASTARTMAP_H_#define ASTARTMAP_H_#include
class AStartNode;class AstartMap{public: typedef enum { STARTMAP_COL = 10, STARTMAP_ROW = 10, } StartMap; typedef enum { MAPPATH_BEGINPOINT = -2, MAPPATH_WALL = -1, MAPPATH_ROAD = 0, MAPPATH_ENDPOINT = 2, } MapPath; typedef enum { STARTNODE_G = 10, STARTNODE_H = 10, }StartNodeInfo;public: AstartMap(); ~AstartMap();public: void initMap();private: void _initMapBoard(); void _initSelectBeginPoint(); void _addIntoCloseNode(AStartNode *newCloseNode); void _addIntoOpenNode(AStartNode *newOpenNode); void _deleteBeginNodefromOpenNode(AStartNode *newOpenNode); void _add_adjacentnodeToOpenNode(AStartNode *newOpenNode); void _beginToMove(); void _setStartNode_G_H_Value(AStartNode *newOpenNode, AStartNode *parentNode); bool _isWater(AStartNode *pStartNode);bool _isInClose(AStartNode *pStartNode); bool _isInOpen(AStartNode *pStartNode);private: AStartNode *_getMinFstartNode(); AStartNode *_getAStartNodeAt(int iCol, int iRow); void _heapRebuild(std::vector &rStartNodeArray,int root,int size); void _heapSort(std::vector &rStartNodeArray ,int size);private: std::vector m_AstartNode; std::vector m_openNode; std::vector m_closeNode; AStartNode *m_pEndNode; int GameMap[STARTMAP_COL][STARTMAP_ROW]; // map };// end of AstartMapbool isNum(int inum);#endif // end of ASTARTMAP_H_#include "AStartNode.h"#include
#include
#include #include
#include "AStartMap.h"extern bool isNum(int inum);AstartMap::AstartMap() : m_pEndNode(0){}AstartMap::~AstartMap() {}void AstartMap::initMap() { /* *@init the game map */ _initMapBoard();}void AstartMap::_initMapBoard() { //memset(GameMap, MAPPATH_ROAD, STARTMAP_COL * STARTMAP_ROW * sizeof(int)); for(int i = 0; i < STARTMAP_COL; ++i) { for(int j = 0; j < STARTMAP_ROW; ++j) { GameMap[i][j] = MAPPATH_ROAD; AStartNode *aStartNode = new AStartNode; aStartNode->setPos(i, j); m_AstartNode.push_back(aStartNode); } } for(int i = 0; i < 7; ++i) { // set the game wall GameMap[i + 2][4] = MAPPATH_WALL; } _initSelectBeginPoint();}void AstartMap::_initSelectBeginPoint() { int ibegin_xpos = 0; int ibegin_ypos = 0; std::cout<<"Select the Begin Point(X in(0-9), y in (0- 9): \n"; std::cin>>ibegin_xpos; std::cin>>ibegin_ypos; if(!isNum(ibegin_xpos) || !isNum(ibegin_ypos)) return; std::cout<<"Select the End Point(X in(0-9), y in (0- 9): \n"; int iend_xpos = 0; int iend_ypos = 0; std::cin>>iend_xpos; std::cin>>iend_ypos; if(!isNum(iend_xpos) || !isNum(iend_ypos)) return; GameMap[iend_xpos][iend_ypos] = MAPPATH_ENDPOINT; // set end point AStartNode *pBeginNode = _getAStartNodeAt(ibegin_xpos, ibegin_ypos); m_pEndNode = _getAStartNodeAt(iend_xpos, iend_ypos); if(pBeginNode == 0) return; pBeginNode->setG(0); pBeginNode->setF(0); pBeginNode->setH(0); m_openNode.push_back(pBeginNode); /* *@Game Begin *the player begins to move */ _beginToMove();}void AstartMap::_beginToMove() { while(true) { AStartNode *pBeginNode = _getMinFstartNode(); std::cout<<"select point: "<
getCol()<<", "<
getRow()<
getCol() == iCol && m_AstartNode[i]->getRow() == iRow) return m_AstartNode[i]; } return 0;}void AstartMap::_addIntoCloseNode(AStartNode *newCloseNode) { if(newCloseNode == 0) return; m_closeNode.push_back(newCloseNode);}void AstartMap::_addIntoOpenNode(AStartNode *newOpenNode) { if(newOpenNode == 0) return; m_openNode.push_back(newOpenNode); // then other 4 node}void AstartMap::_add_adjacentnodeToOpenNode(AStartNode *newOpenNode) { int ileftNodeRow = newOpenNode->getRow() - 1; if(ileftNodeRow >= 0) { AStartNode *leftNode = _getAStartNodeAt(newOpenNode->getCol(), ileftNodeRow); if(!_isWater(leftNode) && !_isInClose(leftNode) ) { if(! _isInOpen(leftNode) ) { // in open leftNode->setFID(newOpenNode->getFID()); _addIntoOpenNode(leftNode); _setStartNode_G_H_Value(leftNode, newOpenNode); } else { // not in open // _setStartNode_G_H_Value(leftNode, newOpenNode); } } } int irightNodeRow = newOpenNode->getRow() + 1; if(irightNodeRow < STARTMAP_ROW) { AStartNode *rightNode = _getAStartNodeAt(newOpenNode->getCol(), irightNodeRow); if(!_isWater(rightNode) && !_isInClose(rightNode)) { if(! _isInOpen(rightNode) ) { // in open rightNode->setFID(newOpenNode->getFID()); _addIntoOpenNode(rightNode); _setStartNode_G_H_Value(rightNode, newOpenNode); } else { // not in open //_setStartNode_G_H_Value(rightNode, newOpenNode); } } } int iupNodeCol = newOpenNode->getCol() - 1; if(iupNodeCol >= 0) { AStartNode *upNode = _getAStartNodeAt(iupNodeCol, newOpenNode->getRow()); if(!_isWater(upNode) && !_isInClose(upNode)) { if( ! _isInOpen(upNode)) { //in open upNode->setFID(newOpenNode->getFID()); _addIntoOpenNode(upNode); _setStartNode_G_H_Value(upNode, newOpenNode); } else { //_setStartNode_G_H_Value(upNode, newOpenNode); } } } int idownNodeCol = newOpenNode->getCol() + 1; if(idownNodeCol < STARTMAP_COL) { AStartNode *downNode = _getAStartNodeAt(idownNodeCol, newOpenNode->getRow()); if(!_isWater(downNode) && !_isInClose(downNode)) { if( ! _isInOpen(downNode)) { //in open downNode->setFID(newOpenNode->getFID()); _addIntoOpenNode(downNode); _setStartNode_G_H_Value(downNode, newOpenNode); } else { //_setStartNode_G_H_Value(downNode, newOpenNode); } } }}bool AstartMap::_isWater(AStartNode *pStartNode) { int icol = pStartNode->getCol(); int irow = pStartNode->getRow(); if(GameMap[icol][irow] == MAPPATH_WALL) return true; return false;}bool AstartMap::_isInClose(AStartNode *pStartNode) { assert(pStartNode); std::vector::iterator it = m_closeNode.begin(); for( ; it != m_closeNode.end(); ++it) { if(*it == pStartNode) { return true; } } return false;}bool AstartMap::_isInOpen(AStartNode *pStartNode) { assert(pStartNode); std::vector::iterator it = m_openNode.begin(); for(; it != m_openNode.end(); ++it) { if(*it == pStartNode) { return true; } } return false;}void AstartMap::_deleteBeginNodefromOpenNode(AStartNode *newOpenNode) { if(newOpenNode == 0) return; std::vector::iterator it = m_openNode.begin(); for( ; it != m_openNode.end(); ++it) { if(*it == newOpenNode) { m_openNode.erase(it); break; } }}void AstartMap::_setStartNode_G_H_Value(AStartNode *newOpenNode, AStartNode *parentNode) { if(newOpenNode == 0 || parentNode == 0) return ; if(newOpenNode->getCol() == 6 && newOpenNode->getRow() == 3) { int i = 0; } newOpenNode->setG( parentNode->getG() + 10); newOpenNode->setH( ( abs((m_pEndNode->getRow() - newOpenNode->getRow())) + abs((m_pEndNode->getCol() - newOpenNode->getCol())) - 1) * 10); newOpenNode->setF(newOpenNode->getG() + newOpenNode->getH());}AStartNode *AstartMap::_getMinFstartNode() { _heapSort(m_openNode, m_openNode.size()); int icount = m_openNode.size(); AStartNode *minNode = m_openNode[0]; return minNode;}void AstartMap::_heapRebuild(std::vector &rStartNodeArray, int root, int size) { int child = 2 * root + 1; if(child <= size - 1) { int rightChild = child + 1; if(rightChild <= size - 1) if(rStartNodeArray[child]->getF() < rStartNodeArray[rightChild]->getF()) child = rightChild; if(rStartNodeArray[root]->getF() < rStartNodeArray[child]->getF()) { AStartNode *temp = rStartNodeArray[child]; rStartNodeArray[child] = rStartNodeArray[root]; rStartNodeArray[root] = temp; _heapRebuild(rStartNodeArray, child, size); } } } void AstartMap::_heapSort(std::vector &rStartNodeArray, int size) { for(int i = size-1; i >= 0; i--){ _heapRebuild(rStartNodeArray,i,size); } int last=size-1; for(int i = 1;i <= size; i++, last--) { AStartNode *temp=rStartNodeArray[0]; rStartNodeArray[0]=rStartNodeArray[last]; rStartNodeArray[last]=temp; _heapRebuild(rStartNodeArray,0,last); } } //bool isNum(int inum) { // if the num in(0-9) return true, or return false if(inum >= 0 && inum <= 9) return true; return false;}
The choice between speed and accuracy is not static. You can use the CPU speed, the number of time slices used for path search, the number of objects (units) on the map, the importance of objects, and the group) or any other factors. One way to achieve a dynamic compromise is to create a heuristic function to assume that the minimum cost of a grid space is 1, and then create a cost function for scales ): g' (n) = 1 + alpha * (g (n)-1) If alpha is 0, the value of the improved cost function is always 1. In this case, the terrain cost is completely ignored, and A *'s work becomes A simple way to determine whether A grid can pass through. If alpha is 1, the initial cost function will work, and then you get all the advantages of. You can set any value of alpha to 0 to 1. You can also consider selecting the return value of a heuristic function: absolute minimum price or expected minimum price. For example, if most of the terrain on your map is the lawn with a price of 2 and the road with a price of 1 in other places, you can consider removing the road from heuristic functions, returns 2 * distance. The choice between speed and accuracy is not global. Accuracy is important in some areas of a map. You can dynamically choose based on this. For example, if we may stop re-computing the path or change the direction at a certain point, it is more important to select a good path near the current position, so why do we get bored with the accuracy of subsequent paths? Or, the shortest path may not be very important for a safe area on the map, but when escaping from an enemy's village, security and speed are the most important. In the game, the path potentially consumes a lot of storage space, especially when the path is long and many objects need to be searched. Path compression, navigation points, and beacons save multiple steps as a small data, thus reducing the need for space. Waypoints rely on straight-line segments being common so that we have to store only the endpoints, while beacons rely on there being well-known paths calculated beforehand between specially marked places on the map. if the path still uses a lot of storage space, you can limit the path length, which returns to the classic time-space Compromise Method: to save space, information can be discarded, calculate it later.