[C++] A* optimal pathfinding algorithm (additional maze obstacles are automatically generated)

Table of contents

I. Introduction

2. About the generation of maze (obstacle)

1. Simple explanation

 2. Code

3. A* Algorithm

1. Simple explanation

 2. Effect demonstration

3. Code

Four, the total code

V. Summary


I. Introduction

        I recently took an artificial intelligence course, learned the A* algorithm, and tried to use C++ to implement this algorithm.

        In order to reflect the pathfinding process more conveniently, the easyx image library is used here to display the movement process in real time. You can go to the official website of easyx to download this library. easyx official website

        It is best to use the easyx library in: Debug-Debug Properties-Configuration Properties-Advanced-Character Set to use multi-byte character sets, otherwise some functions may report errors.

2. About the generation of maze (obstacle)

1. Simple explanation

        First of all, we can use a two-dimensional array to store the maze, whose size is input by the user. Use 1 for walls and 0 for roads.

        To start initializing the maze array should be initialized to the following state.

        

         The array (1, 1) is the starting point, and then start to find out whether there is a path next to it. If so, open a path at random, and then record the current coordinates for recursion. The function exit means that if there is no place to get through, it will return directly. The path traveled needs to be marked as passed, otherwise an infinite loop may occur and the program will be incorrect. The path traveled here is marked as 5.

        After completing this step, you need to change all the 5s in the previous array to 0, that is, to the path.

        Finally, it is displayed, which is probably the following effect. Of course, the quality of the maze generated by this algorithm is not very good, but it is enough for use now.

           

        Of course, we don’t need a maze when using the A* algorithm, we only need some obstacles. Let’s write another function to randomly delete some walls of the above maze, and this step of generating obstacles is completed.

         

 2. Code

The structure that stores the maze

struct AStar {
	int M;						//M*M map
	int** maze;					//map迷宫
	//int* close;					//close表
	//int* open;					//open表
	int O_row;					//开始的行
	int O_col;					//开始的列	
	int E_row;					//终点行
	int E_col;					//终点列
}Astar{ 0,NULL,1,1,0,0 };

Initialize the maze

int main()
{
	Astar.M = 0;
	cout << "请输入迷宫是几乘几的" << endl;
	cin >> Astar.M;
	if (Astar.M % 2 == 0)//如果输入不为奇数变为奇数
		Astar.M++;

	Astar.E_col = Astar.M;
	Astar.E_row = Astar.M;
    Astar.maze = new int* [Astar.M + 2];//动态创建二维数组
	for (int i = 0; i < Astar.M + 2; i++)
		Astar.maze[i] = new int[Astar.M + 2];//每一行申请一个int空间的M+2列空间
}

//初始化迷宫
void Init_Maze(AStar& Astar)
{
	for (int i = 0; i < Astar.M + 2; i++)//动态创建迷宫
		for (int j = 0; j < Astar.M + 2; j++)
		{
			if (i == 0 || i == Astar.M + 1)//1为墙壁
				Astar.maze[i][j] = 1;
			if ((i % 2 == 1) && (j % 2 == 1))
				Astar.maze[i][j] = 0;
			else Astar.maze[i][j] = 1;
		}
}

looking for neighbors

bool findneighbor(AStar& Astar, int x_index, int y_index)
{
	if ((x_index >= 3 && Astar.maze[x_index - 2][y_index] == 0) || (x_index <= Astar.M - 1 && Astar.maze[x_index + 2][y_index] == 0)
		|| (y_index >= 3 && Astar.maze[x_index][y_index - 2] == 0) || (y_index <= Astar.M - 1 && Astar.maze[x_index][y_index + 2] == 0))
		return 1;
	else
		return 0;
}

create a maze

//随机创建迷宫
void creatMaze(AStar& Astar, int x_index, int y_index)
{
	int pos, x, y, flag = 0;
	x = x_index;
	y = y_index;

	while (1)
	{
		flag = 0;
		flag = findneighbor(Astar, x, y);
		if (!flag)
			return;
		else {
			Astar.maze[x_index][y_index] = 5;
			x = x_index;
			y = y_index;
			while (1)
			{
				pos = rand() % (4 - 1 + 1) + 1;
				if (pos == 1 && x_index >= 3 && Astar.maze[x_index - 2][y_index] == 0)//上
				{
					x_index -= 2;
				}
				else if (pos == 2 && x_index <= Astar.M - 1 && Astar.maze[x_index + 2][y_index] == 0)//下
				{
					x_index += 2;
				}
				else if (pos == 3 && y_index <= Astar.M - 1 && Astar.maze[x_index][y_index + 2] == 0)
				{
					y_index += 2;
				}
				else if (pos == 4 && y_index >= 3 && Astar.maze[x_index][y_index - 2] == 0)
				{
					y_index -= 2;
				}
				Astar.maze[(x + x_index) / 2][(y + y_index) / 2] = 5;
				Astar.maze[x_index][y_index] = 5;

				//	showmaze(maze, M);

				creatMaze(Astar, x_index, y_index);
				break;
			}
		}
	}
}

 Change the path in the array, identifying the entry and exit

//给创建的迷宫创建标识
void makeMaze(AStar& Astar)
{
	for (int i = 0; i < Astar.M + 2; i++)
		for (int j = 0; j < Astar.M + 2; j++)
		{
			if (Astar.maze[i][j] == 5)
			{
				Astar.maze[i][j] = 0;
			}
		}
	Astar.maze[1][1] = 8;
	Astar.maze[Astar.M][Astar.M] = 2;
}

 change terrain

//改变地形,出现更多通路
void MakeDifficult(AStar& Astar)
{
	if (Astar.M > 5)
	{
		int half = (int)((0.4 * Astar.M) * (0.5 * Astar.M));
		int x_c, y_c = 0;
		for (int i = 0; i < half;)
		{
			x_c = rand() % (Astar.M - 1 + 1) + 1;
			y_c = rand() % (Astar.M - 1 + 1) + 1;
			if (Astar.maze[x_c][y_c] == 1)
			{
				Astar.maze[x_c][y_c] = 0;
				i++;
			}
		}
	}
}

show maze

//图像显示迷宫
//3为路,4为搜寻的路,1为墙,2为终点,8为开始
void show(AStar& Astar)
{
	IMAGE star, wall, ball ,ly;
	loadimage(&star, "./五角星.png", SIZE, SIZE);
	loadimage(&wall, "./墙.png", SIZE, SIZE);
	loadimage(&ball, "./球.png", SIZE, SIZE);
	loadimage(&ly, "./兰音.png", SIZE, SIZE);
	for (int i = 0; i < Astar.M + 2; i++)
	{
		for (int j = 0; j < Astar.M + 2; j++)
		{
			if (Astar.maze[i][j] == 1)
			{
				putimage((j + Astar.M + 2 + 1) * SIZE, (i)*SIZE, &wall);
				putimage(j * SIZE, i * SIZE, &wall);
			}
			else if (Astar.maze[i][j] == 4)
			{
				putimage((j + Astar.M + 2 + 1) * SIZE, (i)*SIZE, &ball);
			}
			else if (Astar.maze[i][j] == 3)
			{
				putimage(j * SIZE, (i)*SIZE, &ly);
			}
		}
	}
	putimage((Astar.M + 2 + 1 + 1) * SIZE, (SIZE), &star);
	putimage((Astar.M + Astar.M + 2 + 1) * SIZE, (Astar.M) * SIZE, &star);
	putimage(SIZE, SIZE, &star);
	putimage(Astar.M * SIZE, Astar.M * SIZE, &star);
}

At this point, the creation of maze or obstacle is completed, and the maze at this time is saved in the two-dimensional array of maze.

3. A* Algorithm

1. Simple explanation

        We assume that the beginning is the following picture, the starting point is the upper left corner, and the ending point is the lower right corner. The focus of the A* algorithm is the process by which we set the evaluation function F.

        F=G+H. Where G is the cost from the starting node to the current node, and H is the cost from the current node to the target node. The A* algorithm is an improvement on the A algorithm, which requires that G must be greater than 0 and H must not be greater than the actual cost.

        We assume that G is the number of steps from the beginning to the current node, and H is the Manhattan distance (ie |xe-xi|+|ye-yi|, where xe, ye are the end point coordinates, xi, yi are the current point coordinates), For the sake of convenience, we assume that we can only walk left, right, up and down, and cannot walk sideways, so the above F is as follows. The F value of the starting point should be the largest.

 

         Then the A* algorithm will find the smallest F and go up, and record the parent node. Professionally speaking, it is to calculate the F value of all the nodes that can go to the current node, put the node into the open table, and then find the node with the smallest F value, and put it into the close table. Here we can represent open as a tree and close as an array.

         Of course, our premise is that the four directions of walking must be determined in advance, and then every time we look for the node that enters the tree last and has the lowest cost, walk up, and continue to look for subsequent nodes. The corresponding reference frame diagram is also given in the artificial intelligence book.

 

 2. Effect demonstration

3. Code

int main()
{
	treeNode* pRoot = NULL;	 //准备一棵树
	vector<treeNode*> open;  //准备一个数组

	vector<treeNode*>::iterator it;	//迭代器
	vector<treeNode*>::iterator itmin;	//迭代器
    pRoot = creatTreeNode(Astar.O_row, Astar.O_col); //起点入树
    treeNode* Now = pRoot;
	pRoot->pos.F = Astar.M * Astar.M;
	open.push_back(pRoot);
	while (1)
	{
		//1.把当前能走的点找出来
		for (int i = 0; i < 4; i++)
		{
			treeNode* pChild = creatTreeNode(Now->pos.row, Now->pos.col);
			switch (i)
			{
			case Up:
				pChild->pos.row--;
				pChild->pos.g = Now->pos.g + 1;
				break;
			case Down:
				pChild->pos.row++;
				pChild->pos.g = Now->pos.g + 1;
				break;
			case Left:
				pChild->pos.col--;
				pChild->pos.g = Now->pos.g + 1;
				break;
			case Right:
				pChild->pos.col++;
				pChild->pos.g = Now->pos.g + 1;
				break;
			default:
				break;
			}
			//2.判断能不能走
			if (Astar.maze[pChild->pos.row][pChild->pos.col] == 0 || Astar.maze[pChild->pos.row][pChild->pos.col] == 8
				|| Astar.maze[pChild->pos.row][pChild->pos.col] == 2)	//如果是路则计算F
			{
				//标记走过
				Astar.maze[pChild->pos.row][pChild->pos.col] = 4;
				pChild->pos.h = getH(pChild->pos, Astar);//计算H
				pChild->pos.F = pChild->pos.g + pChild->pos.h;//计算F

				//入树
				Now->child.push_back(pChild);
				pChild->parent = Now;

				//存入数组
				open.push_back(pChild);
			}
			else {
				delete pChild;
			}
		}
		
		//3.找出数组最小的点,走上去
		//cout << open.size() << endl;
		itmin = open.begin();//假设第一个F最小	时间复杂度O(n)
		for (it = open.begin(); it != open.end(); it++)
		{
			itmin = ((*it)->pos.F <= (*itmin)->pos.F) ? it : itmin;	//细节 加上等于
		}
		//sort(open.begin(), open.end(),cmp);
		//itmin = open.begin();
		Now = *itmin;
		//标记走过
		//Astar.maze[Now->pos.row][Now->pos.col] = -1;
		show(Astar);
		//删除
		open.erase(itmin);

		//4.判断是否找到终点
		if (Now->pos.row == Astar.E_row && Now->pos.col == Astar.E_col)
		{
			flag = 1;
			break;
		}

		//5.如果为数组为空也退出
		if (0 == open.size()) break;
	}

	//成功则打印
	if (flag)
	{
		cout << "success" << endl;
		while (Now)
		{
			cout << "(" << Now->pos.row << "," << Now->pos.col << ")";
			printf("(g:%d,h:%d,f:%d)\n", Now->pos.g, Now->pos.h, Now->pos.F);
			Astar.maze[Now->pos.row][Now->pos.col] = 3;
			Now = Now->parent;
		}
	}
	else
	{
		cout << "not found" << endl;
	}

	while (1)
	{
		BeginBatchDraw();

		show(Astar);

		FlushBatchDraw();
	}//循环绘图防止BUG
	closegraph();
}

Four, the total code

#include<iostream>
#include<iomanip>
#include<graphics.h>
#include<time.h>
#include<easyx.h>
#include<algorithm>
#include<vector>
using namespace std;

//1为墙壁,0为路
#define SIZE 10

bool flag = 0;

enum dirct {
	Up,
	Down,
	Left,
	Right,
};

struct AStar {
	int M;						//M*M map
	int** maze;					//map迷宫
	//int* close;					//close表
	//int* open;					//open表
	int O_row;					//开始的行
	int O_col;					//开始的列	
	int E_row;					//终点行
	int E_col;					//终点列
}Astar{ 0,NULL,1,1,0,0 };

struct position {
	int row, col;
	int g;						//路径代价 行走的步数
	int h;						//启发函数 曼哈顿距离
	int F = h + g;				//估价函数
};

struct treeNode {	//close类似
	position pos;
	vector<treeNode*> child;
	treeNode* parent;
};

treeNode* creatTreeNode(int row, int col);						//创建树节点
int getH(position pos, AStar a);								//计算H

void Init_Maze(AStar& Astar);									//初始化迷宫
void show(AStar& Astar);										//图像显示迷宫
void creatMaze(AStar& Astar, int x_index, int y_index);			//随机创建迷宫
void makeMaze(AStar& Astar);									//给创建的迷宫创建标识
bool findneighbor(AStar& Astar, int x_index, int y_index);		//寻找邻居
void MakeDifficult(AStar& Astar);								//改变地形,出现更多通路
void ConsoleShow(AStar& Astar);									//控制台显示迷宫

///*排序方式*/
//bool cmp(treeNode* m1, treeNode* m2)
//{
//	return m1->pos.F < m2->pos.F;
//}

int main()
{
	treeNode* pRoot = NULL;	 //准备一棵树
	vector<treeNode*> open;  //准备一个数组

	vector<treeNode*>::iterator it;	//迭代器
	vector<treeNode*>::iterator itmin;	//迭代器

	Astar.M = 0;
	cout << "请输入迷宫是几乘几的" << endl;
	cin >> Astar.M;
	if (Astar.M % 2 == 0)//如果输入不为奇数变为奇数
		Astar.M++;

	Astar.E_col = Astar.M;
	Astar.E_row = Astar.M;

	pRoot = creatTreeNode(Astar.O_row, Astar.O_col); //起点入树

	srand((unsigned)time(0));//随机播种

	initgraph(((Astar.M + 2) * 2 + 1) * SIZE, (Astar.M + 2) * SIZE, SHOWCONSOLE);//打开绘图

	Astar.maze = new int* [Astar.M + 2];//动态创建二维数组
	for (int i = 0; i < Astar.M + 2; i++)
		Astar.maze[i] = new int[Astar.M + 2];//每一行申请一个int空间的M+2列空间

	Init_Maze(Astar);
	creatMaze(Astar, 1, 1);
	makeMaze(Astar);
	MakeDifficult(Astar);
	//show(Astar);

	treeNode* Now = pRoot;
	pRoot->pos.F = Astar.M * Astar.M;
	open.push_back(pRoot);
	while (1)
	{
		//1.把当前能走的点找出来
		for (int i = 0; i < 4; i++)
		{
			treeNode* pChild = creatTreeNode(Now->pos.row, Now->pos.col);
			switch (i)
			{
			case Up:
				pChild->pos.row--;
				pChild->pos.g = Now->pos.g + 1;
				break;
			case Down:
				pChild->pos.row++;
				pChild->pos.g = Now->pos.g + 1;
				break;
			case Left:
				pChild->pos.col--;
				pChild->pos.g = Now->pos.g + 1;
				break;
			case Right:
				pChild->pos.col++;
				pChild->pos.g = Now->pos.g + 1;
				break;
			default:
				break;
			}
			//2.判断能不能走
			if (Astar.maze[pChild->pos.row][pChild->pos.col] == 0 || Astar.maze[pChild->pos.row][pChild->pos.col] == 8
				|| Astar.maze[pChild->pos.row][pChild->pos.col] == 2)	//如果是路则计算F
			{
				//标记走过
				Astar.maze[pChild->pos.row][pChild->pos.col] = 4;
				pChild->pos.h = getH(pChild->pos, Astar);//计算H
				pChild->pos.F = pChild->pos.g + pChild->pos.h;//计算F

				//入树
				Now->child.push_back(pChild);
				pChild->parent = Now;

				//存入数组
				open.push_back(pChild);
			}
			else {
				delete pChild;
			}
		}
		
		//3.找出数组最小的点,走上去
		//cout << open.size() << endl;
		itmin = open.begin();//假设第一个F最小	时间复杂度O(n)
		for (it = open.begin(); it != open.end(); it++)
		{
			itmin = ((*it)->pos.F <= (*itmin)->pos.F) ? it : itmin;	//细节 加上等于
		}
		//sort(open.begin(), open.end(),cmp);
		//itmin = open.begin();
		Now = *itmin;
		//标记走过
		//Astar.maze[Now->pos.row][Now->pos.col] = -1;
		show(Astar);
		//删除
		open.erase(itmin);

		//4.判断是否找到终点
		if (Now->pos.row == Astar.E_row && Now->pos.col == Astar.E_col)
		{
			flag = 1;
			break;
		}

		//5.如果为数组为空也退出
		if (0 == open.size()) break;
	}

	//成功则打印
	if (flag)
	{
		cout << "success" << endl;
		while (Now)
		{
			cout << "(" << Now->pos.row << "," << Now->pos.col << ")";
			printf("(g:%d,h:%d,f:%d)\n", Now->pos.g, Now->pos.h, Now->pos.F);
			Astar.maze[Now->pos.row][Now->pos.col] = 3;
			Now = Now->parent;
		}
	}
	else
	{
		cout << "not found" << endl;
	}

	while (1)
	{
		BeginBatchDraw();

		show(Astar);

		FlushBatchDraw();
	}//循环绘图防止BUG
	closegraph();
}

//初始化迷宫
void Init_Maze(AStar& Astar)
{
	for (int i = 0; i < Astar.M + 2; i++)//动态创建迷宫
		for (int j = 0; j < Astar.M + 2; j++)
		{
			if (i == 0 || i == Astar.M + 1)//1为墙壁
				Astar.maze[i][j] = 1;
			if ((i % 2 == 1) && (j % 2 == 1))
				Astar.maze[i][j] = 0;
			else Astar.maze[i][j] = 1;
		}
}

//图像显示迷宫
//3为路,4为搜寻的路,1为墙,2为终点,8为开始
void show(AStar& Astar)
{
	IMAGE star, wall, ball ,ly;
	loadimage(&star, "./五角星.png", SIZE, SIZE);
	loadimage(&wall, "./墙.png", SIZE, SIZE);
	loadimage(&ball, "./球.png", SIZE, SIZE);
	loadimage(&ly, "./兰音.png", SIZE, SIZE);
	for (int i = 0; i < Astar.M + 2; i++)
	{
		for (int j = 0; j < Astar.M + 2; j++)
		{
			if (Astar.maze[i][j] == 1)
			{
				putimage((j + Astar.M + 2 + 1) * SIZE, (i)*SIZE, &wall);
				putimage(j * SIZE, i * SIZE, &wall);
			}
			else if (Astar.maze[i][j] == 4)
			{
				putimage((j + Astar.M + 2 + 1) * SIZE, (i)*SIZE, &ball);
			}
			else if (Astar.maze[i][j] == 3)
			{
				putimage(j * SIZE, (i)*SIZE, &ly);
			}
		}
	}
	putimage((Astar.M + 2 + 1 + 1) * SIZE, (SIZE), &star);
	putimage((Astar.M + Astar.M + 2 + 1) * SIZE, (Astar.M) * SIZE, &star);
	putimage(SIZE, SIZE, &star);
	putimage(Astar.M * SIZE, Astar.M * SIZE, &star);
}

//寻找邻居
bool findneighbor(AStar& Astar, int x_index, int y_index)
{
	if ((x_index >= 3 && Astar.maze[x_index - 2][y_index] == 0) || (x_index <= Astar.M - 1 && Astar.maze[x_index + 2][y_index] == 0)
		|| (y_index >= 3 && Astar.maze[x_index][y_index - 2] == 0) || (y_index <= Astar.M - 1 && Astar.maze[x_index][y_index + 2] == 0))
		return 1;
	else
		return 0;
}

//随机创建迷宫
void creatMaze(AStar& Astar, int x_index, int y_index)
{
	int pos, x, y, flag = 0;
	x = x_index;
	y = y_index;

	while (1)
	{
		flag = 0;
		flag = findneighbor(Astar, x, y);
		if (!flag)
			return;
		else {
			Astar.maze[x_index][y_index] = 5;
			x = x_index;
			y = y_index;
			while (1)
			{
				pos = rand() % (4 - 1 + 1) + 1;
				if (pos == 1 && x_index >= 3 && Astar.maze[x_index - 2][y_index] == 0)//上
				{
					x_index -= 2;
				}
				else if (pos == 2 && x_index <= Astar.M - 1 && Astar.maze[x_index + 2][y_index] == 0)//下
				{
					x_index += 2;
				}
				else if (pos == 3 && y_index <= Astar.M - 1 && Astar.maze[x_index][y_index + 2] == 0)
				{
					y_index += 2;
				}
				else if (pos == 4 && y_index >= 3 && Astar.maze[x_index][y_index - 2] == 0)
				{
					y_index -= 2;
				}
				Astar.maze[(x + x_index) / 2][(y + y_index) / 2] = 5;
				Astar.maze[x_index][y_index] = 5;

				//	showmaze(maze, M);

				creatMaze(Astar, x_index, y_index);
				break;
			}
		}
	}
}

//给创建的迷宫创建标识
void makeMaze(AStar& Astar)
{
	for (int i = 0; i < Astar.M + 2; i++)
		for (int j = 0; j < Astar.M + 2; j++)
		{
			if (Astar.maze[i][j] == 5)
			{
				Astar.maze[i][j] = 0;
			}
		}
	Astar.maze[1][1] = 8;
	Astar.maze[Astar.M][Astar.M] = 2;
}

//改变地形,出现更多通路
void MakeDifficult(AStar& Astar)
{
	if (Astar.M > 5)
	{
		int half = (int)((0.4 * Astar.M) * (0.5 * Astar.M));
		int x_c, y_c = 0;
		for (int i = 0; i < half;)
		{
			x_c = rand() % (Astar.M - 1 + 1) + 1;
			y_c = rand() % (Astar.M - 1 + 1) + 1;
			if (Astar.maze[x_c][y_c] == 1)
			{
				Astar.maze[x_c][y_c] = 0;
				i++;
			}
		}
	}
}

//计算H
int getH(position pos, AStar a) {
	return (a.E_row - pos.row) + (a.E_col - pos.col);
}

//创建树节点
treeNode* creatTreeNode(int row, int col)
{
	treeNode* pNew = new treeNode;//开内存
	memset(pNew, 0, sizeof(treeNode)); //全部赋值为0
	pNew->pos.col = col;
	pNew->pos.row = row;
	return pNew;
}

//控制台显示迷宫
void ConsoleShow(AStar& Astar)
{
	cout << endl << "3为走出迷宫的路,1为墙壁,2为出口" << endl;;
	for (int i = 0; i < Astar.M + 2; i++)
	{
		for (int j = 0; j < Astar.M + 2; j++)
		{
			cout << setw(3) << Astar.maze[i][j];
		}
		cout << endl;
	}
}

V. Summary

        There are still some inexplicable bugs in this code, but they can't be changed. Hope it can help everyone.

Guess you like

Origin blog.csdn.net/ClushioAqua/article/details/130310256