在前一篇博客中随机生成迷宫,现在就以随机生成的迷宫为地图,开始寻找路径。

迷宫寻路也可以使用DFS,BFS,但常见的是A*算法,它是启发式搜索算法的一种,效率相比前两者也更高。接下来以A*算法为例,迷宫是一个连通图,因此可以寻找到地图上可通行的任意两点间的路径。

A*算法

A*算法的目的是求出最低通过成本,利用它来寻找最优路径。

A*算法的核心在于它的估值函数:f(n)=g(n)+h(n);

  f(n)表示第n点的估值;

  g(n)表示起始点到顶点n的代价,这里代表起始点到顶点n距离;

  h(n)表示顶点n到终点的代价,这里代表顶点n到终点的距离。

对于h(n)采用曼哈顿距离计算。

|r1-r2| + |y1 - y2|

当然也可以使用其他的评估函数,如欧几里得距离、切比雪夫距离;

实现

每个顶点除了保存自身的坐标位置r、c和通行状态flag外,还需要保存各自的f、g、h值(初始化均为0,通过A*算法计算得出),parent指向经过顶点n的前一顶点,state表示顶点n的状态。

class Point {
constructor(r, c, flag=1) {
this.r = r;
this.c = c;
this.flag = flag; // 0 可以通过 1不能通过
this.state = -1; // 0 在openList 1 在closeList -1 未处理 this.f = 0;
this.g = 0;
this.h = 0;
this.parent = null;
}
}

需要两个数组,openList保存已计算 f 值得顶点,closeList保存最有路径的顶点。

(1)初始状态下,openList包含起点,closeList为空。

(2)从openList中取出第一个顶点curPoint,放入closeList中,将curPoint的state设置为1,表示该顶点在closeList数组中

(3)遍历curPoint上下左右的顶点tp,对tp做处理:

  tp没有超出边界,flag==0,且state!=1(不在closeList)的,判断tp是否为终点;

  是则将tp的parent指向curPoint,返回true,结束函数执行;

  如果不是,计算或更新tp的估值等。

(4)从openList中选择估值最小的顶点,循环步骤3。

/*
* start 起点
* end 终点
* distPoint() 计算两点间曼哈顿距离
* processPoint() 处理四周顶点
*/
findPath(start, end) {
let curPoint = start;
// 上下左右顶点偏移量
let near = [{
r: -1,
c: 0
}, {
r: 1,
c: 0
}, {
r: 0,
c: -1
}, {
r: 0,
c: 1
}];
let finded = false;
start.f = start.h = this.distPoint(start, end);
this.openList.push(start); while(this.openList.length > 0) {
curPoint = this.openList.pop();
curPoint.state = 1;
this.closeList.push(curPoint);
finded = this.processPoint(near, curPoint, end);
if(finded) {
break;
}
}
}
/*
* near 需要遍历的顶点偏移量
* curPoint 当前顶点
* pathArr 包含路和墙的二位数组
* end 终点
* between() 计算数值是否在所给范围内
* addArrSort() 添加顶点到指定数组并排序
* quickSort() 快速排序
* comPonitF() 指定排序方法 — — 比较 f 值
*/
processPoint(near, curPoint, end) {
let len = near.length,
i = 0; while(i < len) {
let tr = curPoint.r + near[i].r,
tc = curPoint.c + near[i].c;
if(this.between(tr, 0, this.r - 1) && this.between(tc, 0, this.c - 1)) {
let tp = this.pathArr[tr][tc];
if(tp.flag === 0 && tp.state !== 1) {
// 判断 tp 是否为终点
if(this.isEqual(tp, end)) {
tp.parent = curPoint;
return true;
}
// tp不在openList(估值未计算)
if(tp.state === -1) {
tp.parent = curPoint;
tp.g = curPoint.g + 1;
tp.h = this.distPoint(tp, end);
tp.f = tp.g + tp.h;
this.addArrSort(this.openList, tp, this.comPonitF);
}else {
let g = curPoint + 1;
if(g < tp.g) {
tp.parent =curPoint;
tp.g = g;
tp.f = tp.g + tp.h;
this.quickSort(this.openList, 0, this.openList.length, this.comPonitF);
}
}
}
}
++i;
}
return false;
}

最终,可以通过终点 parent 往回找到路径。

确定起点、终点

为了探寻任意两点间路径,这里为canvas添加了点击事件,以确定起点和终点,然后描绘出两点间的路线。

/*
* removeEvent() 移除点击事件
*/
addEvent(e) {
if(this.start && this.end) {
this.removeEvent();
return;
}
let c = ~~(e.offsetX / 10), // 取整
r = ~~(e.offsetY / 10);
if(this.pathArr[r][c].flag === 0) {
if(!this.start) {
this.start = this.pathArr[r][c];
this.renderPer(this.start, 'yellow');
}else {
this.end = this.pathArr[r][c];
this.findPath(this.start, this.end);
this.render(this.end);
}
}
}

路径搜索就到这里啦。

效果

完整代码

canvas——路径搜索的更多相关文章

  1. html5 canvas常用api总结(三)--图像变换API

    canvas的图像变换api,可以帮助我们更加方便的绘画出一些酷炫的效果,也可以用来制作动画.接下来将总结一下canvas的变换方法,文末有一个例子来更加深刻的了解和利用这几个api. 1.画布旋转a ...

  2. 【探索】利用 canvas 实现数据压缩

    前言 HTTP 支持 GZip 压缩,可节省不少传输资源.但遗憾的是,只有下载才有,上传并不支持.如果上传也能压缩,那就完美了.特别适合大量文本提交的场合,比如博客园,就是很好的例子. 虽然标准不支持 ...

  3. 简单入门canvas - 通过刮奖效果来学习

    一 .前言 一直在做PC端的前端开发,从互联网到行业软件.最近发现移动端已经成为前端必备技能了,真是不能停止学习.HTML5新增的一些东西,canvas是用的比较多也比较复杂的一个,简单的入门了一下, ...

  4. 获取Canvas当前坐标系矩阵

    前言 在我的另一篇博文 Canvas坐标系转换 中,我们知道了所有的平移缩放旋转操作都会影响到画布坐标系.那在我们对画布进行了一系列操作之后,怎么再知道当前矩阵数据状态呢. 具体代码 首先请看下面的一 ...

  5. Canvas坐标系转换

    默认坐标系与当前坐标系 canvas中的坐标是从左上角开始的,x轴沿着水平方向(按像素)向右延伸,y轴沿垂直方向向下延伸.左上角坐标为x=0,y=0的点称作原点.在默认坐标系中,每一个点的坐标都是直接 ...

  6. Canvas绘图之平移translate、旋转rotate、缩放scale

    画布操作介绍 画布绘图的环境通过translate(),scale(),rotate(), setTransform()和transform()来改变,它们会对画布的变换矩阵产生影响. 函数 方法 描 ...

  7. 用html5的canvas和JavaScript创建一个绘图程序

    本文将引导你使用canvas和JavaScript创建一个简单的绘图程序. 创建canvas元素 首先准备容器Canvas元素,接下来所有的事情都会在JavaScript里面. <canvas ...

  8. html5标签canvas函数drawImage使用方法

    html5中标签canvas,函数drawImage(): 使用drawImage()方法绘制图像.绘图环境提供了该方法的三个不同版本.参数传递三种形式: drawImage(image,x,y):在 ...

  9. 使用 JavaScript 和 canvas 做精确的像素碰撞检测

    原文地址:Pixel accurate collision detection with Javascript and Canvas 译者:nzbin 我正在开发一个需要再次使用碰撞检测的游戏.我通常 ...

随机推荐

  1. CF #349 div1 B. World Tour

    题目链接:http://codeforces.com/problemset/problem/666/B 大意是给一张有向图,选取四个点,使得走这四个点,任意两个点之间走最短路,总距离最长. 3000个 ...

  2. 1.2 N层架构

    N层架构 介绍 ABP架构 其他(通用) 领域层 应用层 基础设施层 网络和展现层 其他 总结 介绍 应用程序代码库的分层架构是被广泛认可的可以减少程序复杂度.提高代码复用率的技术.为了实现分层架构, ...

  3. C#,VB.NET如何将Word转换为PDF和Text

    众所周知,Word是我们日常工作中常用的办公软件之一,有时出于某种需求我们需要将Word文档转换为PDF以及Text.那么如何以C#,VB.NET编程的方式来实现这一功能呢? 下面我将分开介绍如何运用 ...

  4. linux基础 作业篇

    1.自动部署反向代理 web nfs #!/usr/bin/python #-*- coding:utf-8 -*- #开发脚本自动部署及监控 #1.编写脚本自动部署反向代理.web.nfs: #!/ ...

  5. MongoDB3.4 shell CRUD操作

    输入db,显示你正在操作的数据库:切换数据库,输入use dbName,如果数据库不存在的话会自动帮我们创建一个:使用show dbs可以显示所有可用的数据库. 测试数据在文末 插入文档 插入操作的行 ...

  6. Linux centos7下安装配置redis及Redis desktop Manager工具连接注意事项

    基本工具:VMware12.CentOS-7-x86_64-Everything-1611.iso.redis-desktop-manager-0.8.0.3841 废话不多说,首先,关于什么是Red ...

  7. Transform java future into completable future 【将 future 转成 completable future】

    Future is introduced in JDK 1.5 by Doug Lea to represent "the result of an asynchronous computa ...

  8. (转)详解JS位置、宽高属性之一:offset系列

    很多初学者对于JavaScript中的offset.scroll.client一直弄不明白,虽然网上到处都可以看一张图(图1),但这张图太多太杂,并且由于浏览器差异性,图示也不完全正确. 图一 不知道 ...

  9. 高斯消元法(Gauss Elimination)【超详解&模板】

    高斯消元法,是线性代数中的一个算法,可用来求解线性方程组,并可以求出矩阵的秩,以及求出可逆方阵的逆矩阵.高斯消元法的原理是:若用初等行变换将增广矩阵 化为 ,则AX = B与CX = D是同解方程组. ...

  10. 第一个远程javaweb项目测试全过程

    2017-5-20,在这个奇特的日子,我不再满足于在本地测试javaweb,于是在上腾讯云买了第一个云服务器,由于是学生认证,所以一个月只要10块钱,还是要抢的,每天早上9点开抢 打开后,发现其实是一 ...