不再依赖A*,利用C++编写全新寻路算法
一,说在前面的话
大概在半年前,看见一到信息竞赛题:在任意方格阵中设置障碍物,确定起始点后,求这两点之间路径。当时觉得蛮有意思的,但是没有时间去做,今天花了两个小时来实现它。据说有一个更高级的寻路算法叫做a*, 那我就把我的算法叫做W*。
这个算法主要用于解迷宫和实现战棋游戏(SLG)的寻路。
首先讲一讲我的算法的思路:
我们先确定起始点,然后从起点出发,按一定顺序判断这个位置上下左右是否有可走的位置,如果发现有可走的位置,则递归进入该位置的判断。在递归的同时记录所走的路线。当发现某个位置无路可走,则删除路线的最后一个位置并返回上级位置进行判断。如此反复尝试最终找到路线。
说了这么多,就来讲解一下代码吧。
二,讲解部分
包含头文件(全部都是stl中的):
#include <map>
#include <vector>
#include <iostream>
为几个冗长的类型重命名,用来使后来的代码更明了。
typedef unsigned int uint;
typedef std::vector<int> CRow;
//相当于把CLabyrinth定义成一个整型的二维数组
typedef std::vector<CRow> CLabyrinth;
定义一个类类型表示二维数组中的位置:
class CPoint
{ public: int col; //列
int row; //行 public: //构造函数,接受行和列的初始化
CPoint(int c = , int r = )
: col(c)
, row(r)
{
return;
} //赋值操作
CPoint& operator=(const CPoint& pt)
{
col = pt.col;
row = pt.row;
return *this;
} //比较操作
bool operator==(const CPoint& pt)
{
return col == pt.col && row == pt.row;
} //判断该位置是否合法
bool allRight()
{
return col >= && row >= ;
} }; typedef std::vector<CPoint> CRoute;
然后到了核心类类型CLabyrinthAI
{
protected:
//装有迷宫数据的二维数组
CLabyrinth m_xLabyrinth;
//起点位置
CPoint m_ptBeginning;
//终点位置
CPoint m_ptEnding;
//记录路线的数组
CRoute m_vRoute;
public:
//枚举表示起点、终点的值
enum{Beginning = -, Ending = -};
//枚举表示障碍物与可走区的值
enum{CanntGo = , CanGo = };
//枚举是否找到终点
enum{FoundEnding = , NotFoundEnding = };
protected:
//判断某个位置是否已在路线数组中,用于别走重复的路
bool isRepeat(const CPoint& pt)
{
bool bRes = false;
CRoute::iterator it = m_vRoute.begin();
for(; it != m_vRoute.end(); it++){
CPoint pt0 = *it;
if(pt0 == pt){
bRes = true;
break;
}
}
return bRes;
}
//将某一位置加入路线数组
void advance(const CPoint& ptTo)
{
m_vRoute.push_back(ptTo);
}
//将路线数组最后一个位置弹出
void back()
{
m_vRoute.pop_back();
}
//判断某一位置是否是起点
bool isBeginning(const CPoint& pt)
{
return m_ptBeginning == pt;
}
//判断某一位置是否是终点
bool isEnding(const CPoint& pt)
{
return m_ptEnding == pt;
}
/*-----------------核心算法------------------------*/
//判断某一位置是否可以向上移动
CPoint canUp(const CPoint& ptCurrent) //接受当前位置
{
CPoint ptRes = CPoint(-, -);
int col = ptCurrent.col;
int row = ptCurrent.row;
if(row > ){
CPoint ptNext = CPoint(col, row - ); //上移后位置
//检查上移后位置是否已经走过,以免寻路过程中绕圈子进入死循环
if(!isRepeat(ptNext)){
//获得迷宫二维数组中上移后位置的属性(起点、终点、可走、障碍)
int nAttr = m_xLabyrinth[ptNext.row][ptNext.col];
//如果上移后位置为可走或到达终点,则设定返回值为上移后的位置
if(nAttr == CanGo || nAttr == Ending){
ptRes = ptNext;
}
}
}
return ptRes; //如果上移后位置不可走则返回非法的位置
}
//以下判断某一位置可否移动的原理大致与上相同,就不多说了
//判断某一位置是否可以向下移动
CPoint canDown(const CPoint& ptCurrent)
{
CPoint ptRes = CPoint(-, -);
int col = ptCurrent.col;
int row = ptCurrent.row;
if(row < m_xLabyrinth.size() - ){
CPoint ptNext = CPoint(col, row + );
if(!isRepeat(ptNext)){
int nAttr = m_xLabyrinth[ptNext.row][ptNext.col];
if(nAttr == CanGo || nAttr == Ending){
ptRes = ptNext;
}
}
}
return ptRes;
}
//判断某一位置是否可以向左移动
CPoint canLeft(const CPoint& ptCurrent)
{
CPoint ptRes = CPoint(-, -);
int col = ptCurrent.col;
int row = ptCurrent.row;
if(col > ){
CPoint ptNext = CPoint(col - , row);
if(!isRepeat(ptNext)){
int nAttr = m_xLabyrinth[ptNext.row][ptNext.col];
if(nAttr == CanGo || nAttr == Ending){
ptRes = ptNext;
}
}
}
return ptRes;
}
//判断某一位置是否可以向右移动
CPoint canRight(const CPoint& ptCurrent)
{
CPoint ptRes = CPoint(-, -);
int col = ptCurrent.col;
int row = ptCurrent.row;
if(col < m_xLabyrinth[].size() - ){
CPoint ptNext = CPoint(col + , row);
if(!isRepeat(ptNext)){
int nAttr = m_xLabyrinth[ptNext.row][ptNext.col];
if(nAttr == CanGo || nAttr == Ending){
ptRes = ptNext;
}
}
}
return ptRes;
}
/*
*判断某一位置是否可以向四周移动,如果判断到某一位置可以移动,则递归进入该位置判断。
*如果该位置没有任何位置可移动,则返会上级位置并且调用back函数。如果走到终点,
*则立刻返回枚举值FoundEnding,上级位置检查到返回值为FoundEnding,也直接返回。
*/
int findRoute(const CPoint& ptCurrent)
{
int nRes = NotFoundEnding; //默认返回值为没有找到终点
CPoint ptNext = CPoint(-, -);
advance(ptCurrent); //将当前位置加入路线数组
//判断当前位置是否是终点,如果是终点则不进行下面的判断,将返回值设置为找到终点
if(isEnding(ptCurrent)){
nRes = FoundEnding;
}else{ //按上左下右的顺序判断有无可走路径
//尝试向上
ptNext = canUp(ptCurrent); //获取向上走后的位置
//判断向上走后的位置是否是合法位置,若不合法,则表明上走到了迷宫的边缘,或者上面没有可走路径
if(ptNext.allRight()){
//上述判断成功,则将向上移动后的位置传入给自己,进行递归。当该函数退出,查看返回值是否为找到终点。若找到终点则立刻返回FoundEnding
if(findRoute(ptNext) == FoundEnding){
nRes = FoundEnding;
return nRes;
}
}
//下列尝试四周位置是否可走的代码与上述大体相同,就不多说了
//尝试向左
ptNext = canLeft(ptCurrent);
if(ptNext.allRight()){
if(findRoute(ptNext) == FoundEnding){
nRes = FoundEnding;
return nRes;
}
}
//尝试向下
ptNext = canDown(ptCurrent);
if(ptNext.allRight()){
if(findRoute(ptNext) == FoundEnding){
nRes = FoundEnding;
return nRes;
}
}
//尝试向右
ptNext = canRight(ptCurrent);
if(ptNext.allRight()){
if(findRoute(ptNext) == FoundEnding){
nRes = FoundEnding;
return nRes;
}
}
}
//检测是否到达终点,若没有到达终点,则立刻从路线表中删除该位置
if(nRes != FoundEnding){
back();
}
return nRes;
}
/*-----------------核心算法------------------------*/
public:
//构造函数
CLabyrinthAI()
{
return;
}
//带有初始化迷宫数组构造函数
CLabyrinthAI(const CLabyrinth& vLabyrinth)
{
m_xLabyrinth = vLabyrinth;
getBeginning();
getEnding();
}
//初始化迷宫数组
void setLabyrinth(const CLabyrinth& vLabyrinth)
{
m_xLabyrinth = vLabyrinth;
}
//查找起点
void getBeginning()
{
uint nRow = ;
for(; nRow < m_xLabyrinth.size(); nRow++){
CRow xRow = m_xLabyrinth[nRow];
uint nCol = ;
for(; nCol < xRow.size(); nCol++){
int n = xRow[nCol];
if(n == Beginning){
m_ptBeginning = CPoint(nCol, nRow);
break;
}
}
}
}
//查找终点
void getEnding()
{
uint nRow = ;
for(; nRow < m_xLabyrinth.size(); nRow++){
CRow xRow = m_xLabyrinth[nRow];
uint nCol = ;
for(; nCol < xRow.size(); nCol++){
int n = xRow[nCol];
if(n == Ending){
m_ptEnding = CPoint(nCol, nRow);
break;
}
}
}
}
//调用核心算法函数,输出获得的路线
void AI()
{
findRoute(m_ptBeginning);
if(!m_vRoute.empty()){
CRoute::iterator it = m_vRoute.begin();
for(; it != m_vRoute.end(); it++){
CPoint pt = *it;
std::cout << "(" << pt.row << ", " << pt.col << ")";
if(it != m_vRoute.end() - ){
std::cout << "->";
}else{
std::cout << std::endl;
}
}
}else{
//如果没有找到路线到达终点
std::cout << "Sorry cannot file any ways to get ending." << std::endl;
}
}
};
代码都加上了注释,大家可以慢慢看。
如果上述过程把你搅晕了,那就用图来为你解答吧。
然后来到main函数
//用VC 6.0貌似不需要给main传参数,那我就偷一下懒
int main()
{
//定义迷宫数组,定义成C风格的二维数组方便查看
int vLabyrinthArray[][] = {
{,,-,}
, {,,,}
, {,,,}
, {,,,}
, {,,,}
, {-,,,}
}; //以下代码为将C风格的二维数组导入成C++风格的二维数组
int nRowNum = sizeof(vLabyrinthArray) / sizeof(vLabyrinthArray[]);
int nColNum = sizeof(vLabyrinthArray[]) / sizeof(int); CLabyrinth vLabyrinth;
for(int row = ; row < nRowNum; row++){
CRow xRow;
for(int col = ; col < nColNum; col++){
int n = vLabyrinthArray[row][col];
xRow.push_back(n);
}
vLabyrinth.push_back(xRow);
} //实例化CLabyrinthAI
CLabyrinthAI xAI(vLabyrinth);
//打出路线
xAI.AI(); //使程序暂停,方便查看数据
system("Pause"); return ;
}
以上代码同样加了注释,相信了解C++的同学都能看懂。
运行截图:
(Dos的,有点丑……
)
三,Javascript版
顺便我也把C++版的移植到了JavaScript上,代码如下:
function CLabyrinthAI(){
var s = this;
s.m_xLabyrinth = new Array(new Array());
s.m_ptBeginning = {};
s.m_ptEnding = {};
s.m_vRoute = new Array();
s.Beginning = -1;
s.Ending = -2;
s.CannotGo = 0;
s.CanGo = 1;
s.FoundEnding = 0;
s.NotFoundEnding = 1;
}
CLabyrinthAI.prototype.initAI = function(){
var s = this;
s.getBeginning();
s.getEnding();
}
CLabyrinthAI.prototype.isRepeat = function(pt){
var s = this;
var bRes = false;
for(var n = 0; n < s.m_vRoute.length; n++){
var pt0 = s.m_vRoute[n];
if(pt0.col == pt.col && pt0.row == pt.row){
bRes = true;
break;
}
}
return bRes;
};
CLabyrinthAI.prototype.advance = function(ptTo){
this.m_vRoute.push(ptTo);
};
CLabyrinthAI.prototype.back = function(){
this.m_vRoute.splice(this.m_vRoute.length-1,1);
};
CLabyrinthAI.prototype.isBeginning = function(pt){
if(this.m_ptBeginning.col == pt.col && this.m_ptBeginning.row == pt.row){
return true;
}else{
return false;
}
};
CLabyrinthAI.prototype.isEnding = function(pt){
if(this.m_ptEnding.col == pt.col && this.m_ptEnding.row == pt.row){
return true;
}else{
return false;
}
};
CLabyrinthAI.prototype.canUp = function(ptCurrent){
var s = this;
var ptRes = {col:-1,row:-1};
var col = ptCurrent.col;
var row = ptCurrent.row;
if(row > 0){
var ptNext = {col:col,row:row - 1};
if(!s.isRepeat(ptNext)){
var nAttr = s.m_xLabyrinth[ptNext.row][ptNext.col];
if(nAttr == s.CanGo || nAttr == s.Ending){
ptRes = ptNext;
}
}
}
return ptRes;
};
CLabyrinthAI.prototype.canDown = function(ptCurrent){
var s = this;
var ptRes = {col:-1,row:-1};
var col = ptCurrent.col;
var row = ptCurrent.row;
if(row < s.m_xLabyrinth.length - 1){
var ptNext = {col:col,row:row + 1};
if(!s.isRepeat(ptNext)){
var nAttr = s.m_xLabyrinth[ptNext.row][ptNext.col];
if(nAttr == s.CanGo || nAttr == s.Ending){
ptRes = ptNext;
}
}
}
return ptRes;
};
CLabyrinthAI.prototype.canLeft = function(ptCurrent){
var s = this;
var ptRes = {col:-1,row:-1};
var col = ptCurrent.col;
var row = ptCurrent.row;
if(col > 0){
var ptNext = {col:col-1,row:row};
if(!s.isRepeat(ptNext)){
var nAttr = s.m_xLabyrinth[ptNext.row][ptNext.col];
if(nAttr == s.CanGo || nAttr == s.Ending){
ptRes = ptNext;
}
}
}
return ptRes;
};
CLabyrinthAI.prototype.canRight = function(ptCurrent){
var s = this;
var ptRes = {col:-1,row:-1};
var col = ptCurrent.col;
var row = ptCurrent.row;
if(col < s.m_xLabyrinth[0].length - 1){
var ptNext = {col:col+1,row:row};
if(!s.isRepeat(ptNext)){
var nAttr = s.m_xLabyrinth[ptNext.row][ptNext.col];
if(nAttr == s.CanGo || nAttr == s.Ending){
ptRes = ptNext;
}
}
}
return ptRes;
};
CLabyrinthAI.prototype.allRight = function(p){
if(p.col >= 0 && p.row >= 0){
return true;
}else{
return false;
}
};
CLabyrinthAI.prototype.findRoute = function(ptCurrent){
var s = this;
var nRes = s.NotFoundEnding;
var ptNext = {col:-1,row:-1};
s.advance(ptCurrent);
if(s.isEnding(ptCurrent)){
nRes = s.FoundEnding;
}else{
ptNext = s.canUp(ptCurrent);
if(s.allRight(ptNext)){
if(s.findRoute(ptNext) == s.FoundEnding){
nRes = s.FoundEnding;
return nRes;
}
}
ptNext = s.canLeft(ptCurrent);
if(s.allRight(ptNext)){
if(s.findRoute(ptNext) == s.FoundEnding){
nRes = s.FoundEnding;
return nRes;
}
}
ptNext = s.canDown(ptCurrent);
if(s.allRight(ptNext)){
if(s.findRoute(ptNext) == s.FoundEnding){
nRes = s.FoundEnding;
return nRes;
}
}
ptNext = s.canRight(ptCurrent);
if(s.allRight(ptNext)){
if(s.findRoute(ptNext) == s.FoundEnding){
nRes = s.FoundEnding;
return nRes;
}
}
}
if(nRes != s.FoundEnding){
s.back();
}
return nRes;
};
CLabyrinthAI.prototype.getBeginning = function(){
var s = this;
for(var nRow = 0; nRow < s.m_xLabyrinth.length; nRow++){
var xRow = s.m_xLabyrinth[nRow];
for(var nCol = 0; nCol < xRow.length; nCol++){
var n = xRow[nCol];
if(n == s.Beginning){
s.m_ptBeginning = {col:nCol,row:nRow};
break;
}
}
}
};
CLabyrinthAI.prototype.getEnding = function(){
var s = this;
for(var nRow = 0; nRow < s.m_xLabyrinth.length; nRow++){
var xRow = s.m_xLabyrinth[nRow];
for(var nCol = 0; nCol < xRow.length; nCol++){
var n = xRow[nCol];
if(n == s.Ending){
s.m_ptEnding = {col:nCol,row:nRow};
break;
}
}
}
};
CLabyrinthAI.prototype.AI = function(data){
var s = this;
s.m_xLabyrinth = data;
s.initAI();
s.findRoute(s.m_ptBeginning);
return s.m_vRoute;
};
设计原理和C++版差不多,只是没有CPoint类而已。
虽然这套算法是研究出来了,但是还不能判断是否为最近路线,因此有待更新。不过以现在的算法,开发一个SLG应该不是问题了。
※感谢我的哥哥与我一起讨论其中的原理。
源代码下载:
http://files.cnblogs.com/yorhom/findRoute.rar
不再依赖A*,利用C++编写全新寻路算法的更多相关文章
- 简单利用jQuery加tomcat,让前端开发不再依赖于后端的接口
前端开发的过程中,我们免不了和后端进行联调,这时候就会出现以下的尴尬场景: 接口没写好,没法做接下来的功能 功能写好了,接口没写好,没法测这个功能 联调了,除了BUG,不知道锅在谁身上,只得陪后端耗时 ...
- 简单利用jQuery,让前端开发不再依赖于后端的接口
前端开发的过程中,我们免不了和后端进行联调,这时候就会出现以下的尴尬场景: 接口没写好,没法做接下来的功能 功能写好了,接口没写好,没法测这个功能 联调了,出了BUG,不知道锅在谁身上,只得陪后端耗时 ...
- LoadRunner利用ODBC编写MySql脚本
最近做了几周的LoadRunner测试,有一些心得,记录下来,以便以后查找. LoadRunner测试数据库是模拟客户端去连接数据库服务器,因此,需要协议(或者说驱动的支持).LoadRunner本身 ...
- 利用反射编写私有 Private 方法的单元测试
利用反射编写私有 Private 方法的单元测试 最近在添加一个新feature时,鉴于要给自己的代码一是增加代码的强壮性,二是增加代码测试的覆盖率.但是遇到了有些方法是 Private 的,但是在调 ...
- 利用java编写的盲注脚本
之前在网上见到一个盲注的题目,正好闲来无事,便用java写了个盲注脚本,并记录下过程中的坑 题目源码: <?php header("Content-Type: text/html;ch ...
- 品味性能之道<九>:利用Loadrunner编写socket性能测试脚本简述
一.概述 Loadrunner拥有极为丰富的工具箱,供予我们制造出各种奇妙魔法的能力.其中就有此次要讨论的socket套接字操作. 二.socket概述 ...
- Python 利用Python编写简单网络爬虫实例3
利用Python编写简单网络爬虫实例3 by:授客 QQ:1033553122 实验环境 python版本:3.3.5(2.7下报错 实验目的 获取目标网站“http://bbs.51testing. ...
- Python 利用Python编写简单网络爬虫实例2
利用Python编写简单网络爬虫实例2 by:授客 QQ:1033553122 实验环境 python版本:3.3.5(2.7下报错 实验目的 获取目标网站“http://www.51testing. ...
- 利用Python编写Windows恶意代码!自娱自乐!勿用于非法用途!
本文主要展示的是通过使用python和PyInstaller来构建恶意软件的一些poc. 利用Python编写Windows恶意代码!自娱自乐!勿用于非法用途!众所周知的,恶意软件如果影响到了他人的生 ...
随机推荐
- 情商 EQ & 儿童情商
EQ 包括哪些内容 1. 认知自身情绪的能力(正确客观的评价自己)2. 管理自己情绪的能力(控制冲动) 3. 自我激励能力(学会抗挫折) 4. 认识他人情绪的能力(学会移情) 5. 人际关系处理能力 ...
- 打开palette控制面板
(2)
- 多个 label checkbox 组合 显示在同一个水平线上[前提Bootstrap框架]
<th align="left" valign="middle"> <label class="checkbox inline fo ...
- Javascript代码执行过程-《悟透Javascript》笔记
本文摘录自李战老师<悟透Javascript>一书的部分章节,为适应博客发表作了一点点修改. 1) 预编译分析. JavaScript执行引擎将所有定义式函数直接创建为作用域上的函数变量, ...
- HBase和ZooKeeper
HBase和ZooKeeper HBase内置有ZooKeeper,也可以使用外部ZooKeeper. 让HBase使用一个已有的不被HBase托管的Zookeep集群,需要设置 conf/hbase ...
- 使用webdriverwait封装查找元素方法
对于selenium原生的查找元素方法进行封装,在timeout规定时间内循环查找页面上有没有某个元素 这样封装的好处: 1.可以有效提高查找元素的效率,避免元素还没加载完就抛异常 2.相对于time ...
- Excel单元格格式设置
工作中遇到一些小问题: 例如办公自动化里的如何设置单元格格式 此格式分为两种:一种是样式上的格式 比如边框 行距字体等 第二种为数据格式: 比如每次我输入1000的话自动变红或者加粗字体 office ...
- 170330、Spring中你不知道的注入方式
前言 在Spring配置文件中使用XML文件进行配置,实际上是让Spring执行了相应的代码,例如: 使用<bean>元素,实际上是让Spring执行无参或有参构造器 使用<prop ...
- SpringMVC的解释与搭建Maven私有代理服务器
SpringMVC静态资源处理 通常会配置SpringMVC拦截所有请求 即将DisptcherServlet的url-pattern设置为 / 此时会导致SpringMVC同时拦截.css .j ...
- 使用 paramsPrepareParamsStack 拦截器栈后的运行流程
2. 使用 paramsPrepareParamsStack 拦截器栈后的运行流程 1). paramsPrepareParamsStack 和 defaultStack 一样都是拦截器栈. 而 st ...