C++寻路算法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++寻路算法相关的知识,希望对你有一定的参考价值。

介绍写寻路算法?最好有代码。

迷宫寻找路径要不。。。。。。#include <iostream>
#include<iomanip>
using namespace std;
#define M 10
#define N 10
typedef enumX=0,up,dn,rt,lt tDir; //搜索方向
class Migong

public:
int tag; /*0 1*/
tDir comeDir; /*退回*/
int up,rt,dn,lt; //方向
int back;
;int m[M][N]= //初始化通道数据
0,0,1,1,1,1,1,1,1,1,
1,0,1,1,1,0,1,1,1,1,
1,0,0,0,1,0,1,1,1,1,
1,1,1,0,1,1,1,1,1,1,
1,0,0,0,1,0,0,0,1,1,
1,0,1,1,1,0,1,0,1,1,
1,0,1,0,0,0,1,0,0,1,
1,0,0,0,1,1,0,1,0,1,
1,1,1,1,1,1,0,1,0,1,
1,1,1,1,1,1,1,1,0,0,
; int ini=0,inj=0; //入点
int outi=9,outj=9; //出口
int cnt=0; //从入口到出口需多少步
int path[M][N]; //记录找到路径后的迷宫
Migong maze[10][10]; void initmaze(Migong mz[][10]) //初始化迷宫数据

int i;
int j;
for(i=0;i<10;i++)
for(j=0;j<10;j++)

mz[i][j].tag=m[i][j];
mz[i][j].comeDir=X;
mz[i][j].up=0;
mz[i][j].dn=0;
mz[i][j].rt=0;
mz[i][j].lt=0;
mz[i][j].back=0;

int find(Migong mz[][10],int i,int j,tDir dir) //搜索路径

if(i==outi&&j==outj)
return 1; ////if 当前节点是出口 then return 1;
if(dir!=X) //if 不是是回退到本节点,then 记录来的方向 根据来的方向设定其相对方向已走

mz[i][j].comeDir=dir;
if(dir==up)
mz[i][j].dn=1; //向N方向没有走&&可以走,则向N递归走
if(dir==dn)
mz[i][j].up=1; //向E方向没有走&&可以走,则向N递归走
if(dir==rt)
mz[i][j].lt=1; //向S方向没有走&&可以走,则向N递归走
if(dir==lt)
mz[i][j].rt=1; //向W方向没有走&&可以走,则向N递归走

if(mz[i][j].up==0) //if 向up方向没走&&不越界&&可以走 则向up递归走

int ni;
int nj;
mz[i][j].up=1; //记录本节点
ni=i-1;
nj=j; //ni,nj表示i,j的上一个坐标
if(ni>=0 && mz[ni][nj].tag==0)
if(find(maze,ni,nj,up))
return 1;

if(mz[i][j].dn==0) //if 向dn方向没走&&不越界&&可以走 则向dn递归走

int ni;
int nj;
mz[i][j].dn=1; //记录本节点
ni=i+1;
nj=j; //ni,nj表示i,j的下一个坐标
if(ni<10 && mz[ni][nj].tag==0)
if(find(maze,ni,nj,dn))
return 1;

if(mz[i][j].rt==0) //if 向rt方向没走&&不越界&&可以走 则向rt递归走

int ni;
int nj;
mz[i][j].rt=1; //记录本节点
ni=i;
nj=j+1; //ni,nj表示i,j的右一个坐标
if(nj<10 && mz[ni][nj].tag==0)
if(find(maze,ni,nj,rt))
return 1;

if(mz[i][j].lt==0) //if 向lt方向没走&&不越界&&可以走 则向lt递归走

int ni;
int nj;
mz[i][j].lt=1; //记录本节点
ni=i;
nj=j-1; //ni,nj表示i,j的左一个坐标
if(nj>=0 && mz[ni][nj].tag==0)
if(find(maze,ni,nj,lt))
return 1;


//四个方向都走完了还没有结果

if(i==ini && j==inj) // if 是入口 return 0
return 0;
else // else 则回退

mz[i][j].back=1;
if(mz[i][j].comeDir=up) //如果回退的值等于up的值,则向up方向搜索

int ni;
int nj;
ni=i+1;
nj=j; //ni,nj表示i,j的下一个坐标
if(find(maze,ni,nj,X))
return 1;

if(mz[i][j].comeDir=dn) //如果回退的值等于dn的值,则向dn方向搜索

int ni;
int nj;
ni=i-1;
nj=j; //ni,nj表示i,j的上一个坐标
if(find(maze,ni,nj,X))
return 1;

if(mz[i][j].comeDir=rt) //如果回退的值等于rt的值,则向rt方向搜索

int ni;
int nj;
ni=i;
nj=j-1; //ni,nj表示i,j的左一个坐标
if(find(maze,ni,nj,X))
return 1;

if(mz[i][j].comeDir=lt) //如果回退的值等于lt的值,则向lt方向搜索

int ni;
int nj;
ni=i+1;
nj=j+1; //ni,nj表示i,j的左下角一个坐标
if(find(maze,ni,nj,X))
return 1;


return 0;
int onway( Migong nd) //判断点是否在路径上

if(nd.tag!=0)
return 0; //墙
if(nd.up==0&&nd.dn==0&&nd.rt==0&&nd.lt==0)
return 0; //没访问过
if(nd.up==1&&nd.dn==1&&nd.rt==1&&nd.lt==1&&nd.back==1)
return 0; //访问过但不通
return 1;
//打印
void print( Migong mz[][10]) //打印原迷宫

int i;
int j;
cout<<"0表示可通过,1表示墙"<<endl;
cout<<"-------------------------------";
cout<<endl<<" ";
cout<<"▂▂▂▂▂▂▂▂"<<endl;
for(i=0;i<10;i++)

cout<<setw(10)<<"▎ ";
for(j=0;j<10;j++)

cout<<mz[i][j].tag;
cout<<" ▎"<<endl;


cout<<" ";
cout<<"▂▂▂▂▂▂▂▂"<<endl;
void print1( Migong mz[][10]) //打印含路径迷宫

int i;
int j;
cout<<endl<<" ";
cout<<"▂▂▂▂▂▂▂▂"<<endl;

for(i=0;i<10;i++)

cout<<setw(10)<<"▎ ";
for(j=0;j<10;j++)

if(i==9 && j==9) //出口

cnt++;
cout<<"*";
path[i][j]=0;

else
if(onway(mz[i][j]))

cout<<"*"; //*表示路径
cnt++;
path[i][j];

else

cout<<mz[i][j].tag;
path[i][j]=1; //path[i][j]=0表示可通过,path[i][j]=1表示墙

cout<<" ▎"<<endl;
cout<<" ";
cout<<"▂▂▂▂▂▂▂▂"<<endl;
void print2( Migong mz[][10]) //打印路径坐标

int i;
int j;
int di;
int dj; //di,dj的值为-1,0,1,为了搜索坐标i,j附近的坐标
int pi;
int pj; //pi,pj为上一个路径坐标
int r;
int count; //统计输出了多少个坐标
i=0;
j=0;
pi=1;
pj=0;
count=0; cout<<endl<<"路径坐标为:"<<endl<<endl;
for(r=0;r<cnt;r++)

if(i>=10 || j>=10) //i,j的值不可能大于10
continue;
else

for(di=-1;di<2;di++)
for(dj=-1;dj<2;dj++)

if(di==dj ) //path[i][j]的下一步不可能在它的左上角和右下角
continue;
if(di==1 && dj==-1) //path[i][j]的下一步不可能在它的左下角
continue;
if(di==-1 && dj==1) //path[i][j]的下一步不可能在它的右上角
continue;
if((i+di)<0 ||(j+dj)<0) //i+di,j+dj小于0时不符
continue;
if(path[i+di][j+dj]!=0) //i+di,j+dj不是路径上的坐标
continue;
if((i+di)==i && (j+dj)==j ) //path[i][j]的下一步不可能是它本身
continue;
else
if((i+di)==pi && (j+dj)==pj) //path[i][j]的下一步不可能是它的上一步
continue;
else
if(i==9&&j==9)<br> cout<<"(9,9)";<br> else<br> <br> <br> cout<<"("<<i<<","<<j<<")"<<"->";<br> count++;<br> if(count%5==0)<br> cout<<endl;<br>
pi=i;
pj=j;
i=i+di;
j=j+dj;



int main()

initmaze(maze); //初始化迷宫
cout<<endl<<endl<<"原迷宫如下:"<<endl;
print(maze); //打印原迷宫
if (find(maze,ini,inj,rt)) //如果迷宫有路径


cout<<"-------------------------------";
cout<<endl<<"含路径的迷宫,*表示通道"<<endl;
cout<<"-------------------------------";
print1(maze); // 输出含路径的迷宫
cout<<endl<<"-------------------------------"<<endl;
print2(maze); //输出路径坐标

else
cout<<"no way!"; //如果迷宫没路径,输出no way
cout<<endl<<endl<<endl;
cout<<"从入口到出口需"<<cnt<<"步";
cout<<endl<<endl;
return 0;
参考技术A 用Dijkstra算法,我做的给你个参考,下面各点是有距离的(移动力),BIG表示不可通过

#define BIG 32767 //无穷大
int pre[6]=0; //这是关键!pre数组用于记录每个点的前趋,这样算出最短路径后,就可以递归出路径经过的点
int Distance[6]; //用于记录从起点到达每个点的最短路径长度
int Cost[6][6]=0,1450,1650,BIG,BIG,BIG, //有向网的邻接矩阵,这里以6个点为例
        1450,0,BIG,1350,2350,BIG,
        1650,BIG,0,BIG,2550,BIG,
        BIG,1350,BIG,0,BIG,1200,
        BIG,2350,2550,BIG,0,850,
        BIG,BIG,BIG,1200,850,0,
        ;

//Dijkstra算法函数,求给定点到其余各点的最短路径
//参数:邻接矩阵、顶点数、出发点的下标、结果数组、每个点的前趋
void Dijkstra(int Cost[][6],int n,int v0,int Distance[],int pre[])

  int s[6];
  int mindis,dis;
  int i,j,u;
  //初始化
  for(i=0;i<n;i++)
    Distance[i]=Cost[v0][i];
    s[i]=0;
  
  s[v0] =1; //标记v0
  //在当前还未找到最短路径的顶点中,寻找具有最短距离的顶点
  for(i=0;i<n;i++)
    if(Distance[i]<BIG) pre[i]=v0;
  
  for(i=1;i<n;i++) //每循环一次,求得一个最短路径
    mindis=BIG;
    for (j=0;j<n;j++) //求离出发点最近的顶点
      if(s[j]==0&&Distance[j]<mindis)
        mindis=Distance [j];
        u=j;
      
    for(j=0;j<n;j++) //修改递增路径序列(集合)
      if(s[j]==0) //对还未求得最短路径的顶点
        //求出由最近的顶点 直达各顶点的距离
        dis=Distance[u] +Cost[u][j];
        //如果新的路径更短,就替换掉原路径
        if(Distance[j]>dis)
          Distance[j]=dis;
          pre[j]=u;
        
      
    s[u]=1; // 标记最短路径已经求得
  


用Dijkstra函数算出最短路径后,就可以递归出从给定顶点到各点的最短路径上的每个点了,函数如下(不含终点):

char pathres[100]=""; //保存路径
char *Vertex[6]="福州","上海","广州","北京","成都","西安";
//参数:起点、终点
void shpath(int st,int ed) //起点应该为Dijkstra函数中的v0

  if(pre[ed]!=st)
    shpath(st,pre[ed]);
  
  strcat(pathres,Vertex[pre[ed]]);
  strcat(pathres,"-");
//最后要在主函数中把终点加到pathres里
参考技术B ⒈问题描述
从一个迷宫的入口到出口找出一条最短路经。用一个二维数组MAZE(1:m,1:n)模拟迷宫,数组元素为0表示该位置可以通过,数组元素为1表示该位置不可以通行。MAZE(1,1)和MAZE(m,n)分别为迷宫的入口和出口。

⒉基本要求
输入数据
输入迷宫的大小m行和n列,两者为整数
由随机数产生0或1,建立迷宫。
输出数据
首先输出模拟迷宫的二维数组,若存在最短路经,则由出口回朔到入口打印这一条路径,如下所示:
(m,n), ……, (I,j), ……, (1,1)
如无通道,则打印:
THERE IS NO PATH.
⒊实现提示
数据结构
⑴为了在程序中判断方便,把迷宫扩展成为MAZE(0:m+1,0:n+1),扩展部分的元素设置为1,相当于在迷宫周围布上一圈不准通过的墙,这样,在迷宫的任一位置(I,j)上都有八个可以移动的方向。
⑵用二维数组MOVE(1:8,1:2)存放八个方向上的位置量。
参考技术C 1楼,dijkstra正解还有一些寻路算法Bellman-ford、SPFA全源Wallshall-floyd算法如果节点之间有几何规律,那么最牛X的是A*算法

A*寻路算法所生成的路径

本文目的是对A*寻路算法所生成的路径进行一些人性化的调整,使其看起来不至于太机械化。关于A*算法的原理与实现,读者可以阅读其他资料,这里不再详细阐述。

如何写估价函数

        A*寻路算法本质上是一个有方向性的广度优先搜索算法,它使用一个估价函数,来估测可能的最短路径,在每一次搜索迭代完成后,选取其邻接点中最优的一个(即,距离终点最近的一个点),作为下一次迭代的起点。如此反复,直到找到终点。下面先列出估价函数的常规写法:


        设i点到起点的价值为S,到终点的估价为E,i点的总估价G等于S+E。S的值是确定的:


[cpp] view plain copy 技术分享技术分享

  1. S = parent.S + 1(i点是其父节点的水平或垂直方向上的邻接点)  

  2. 或  

  3. S = parent.S + sqrt(2))(i点是其父节点斜方向上的邻接点)  


E点的值需要估算。精确一点的写法:


[cpp] view plain copy 技术分享技术分享

  1. 水平距离:dx = abs(ix - ex)  

  2. 垂直距离:dy = abs(iy - ey)  

  3. 需要斜着走过的距离:v1 = min(dx, dy) * sqrt(2)  

  4. 需要直线走过的距离:v2 = max(dx, dy) - min(dx, dy)  

  5. E =  v1 + v2  


粗略的写法:


[cpp] view plain copy 技术分享技术分享

  1. E = abs(ix - ex) + abs(iy - ey)  


如何避免转向抖动

        A*寻路得到的结果是最优的,但不是唯一的,这源于两点之间最近的路线可能不只一条。那么问题就产生了,两条最佳路线距离都相等的情况下,哪一条会更好?

 技术分享   技术分享

(红色是障碍,白色可通行,黑色是搜索路径)

        如上图所示,是A* 8方向搜索得到的两条距离相等的路线,但是左图的路线在中间位置发生了“拐弯”,要比右图的路线多一个“拐弯”。如果路线上拐弯太多,人物行走的过程中,会出现频繁转向,从而出现“抖动”现象。所以,我们判定右图路线优于左图路线。针对这一问题,我们可以通过修改估价函数,来选择“拐弯”更少的路线。

        拐弯的问题,可以简化成先尽可能的向一个方向走,然后再考虑转向。进一步简化成,点越接近起点或是终点,越优先考虑。我们给E加上一个干扰值factor,


[cpp] view plain copy 技术分享技术分享

  1. factor = min(abs(ix - sx), abs(ix - ex)) + min(abs(iy - sy), abs(iy - ey))  

  2. factor *= 0.01  


factor的值不能过大,否则会造成搜索结果不是最短距离,因此适当的给factor乘上一个缩放系数。

如何远离障碍物

        A*寻路的效果是抄近道,走捷径。但对于游戏体验来说,这并不完全是件好事,放着宽阔的马路不走,非得走悬崖峭壁,一不小心就跌落万丈深渊,或者卡在岩石边上。那么,我们该如何避免这些现象呢?同样,我们可以通过修改估计函数做到。

        我们给每一块可走区域都加上一个干扰值,越靠近障碍的可走区域,其干扰值越大。干扰值计算方法:


[cpp] view plain copy 技术分享技术分享

  1. factor = 0  

  2. for(x = -n; x <= n; ++x)  

  3. {  

  4.       for(y = -n; y <= n; ++y)  

  5.      {  

  6.         if(isObstacle(ix + x, iy + y))  

  7.         {  

  8.             factor += n - min(abs(x), abs(y))  

  9.         }  

  10.      }  

  11. }  


        我们甚至可以根据地表的材质来增加干扰值,比如山路和沼泽地带明显比马路的干扰值大。

后记

        总之,我们可以调节估价函数来达到不同的效果。但是,也不能随意修改,不良的估价函数,会增加搜索成本。


以上是关于C++寻路算法的主要内容,如果未能解决你的问题,请参考以下文章

游戏的寻路算法

寻路算法和逻辑算法之间异同点都有哪些

教程翻译-理解基于矢量场寻路算法

寻路算法之A*算法详解

寻路算法--- 广度优先搜索

Pacman 的寻路算法