A星寻路算法的实现,单击左键编辑障碍物,单击右键清除障碍物,双击右键开始自动寻路。
AStar.h:
#pragma once
#include <windows.h>
#include <vector>
#define F_H_WHITE 0x0004 | 0x0002 | 0x0001 | 0x0008
#define B_H_WHITE 0x0010|0x0020|0x0040|0x0080
#define F_H_YELLOW 0x0002|0x0004|0x0008
#define F_H_GREEN 0x0002|0x0008
#define F_RED 0x0004
using std::vector;
class CAStar
{
public:
CAStar();
~CAStar();
public:
//表的节点结构体
typedef struct _NODE
{
COORD stcPos; //当前点的坐标
COORD stcPrtPos; //上一个点的坐标
int F; //移动损耗
int G; //估算距离
int H; //G+H
}NODE,*PNOED;
public:
//寻路函数,获取最短路径
bool GetPath(_Out_ vector<COORD>& vecPath);
//设置控制台信息
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
void SetConsoleInfo();
//描点画图函数
void WriteChar(int x, int y, wchar_t* pszChar, WORD wArr);
//画地图
void DrawMap();
//编辑地图
void EditMap();
private:
COORD m_Begin; //起始点
COORD m_End; //终止点
int m_Map[40][40]; //地图
vector<NODE> m_vecOpen; //Open表
vector<NODE> m_vecClose;//Close表
};
AStar.cpp:
#include "stdafx.h"
#include "AStar.h"
CAStar::CAStar()
{
m_Begin.X = 10;
m_Begin.Y = 10;
m_End.X = 30;
m_End.Y = 30;
}
CAStar::~CAStar()
{
}
//************************************************************
// 函数名称: GetPath
// 函数说明: 寻路函数,获取最短路径
// 作 者: Dylan
// 时 间: 2015/08/28
// 参 数: _Out_ vector<COORD> & vecPath
// 返 回 值: bool
//************************************************************
bool CAStar::GetPath(_Out_ vector<COORD>& vecPath)
{
//0.起点就是终点,直接返回
if (m_Begin.X == m_End.X&&m_Begin.Y == m_End.Y)
{
return true;
}
//1.将起始点加入到Open表中
NODE FirstNode = { 0 };
FirstNode.stcPos = m_Begin;
FirstNode.G = 0;
FirstNode.H =
abs(FirstNode.stcPos.X - m_End.X) +
abs(FirstNode.stcPos.Y - m_End.Y);
FirstNode.F = FirstNode.G + FirstNode.H;
m_vecOpen.push_back(FirstNode); //Open表中有了第一个点
while (true)
{
//2.开始循环,从Open表中,选取F值最小的点
int nMin = m_vecOpen[0].F;
int nPos = 0;
int nSize = m_vecOpen.size();
for (int i = 0; i < nSize; i++)
{
if (nMin>m_vecOpen[i].F)
{
nMin = m_vecOpen[i].F;
nPos = i;
}
}
//3.从此点派生出周围的四个点,并为其赋值
NODE stcTemp[4] = { 0 };
//上
stcTemp[0].stcPos.X = m_vecOpen[nPos].stcPos.X;
stcTemp[0].stcPos.Y = m_vecOpen[nPos].stcPos.Y - 1;
//下
stcTemp[1].stcPos.X = m_vecOpen[nPos].stcPos.X;
stcTemp[1].stcPos.Y = m_vecOpen[nPos].stcPos.Y + 1;
//左
stcTemp[2].stcPos.X = m_vecOpen[nPos].stcPos.X - 1;
stcTemp[2].stcPos.Y = m_vecOpen[nPos].stcPos.Y ;
//右
stcTemp[3].stcPos.X = m_vecOpen[nPos].stcPos.X + 1;
stcTemp[3].stcPos.Y = m_vecOpen[nPos].stcPos.Y;
for (int i = 0; i < 4;i++)
{
stcTemp[i].stcPrtPos = m_vecOpen[nPos].stcPos;
stcTemp[i].G = m_vecOpen[nPos].G + 1;
stcTemp[i].H =
abs(stcTemp[i].stcPos.X - m_End.X) +
abs(stcTemp[i].stcPos.Y - m_End.Y);
stcTemp[i].F = stcTemp[i].G + stcTemp[i].H;
}
//3.1将此最小点,将其加入到Close表中,从Open表中删除
m_vecClose.push_back(m_vecOpen[nPos]);
m_vecOpen.erase(m_vecOpen.begin() + nPos);
//4.检测一下这四个点是否可用
for (int i = 0; i < 4; i++)
{
int nX = stcTemp[i].stcPos.X;
int nY = stcTemp[i].stcPos.Y;
//4.0找到了终点
if (nX == m_End.X&&nY == m_End.Y)
{
//4.0.1已经找到终点,从Close表中进行回溯,得到最短的路径,退出
//回溯
//-------------------------------------------------
vecPath.push_back(stcTemp[i].stcPos);
NODE PathNode = stcTemp[i];
for (int i = m_vecClose.size()-1; i >= 0; i--)
{
if (PathNode.stcPrtPos.X == m_vecClose[i].stcPos.X&&
PathNode.stcPrtPos.Y == m_vecClose[i].stcPos.Y
)
{
vecPath.push_back(m_vecClose[i].stcPos);
PathNode = m_vecClose[i];
}
}
//-------------------------------------------------
m_vecOpen.clear();
m_vecClose.clear();
return true;
}
//4.1是否越界
if (nX<0||nY<0||nX>39||nX>39)
{
continue;
}
//4.2是否在障碍物上
if (m_Map[nY][nX]!=0)
{
continue;
}
//4.3是否在Open表中
int j = 0;
for (; j < m_vecOpen.size();j++)
{
if (m_vecOpen[j].stcPos.X == nX&&m_vecOpen[j].stcPos.Y == nY)
{
break;
}
}
if (j != m_vecOpen.size())
{
continue;
}
//4.4是否在Close表中
for (j = 0; j < m_vecClose.size();j++)
{
if (m_vecClose[j].stcPos.X == nX&&m_vecClose[j].stcPos.Y == nY)
{
break;
}
}
if (j != m_vecClose.size())
{
continue;
}
//4.5将可用的点加入到Open表中
m_vecOpen.push_back(stcTemp[i]);
}
//5.Open表中没有点了,说明没有找到路径,退出函数
if (m_vecOpen.size()==0)
{
m_vecClose.clear();
return false;
}
}
}
//************************************************************
// 函数名称: SetConsoleInfo
// 函数说明: 设置控制台信息
// 作 者: Dylan
// 时 间: 2015/08/28
// 返 回 值: void
//************************************************************
void CAStar::SetConsoleInfo()
{
//设置控制台标题
SetConsoleTitle(L"A星寻路算法");
//设置控制台缓冲区大小
COORD BufferSize = { 99, 42 };
SetConsoleScreenBufferSize(hStdOut, BufferSize);
//设置控制台窗口大小
SMALL_RECT strctWindow = { 0, 0, BufferSize.X - 1, BufferSize.Y - 1 };
SetConsoleWindowInfo(hStdOut, true, &strctWindow);
//设置光标位置
COORD pos = { 0, 40 };
SetConsoleCursorPosition(hStdOut, pos);
}
//************************************************************
// 函数名称: WriteChar
// 函数说明: 描点画图函数
// 作 者: Dylan
// 时 间: 2015/08/28
// 参 数: int x
// 参 数: int y
// 参 数: wchar_t * pszChar
// 参 数: WORD wArr
// 返 回 值: void
//************************************************************
void CAStar::WriteChar(int x, int y, wchar_t* pszChar, WORD wArr)
{
CONSOLE_CURSOR_INFO cci;
cci.dwSize = 1;
cci.bVisible = FALSE;//光标是否可见
SetConsoleCursorInfo(hStdOut, &cci);//将光标属性应用到控制台
COORD loc = { x * 2, y };
DWORD dwRes1, dwRes2;
WORD wClr[10];
wmemset((wchar_t*)wClr, wArr, 10);
WriteConsoleOutputAttribute(hStdOut, wClr, 2, loc, &dwRes1);//打印字符串颜色
WriteConsoleOutputCharacter(hStdOut, pszChar, 1, loc, &dwRes2); //打印字符串到指定位置
}
//************************************************************
// 函数名称: DrawMap
// 函数说明: 画地图
// 作 者: Dylan
// 时 间: 2015/08/28
// 返 回 值: void
//************************************************************
void CAStar::DrawMap()
{
SetConsoleInfo();
WriteChar(m_Begin.X, m_Begin.Y, L"☆", F_H_YELLOW);
WriteChar(m_End.X, m_End.Y, L"★", F_H_YELLOW);
for (int i = 0; i < 40; i++)
{
for (int j = 0; j < 40; j++)
{
m_Map[i][j] = 0;
if (i == 0 || j == 0 || i == 39 || j == 39)
{
m_Map[i][j] = 1;
}
}
}
for (int i = 0; i < 40; i++)
{
for (int j = 0; j < 40; j++)
{
if (m_Map[j][i] == 1)
{
WriteChar(i, j, L"▇", B_H_WHITE | F_RED);
}
}
}
}
//************************************************************
// 函数名称: EditMap
// 函数说明: 编辑地图
// 作 者: Dylan
// 时 间: 2015/08/28
// 返 回 值: void
//************************************************************
void CAStar::EditMap()
{
//画张空地图
DrawMap();
//获取标地图准输入输出句柄
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO bInfo;
INPUT_RECORD mouseRec;
DWORD res;
COORD crPos = { 0, 0 }, crHome = { 82, 2 };
SetConsoleMode(hIn, ENABLE_MOUSE_INPUT);//设置鼠标可用,这样cls以后鼠标就不会不好使
while (true)
{
ReadConsoleInput(hIn, &mouseRec, 1, &res);
if (mouseRec.EventType == MOUSE_EVENT)
{
if (mouseRec.Event.MouseEvent.dwButtonState == RIGHTMOST_BUTTON_PRESSED)
{
if (mouseRec.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK)
{
//双击右键退出循环,画路径
break;
}
}
//设置坐标显示信息
crPos = mouseRec.Event.MouseEvent.dwMousePosition;
GetConsoleScreenBufferInfo(hOut, &bInfo);
SetConsoleTextAttribute(hOut, F_H_WHITE);
SetConsoleCursorPosition(hOut, crHome);
printf("坐标X: %2lu Y: %2lu", crPos.X, crPos.Y);
SetConsoleCursorPosition(hOut, bInfo.dwCursorPosition);
//鼠标点击事件
switch (mouseRec.Event.MouseEvent.dwButtonState)
{
//单击左键
case FROM_LEFT_1ST_BUTTON_PRESSED:
if (crPos.X > 1 && crPos.X<77 && crPos.Y>0 && crPos.Y < 39)
{
crPos.X = crPos.X / 2 * 2;
if (!((crPos.X / 2 == m_Begin.X&&crPos.Y == m_Begin.Y) ||
(crPos.X / 2 == m_End.X&&crPos.Y == m_End.Y)))
{
WriteChar(crPos.X / 2, crPos.Y, L"▇", B_H_WHITE | F_RED);
m_Map[crPos.Y][crPos.X / 2] = 1;
}
}
break;
//单击右键
case RIGHTMOST_BUTTON_PRESSED:
if (crPos.X > 1 && crPos.X<77 && crPos.Y>0 && crPos.Y < 39)
{
crPos.X = crPos.X / 2 * 2;
if (!((crPos.X / 2 == m_Begin.X&&crPos.Y == m_Begin.Y) ||
(crPos.X / 2 == m_End.X&&crPos.Y == m_End.Y)))
{
WriteChar(crPos.X / 2, crPos.Y, L" ",NULL);
m_Map[crPos.Y][crPos.X / 2] = 0;
}
}
break;
}
}
}
}
A_Star.cpp:
// A_Star.cpp : 定义控制台应用程序的入口点。
// A星算法
#include "stdafx.h"
#include "AStar.h"
int _tmain(int argc, _TCHAR* argv[])
{
vector<COORD> Path;
CAStar obj;
obj.EditMap();
obj.GetPath(Path);
//画路线
for (int i = Path.size() - 2;i>0;i--)
{
obj.WriteChar(Path[i].X, Path[i].Y, L"☉", F_H_GREEN);
}
system("pause");
return 0;
}
演示效果: