The simple realization of game finding algorithm

Source: Internet
Author: User
Tags abs pow

When we refer to the search algorithm, we all think of a * algorithm.

In the Niang find a lot of code, read a lot of tutorials, especially the summary mentioned in this article: http://www.cppblog.com/christanxw/archive/2006/04/07/5126.html a * Algorithm summary ( Summary of the * method)

Ok, now that you've read the whole introduction, now we put all the steps together:

1. Add the starting point to the open list.

2. Repeat the following process:

A. Traverse the open list to find the node with the lowest F value as the current node to be processed.

B. Move this node to the close list.

C. Each square of the 8 adjacent squares of the current square.

If it is unreachable or it is in the close list, ignore it. Otherwise, do the following.

If it is not in the open list, add it to the open list and set the current grid to its father, recording the values of F, G and H of the square.

If it is already in the open list, check to see if the path (i.e., through the current grid) is better, using the G value for reference. A smaller G-value indicates that this is a better path. If so, set its father to the current grid and recalculate its G and F values. If your open list is sorted by F value, you may need to reorder it after you change it.

D. Stop, when you

Add the end point to the open list, at which point the path has been found, or

The lookup endpoint failed, and the open list is empty and there is no path at this time.

3. Save the path. Starting at the end, each square moves along the parent node to the starting point, which is your path.


I follow the summary of this idea, wrote an algorithm to come out, open list and closed list is based on STL to implement.

probably 10000*10000 map, Seek road to find down, take 30 seconds of time, shame, if more complex terrain, to use more than 1 minutes ...


Finally, I made a summary of my own code, and found that the main slow place was the following steps:

A. Traverse the open list to find the node with the lowest F value as the current node to be processed.

In fact, this step is to sort the path directly, if you don't, the final path is likely to be a lot of repetitive ways to go back and forth, but this bug can be handled at the end of the filter, and the final filter is more efficient than direct search in the routing algorithm. If you're a game developer, then your algorithm has to do this, using a two-fork heap to do this step will be faster, or you first achieve the shortest distance of the best path, in the use of long distance to find a road with a short walk function with the final screening method can also achieve the search road, if you are the game plug-in authors, you can not sort, and finally to filter.

If it is unreachable or it is in the close list, ignore it. Otherwise, do the following.

If it is not in the open list, add it to the open list and set the current grid to its father, recording the values of F, G and H of the square.

If it is already in the open list, check to see if the path (i.e., through the current grid) is better, using the G value for reference. A smaller G-value indicates that this is a better path. If so, set its father to the current grid and recalculate its G and F values. If your open list is sorted by F value, you may need to reorder it after you change it.

In the 8 direction of the logic, it is very unwise to traverse two lists, especially to close the list, which will only grow and there will be no reduction.



Walking through the open list is a very deadly thing, 10000*10000 's map, it's easy to go to tens of thousands of nodes. Every time I look for the next step, I go through it and finally get it down, that's pretty slow.

But the more deadly is to check close list, each looking for the next node, you need to check 8 times. If you really go to search the container, search the linked list, it will eventually find a way to find a very slow.


after you find the problem, take the right medicine, discard the time-consuming code, and redesign the search process:

1, the allocation of memory, create a binary map, so as to avoid the search closed list, but in the form of an array of direct access to the state of the coordinates.

2, to achieve settings/get a coordinate state interface (on or off), since the need to create their own two-valued map, then should be implemented to obtain the binary map interface to the search algorithm used.

3, the starting coordinates as a path list head

Repeat the following procedure:

A, starting from the starting point of valuation, each valuation set the current node is closed, then the 7 direction (the previous node has been closed on the last time, the current node is also closed at the beginning of the valuation, so it is 7 directions)

b, select the nearest direction to the target coordinate as the next valued node, and set the current node to its parent node.

C, if 7 directions can not go, it is a dead end, back to his parent node to find another direction.

End: Until your current coordinates are the target coordinates, or if the path list is empty

After the end, if the path list is not empty, the final path is filtered according to the path list.


This is the whole algorithm process, modified, 10000*10000 simple map search, just 0-30 milliseconds, more complex map not more than 100 milliseconds, anyway, compared to the previous comparison is God.

Anyway, I do not know this algorithm is not a * algorithm, anyway, the efficiency is more than the kind of time to check the table the way much higher.


The key code is as follows:

#define MHD_X 2
#ifndef minsift
#define Minsift 60.0
#endif
#ifndef maxsift
#define Maxsift 120.0< c5/> #endif
#ifndef mhd_x
#define MHD_X 1
#endif
#ifndef mhd_y
#define MHD_Y 1
#endif

typedef struct _apoint{
	int x;
	int y;
	_apoint *parent;
} Apoint, *lpapoint;

Class Castarfinder
{public
:
	Castarfinder () {m_ntx =-1; m_nty =-1; m_pmap = null; m_psafepoint = null;}
	castarfinder (int nTX, int nty): M_ntx (NTX), M_nty (Nty), M_pmap (null), M_psafepoint (null) {}//constructor parameter endpoint x, end y
	void Search (int X, int Y, std::vector<point> &vresult);//Search function, Parameter: Start X, start Y, pass in a container to return the final filtered node
	void Setmapattributes (bool *pmap, DWORD nwidth = 0xFFFFFFFF, dword nheight = 0xFFFFFFFF);
	Set map Properties, Parameters: Map pointer, width, height
	void settargetpos (int X, int Y) {m_ntx = x; m_nty = Y;} Since this class is 2 constructors, one with parameters, one without parameters, exposes a set target address parameter
private:
	lpapoint Manhattan (lpapoint lppoint);//value function
Private:
	int m_ntx, m_nty;
	BOOL *m_pmap;
	DWORD m_nmapwidth, m_nmapheight;
	Lpapoint m_psafepoint;
};

The fetch distance function used in the algorithm
double _p2g (int x1, int y1, int x2, int y2)
{return sqrt (
	POW double (abs (X1-X2)), 2) + POW (do Uble (ABS (Y1-Y2)), 2);
}

Debug output function
void _outf (const char *format, ...)
{
	va_list al;
	Char buf[blx_maxsize];
	Va_start (al, format);
	_vsnprintf (buf, blx_maxsize, format, AL);
	Va_end (AL);
	Outputdebugstringa (BUF);
}


struct _find_astar_note_gap{_find_astar_note_gap (int X, int Y, double gap): _x (x), _y (y), _gap (GAP) {} bool Operator () (Point &point)
	{return _p2g (point.x, Point.y, _x, _y) < _gap;}
	int _x, _y;
Double _gap;

};
	void Castarfinder::setmapattributes (bool *pmap, DWORD nwidth, DWORD nheight) {m_pmap = PMap;
	M_nmapwidth = nwidth;
M_nmapheight = nheight;
	} lpapoint Castarfinder::manhattan (Lpapoint lppoint) {int NX = lppoint->x, NY = lppoint->y;
	int ax[3] = {nx-mhd_x, NX, NX + mhd_x};
	int ay[3] = {ny-mhd_y, NY, NY + mhd_y};
	_setmapvalue (M_pmap, NX, NY, false);/set coordinates turned off state, this function should be written by yourself bool Bstate = 0;
	Double dbmingap = 10000000.0, dbtemp;
	NX =-1;
	NY =-1;
			for (int i = 0; i < 3 i++) {for (int j = 0; J < 3; J + +) {//Traverse 7 locations using a cross way (the current coordinates are already turned off at the start, and his parent node was last turned off) if (Ax[j] > M_nmapwidth | | | ay[i] > m_nmapheight) continue;//coordinates are crossed directly to the next ring Bstate = _getmapvalue (M_pmap, Ax[j], Ay[i]//Get coordinate turn off state, this function should be written by yourself//because _getmapvalue will be used very frequently, so do notShould be called in any object-oriented form or the form of various complex structure pointers, directly write a function to let the compiler hard-coded to locate the past, is the most efficient way if (!bstate) {//is closed directly Next loop continue; else {dbtemp = _p2g (Ax[j], ay[i], M_ntx, m_nty);//Calculate absolute distance if (Dbtemp < DBMINGAP) {//smaller absolute distance represents more
					The good direction dbmingap = dbtemp;
					NX = Ax[j];
				NY = Ay[i];  if (NX > 0 && nY > 0) {//7 direction is any valid, create a new node, set the current node to his parent node, and return to the new node lpapoint PResult =
		New Apoint;
		Presult->x = NX;
		Presult->y = NY;
		Presult->parent = Lppoint;
	return pResult;	
//Otherwise it is a dead end, returns null return null; } void Castarfinder::search (int X, int Y, std::vector<point> &vresult) {Outf ("%d,%d,%d,%d,%d,%d", X, Y,
	M_ntx, M_nty, M_nmapwidth, m_nmapheight); if ((int) M_ntx < 0 | | (int) M_nty < 0 | | X < 0 | | Y < 0 | | (DWORD) X > M_nmapwidth | | (DWORD) Y > M_nmapheight | |

	!M_PMAP) return;
	Apoint *sp = new Apoint;
	sp->x = x;
	Sp->y = y;
	Sp->parent = NULL;
	Lpapoint point = SP; Lpapoint P2 = NULL
		while (M_psafepoint!= NULL) {//This loop is a memory leak problem that prevents the thread from forcing the end of the same object operation P2 = M_psafepoint;
		M_psafepoint = m_psafepoint->parent;
	Delete P2;
	
	DWORD dwtime = Clock ();
		do{P2 = point; Point = Manhattan (p2),//value and return the next minimum distance node if (!point) {//valuation, if NULL is returned, then this path is dead end, this time should be back to the previous node (parent node) to search for points = P
			2->parent;//the current node to the parent node delete p2;
			if (!point) break; If the current node is still null after it is placed as his parent node, this list of paths is already empty and should check://1, coordinates are correct (starting point and end point)//2, map data is correct//3, the function of obtaining map status value is normal}}while (_p2g PO
	Int->x, Point->y, M_ntx, M_nty) > 20.0);
	Because I am not absolutely accurate to the final target coordinates, so I do so, if the request is absolutely accurate, should directly = = Judge.
	int ncount = 0;
	int nresultcount = 0;
	int NX, NY;
	Point Pttemp;
	M_psafepoint = point; if (point) {//filter path, I am based on the distance between all the nodes that have been selected and the next node must be greater than minsift the last selected node and the next node must be less than the distance between the Maxsift to filter, the distance can be set according to the need, the two Chang in the header file,
		You can customize the NX = point->x; before you include
		NY = point->y;
		pttemp.x = NX;
		PTTEMP.Y = NY;
		Vresult.clear (); do{if (_p2g (pttemp.x, Pttemp.y, Point->x, point->y) < 120.0 &&Amp
			Std::find_if (Vresult.begin (), Vresult.end (), _find_astar_note_gap (Point->x, Point->y, 60.0)) = = VResult.end ())
				{//Why do you want to traverse the entire container to filter the nodes that are greater than minsift rather than just the previous node?
				The reason is that the list is not sorted, and if only one node is traversed, it is very likely that the path to go back is added to the container pttemp.x = point->x;
				PTTEMP.Y = point->y;
				Vresult.push_back (pttemp);
			nresultcount++;
			}//Clean memory P2 = point;
			Point = point->parent;
			Delete P2;
			ncount++;
		M_psafepoint = point;
	}while (point!= NULL);
	} dwtime = Clock ()-dwtime;
	_outf ("Eventually find a way to:%x,%x", NX, NY);
	_outf ("Number of PATH nodes:%d", ncount);
	_outf ("Number of nodes filtered:%d", nresultcount);
_outf ("Algorithm time consuming:%d milliseconds", dwtime); }


necessary STL header file:

#include <vector>
#include <algorithm>

required C Function library header file:

#include <math.h>

I also forgot what else I needed, as if the debug output function requires a variable parameter header file.

return to the final path, it is recommended to use queue or List,push_front to add nodes forward, if vector is used like me, the final path returned is reversed.


This algorithm is not high accuracy, this is a weakness, if you want to improve the algorithm, after selecting a node, it is necessary to determine if there are any nodes in the existing node that are adjacent to him, and if so, to remove the node after the existing parent node and reset his parent node, doing so, there are some improvements to efficiency because the whole list , those useless nodes have been excluded, but the accuracy is still not high. But for the game plug-in search, this algorithm is actually very good.


the last thing to note:

1. Do not attempt to write this algorithm as a base class and then inherit to call, do not believe you can add a virtual function to the class to see that efficiency, will be much slower, because by accessing the virtual function table, in the call of the final function, this process can be very expensive. In the case of small amount of processing may not affect, but the processing volume, the visual is 3-10 times slower, depending on the complexity of the map.

2, it is best not to write this algorithm process-oriented programming, because the need for more parameters, if all rely on the pressure stack, efficiency will be much slower than the object-oriented approach.


the reason for the final installation of B is:

C + + ThisCall is a register to pass the this pointer to the outer MOV ecx, esi inside mov esi, ecx is probably this way

A parameter stack instruction push will be slower than the two instructions above the N-fold, push will access memory, the speed of memory with the register is not a level.

In the function, the process-oriented function is always to access the parameters of the stack through the EBP or ESP registers, ThisCall can access the class members through the this pointer, and access parameters is a reason, anyway, to address, this is not to avoid, So what can be avoided is as little as possible transfer parameters, if you really tangled this overhead, you can design the class member function without parameters, all rely on member variables to operate, of course, this code you have to change.


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.