A*寻路算法

发布于 2018-06-05  794 次阅读


参考博客

A星算法详解
英文详解

代码部分(之前的那个有点问题)

#include <iostream>
#include <algorithm>
#include <list>
#include <cstdlib>
#include <ctime>
#include <windows.h>

using namespace std;

const int maxn = 100;
struct node{
	int x, y;
	int f, g, h;
	char dir; 
	node(){
		f = 0;
		g = 0;
		h = 0;
	}
};
int dirx[8] = {-1, -1, -1,  0, 0,  1, 1, 1}; // 8个方向 
int diry[8] = {-1,  0,  1, -1, 1, -1, 0, 1};
int n, m;
string ans;
class aStart{
public:
	list<node> openSet;
	list<node> closeSet;
	char maze[maxn][maxn];
	node map[maxn][maxn], road[maxn][maxn], start, fin;
	
	
	void initMap();
	void drawMap();
	void debug();
	int getH(node a, node b);
	int getG(int i);
	void getDir(int i, node nei);
	bool isInclude(list<node> arr, node qur);
	void dfs(int x, int y);	
	void aStartSearch();		
};

void color(int x) // 给文字上色 
{
	if(x >= 0 && x <= 15)
		SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),x);
	else
		SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);	
}

void gotoxy(int x,int y) // 将光标移动到指定坐标 
{
	COORD pos = {x,y};
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(hOut,pos);
}

void aStart::initMap(){ // 初始化地图 
	memset(map, 0, sizeof(map));
	memset(road, 0, sizeof(road));
	memset(maze, 0, sizeof(maze));
	cin >> n;
	m = n;
	for(int i = 0; i <= n+1; i++){
		maze[i][0] = '#';
		maze[0][i] = '#';
		maze[n+1][i] = '#';
		maze[i][n+1] = '#';
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			cin >> maze[i][j];
			if(maze[i][j] == 's'){
				start.x = i;
				start.y = j;
			}
			if(maze[i][j] == 'e'){
				fin.x = i;
				fin.y = j;
			}
			map[i][j].x = i;
			map[i][j].y = j;
		}
	}
}

void aStart::drawMap(){ // 打印地图 
	gotoxy(0, 0);
	for(int i = 0; i <= n+1; i++){
		for(int j = 0; j <= m+1; j++){
			if(maze[i][j] == '@'){
				color(4);
				cout << '@';
			}else if(maze[i][j] == '#'){
				color(7);
				cout << maze[i][j];
			}
			else{
				cout << ' ';
			}
		}
		cout << endl;
	}
}

void aStart::debug(){ // 打印openSet和closeSet中的点 
	list<node>::iterator it;
	for(it = openSet.begin(); it != openSet.end(); it++){
		gotoxy(it->y, it->x);
		color(4);
		cout << '*';
	}
	for(it = closeSet.begin(); it != closeSet.end(); it++){
		gotoxy(it->y, it->x);
		color(5);
		cout << '$';
	}
}

int aStart::getH(node a, node b){ // 计算当前点到终点的距离 h
	int h_dia = min(abs(a.x-b.x), abs(a.y-b.y));
	int h_str = abs(a.x-b.x) + abs(a.y-b.y);
	return (14 * h_dia + 10 * (h_str - 2*h_dia));
}

int aStart::getG(int i){ // 走下一步需要的花费 g 
	switch(i){
		case 0:
		case 2:
		case 5:
		case 7:return 14; // 走斜线 
		case 1:
		case 3:
		case 4:
		case 6:return 10; // 走直线 
	}
}

bool cmp(node a, node b){ // 对 list中的f继续排序  
	//if(a.f != b.f)
		return a.f < b.f;
	//return a.h < b.h;
}

bool aStart::isInclude(list<node> arr, node qur){ // 判断一个节点是否在list中 
	list<node>::iterator it;
	for(it = arr.begin(); it != arr.end(); it++){
		if(it->x == qur.x && it->y == qur.y){
			return true;
		}
	}
	return false;
}

void aStart::dfs(int x, int y){ // 回溯路径 
	if(x == start.x && y == start.y)
		return;
	else
		dfs(road[x][y].x, road[x][y].y);
	maze[x][y] = '@';
	ans += road[x][y].dir; 
}

void aStart::getDir(int i, node nei){
	switch(i){
		case 0: road[nei.x][nei.y].dir = 'Q'; break;
		case 1: road[nei.x][nei.y].dir = 'W'; break;
		case 2: road[nei.x][nei.y].dir = 'E'; break;
		case 3: road[nei.x][nei.y].dir = 'A'; break;
		case 4: road[nei.x][nei.y].dir = 'D'; break;
		case 5: road[nei.x][nei.y].dir = 'Z'; break;
		case 6: road[nei.x][nei.y].dir = 'X'; break;
		case 7: road[nei.x][nei.y].dir = 'C'; break;
	}
}

void aStart::aStartSearch(){
	int cnt = 0, cou = n, flag = 0;
	openSet.clear();
	closeSet.clear();
	openSet.push_front(start);
	while(!openSet.empty()){
		openSet.sort(cmp);
		node now = openSet.front();
		/*if(cnt >= 50){
			cou += n;
			cnt = 0;
		}
		gotoxy(cou+2, cnt++);
		cout << now.f << "--" << now.x << "--" << now.y << endl; 
		gotoxy(cou+2, cnt++);
		cout << "--------------------" << endl;
		*/
		if(now.x == fin.x && now.y == fin.y){ // 当终点在openSet中时结束循环 
			dfs(fin.x, fin.y);
			drawMap();
			cout << ans << endl;
			cout << "DONE" << endl;
			break;
		}
		openSet.pop_front();
		closeSet.push_back(now);
		for(int i = 0; i < 8; i++){
			flag = 0;
			node nei = map[now.x+dirx[i]][now.y+diry[i]];
			if(maze[now.x+dirx[i]][now.y+diry[i]] == '#' || now.x+dirx[i] >= n+1 || now.x+dirx[i] <= 0 || now.y+diry[i] >= m+1 || now.y+diry[i] <= 0)
		 		continue;
 			// 判断改节点是否在closeSet中 
		 	if(isInclude(closeSet, nei))
		 		continue;
	 		node tmp; // 临时节点,用来判断是否需要改变当前邻居的属性 
	 		tmp.g = now.g + getG(i); // 计算新的g值 
	 		//cout << now.g << endl;
	 		if(!isInclude(openSet, nei)){ // 如果不在openSet中,则加入并更新父节点 
	 			getDir(i, nei);
	 			road[nei.x][nei.y].x = now.x; 
				road[nei.x][nei.y].y = now.y;
	 			openSet.push_front(nei);
	 			flag = 1;
	 		}
		 	else if(tmp.g >= nei.g) // 如果当前节点的g比原来的g大,直接跳过 
		 		continue;
	 		// 如果比较小
	 		if(flag == 1) // 如果当前节点原来不在openSet中,且g更小 
		 		openSet.pop_front(); // 先把之前的节点删除
			// 更新父节点信息 
			getDir(i, nei);
			road[nei.x][nei.y].x = now.x; 
			road[nei.x][nei.y].y = now.y;
			// 更新改邻居节点的属性
		 	nei.g = tmp.g;
		 	nei.h = getH(nei, fin); 
		 	nei.f = nei.g + nei.h; // f = g + h;
		 	map[now.x+dirx[i]][now.y+diry[i]] = nei;
		 	openSet.push_front(nei); // 加入到openSet中 
 			//gotoxy(cou+2, cnt++);
		 	//cout << map[now.x+dirx[i]][now.y+diry[i]].f << '-' << map[now.x+dirx[i]][now.y+diry[i]].x << '-' << map[now.x+dirx[i]][now.y+diry[i]].y << endl;
		}
		//gotoxy(cou+2, cnt++);
		//cout << "--------------------" << endl;
		debug();
		Sleep(100);
	}
}

int main(){
	aStart t;
	while(1){
		t.initMap();
		t.drawMap();
		t.aStartSearch();
	}
}