js版本A*寻路算法

JS版本A*寻路算法

A(A-Star)算法是一种常用的路径搜索算法,它在寻找从起点到终点的最短路径过程中,会通过改进Dijkstra算法来提高效率。JS版本A寻路算法用于在网页游戏等应用场景下,帮助角色格子图中找到最短路径。

算法流程

  1. 创建一个空的开放列表列表(OPEN)和一个空的封闭列表(CLOSED)
  2. 把起始点作为当前点加入到OPEN列表中
  3. 循环执行以下步骤:
  4. 找到OPEN列表中的F值最小的节点作为当前节点P,把P从OPEN列表中移除,加入到CLOSED列表中
  5. 如果当前节点P为目标终点,则路径已找到,跳出循环
  6. 对P的所有邻居节点,计算它们的 G 和 H 值,其中:
    • G 值是指从起点到当前节点的实际代价
    • H 值是指从当前节点到目标节点的估价代价(即启发式函数)
  7. 对于每个邻居节点N:
    • 如果N已经在CLOSED列表中,则跳过该节点,继续下一个节点
    • 如果不在OPEN列表中,则表示之前没有被处理过,将当前节点P设置为它的父节点,计算 F 值,并加入到OPEN列表中
    • 如果当前节点P已经是N的父节点,并且新计算的 G 值比原来的小,那么重新计算 N 的 F、G、H 值,并将其父节点设置为P
  8. 如果 OPEN 列表为空,表示无法找到路径,退出循环。否则继续从 1 步重复执行

代码实现

下面是JS版本A*寻路算法的简单实现:

function AStar(startNode, endNode) {
  let openList = []; // 开启列表,存放待访问节点
  let closedList = []; // 关闭列表,存放已经访问过的节点
  startNode.g = 0; // 起始节点的G值为0
  startNode.f = startNode.g + getH(startNode, endNode); // 起始节点的F值为H值
  openList.push(startNode); // 将起始节点加入到开启列表中

  while (openList.length > 0) {
    // 从开启列表中找到F值最小的节点作为当前节点
    let curNode = openList.reduce((prev, cur) =>
      prev.f < cur.f ? prev : cur
    );
    // 从开启列表中删除当前节点
    openList = openList.filter((item) => item !== curNode);
    // 将当前节点添加到关闭列表中
    closedList.push(curNode);

    if (curNode === endNode) {
      // 找到了目标节点,返回路径
      return getPath(startNode, endNode);
    }

    // 获取当前节点的邻居列表
    let neighborList = getNeighborList(curNode);

    // 遍历邻居列表
    for (let i = 0; i < neighborList.length; i++) {
      let neighborNode = neighborList[i];

      if (closedList.includes(neighborNode)) {
        // 当前节点已经在关闭列表中,忽略它
        continue;
      }

      let gScore = curNode.g + 1; // 更新G值

      if (!openList.includes(neighborNode)) {
        // 邻居节点第一次遍历到,加入开启列表
        neighborNode.g = gScore;
        neighborNode.h = getH(neighborNode, endNode);
        neighborNode.f = neighborNode.g + neighborNode.h;
        neighborNode.parent = curNode;
        openList.push(neighborNode);
      } else if (gScore < neighborNode.g) {
        // 当前节点是邻居节点的更优父节点
        neighborNode.g = gScore;
        neighborNode.f = neighborNode.g + neighborNode.h;
        neighborNode.parent = curNode;
      }
    }
  }
  // 开启列表已经用完,仍然没有到达目标节点,则返回空路径
  return [];
}

示例说明

在以下示例中,我们将用一个简单的二维数组来表示地图,其中1表示障碍物,0表示通路。我们将尝试寻找从起点 (0, 0) 到终点 (3, 3) 的最短路径。

let map = [
  [0, 1, 0, 0],
  [0, 0, 1, 0],
  [1, 0, 0, 1],
  [0, 0, 1, 0],
];

// 节点对象
class Node {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.g = 0;
    this.h = 0;
    this.f = 0;
    this.parent = null;
  }
}

function getH(node, endNode) {
  // 计算H值(曼哈顿距离)
  return Math.abs(endNode.x - node.x) + Math.abs(endNode.y - node.y);
}

function getNeighborList(node) {
  // 获取相邻节点列表
  let neighborList = [];
  let x = node.x;
  let y = node.y;

  if (x > 0) {
    neighborList.push(new Node(x - 1, y));
  }
  if (x < map[0].length - 1) {
    neighborList.push(new Node(x + 1, y));
  }
  if (y > 0) {
    neighborList.push(new Node(x, y - 1));
  }
  if (y < map.length - 1) {
    neighborList.push(new Node(x, y + 1));
  }

  neighborList = neighborList.filter(
    (item) => map[item.y][item.x] !== 1 // 过滤障碍点
  );
  return neighborList;
}

function getPath(startNode, endNode) {
  // 从终点往回推,返回路径
  let path = [];
  let curNode = endNode;

  while (curNode !== startNode) {
    path.push(curNode);
    curNode = curNode.parent;
  }
  // 把起点加入路径末尾
  path.push(startNode);
  return path.reverse();
}

let startNode = new Node(0, 0);
let endNode = new Node(3, 3);
let path = AStar(startNode, endNode);

console.log(path);
// 输出:[
//   Node { x: 0, y: 0, g: 0, h: 6, f: 6, parent: null },
//   Node { x: 1, y: 1, g: 1, h: 4, f: 5, parent: [Node] },
//   Node { x: 2, y: 1, g: 2, h: 3, f: 5, parent: [Node] },
//   Node { x: 2, y: 2, g: 3, h: 2, f: 5, parent: [Node] },
//   Node { x: 3, y: 3, g: 4, h: 0, f: 4, parent: [Node] }
// ]

在上面的示例中,我们使用了一个Node类来表示节点,其中x和y表示节点坐标,g表示起点到当前节点的实际代价,h表示当前节点到目标节点的估价代价(即启发式函数),f表示当前节点的总代价(f=g+h),parent表示当前节点的父节点。其中的 getH、getNeighborList、getPath 分别是计算 H 值、获取邻居节点列表、从终点往回推导路径的辅助函数。

最终,我们得到了从起点到终点的最短路径:(0,0)->(1,1)->(2,1)->(2,2)->(3,3)。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:js版本A*寻路算法 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • Spring Security OAuth2 授权码模式的实现

    下面给出 Spring Security OAuth2 授权码模式的实现攻略。 什么是授权码模式 授权码模式(Authorization Code Grant)是OAuth2.0标准中最为常用的一种流程,在实现 OAuth2.0 授权功能时,授权码模式是最稳妥的一种方式。 授权码模式的具体流程如下:1. 第三方应用向用户请求授权,用户同意授权后,第三方应用获…

    Java 2023年5月20日
    00
  • java使用Dijkstra算法实现单源最短路径

    Java使用Dijkstra算法实现单源最短路径攻略 算法简介 Dijkstra算法是一种经典的计算图的单源最短路径的算法。它的基本思想是从起始点开始,首先确定该点到其他所有点的最短距离,然后以最短距离作为中介点,依次直到所有点的最短路径都被确定。Dijkstra算法主要应用在网络路由、航空等行业中。 算法步骤 将图中节点分为两个集合:已确定路径的节点集合和…

    Java 2023年5月19日
    00
  • 如何解决Spring in action @valid验证不生效的问题

    如何解决Spring in action @valid验证不生效的问题 在Spring中使用@Valid注解可以轻松实现参数校验,但是有时候我们会遇到@Valid校验不生效的问题,接下来我将分享如何解决这个问题的完整攻略。 1. 确认是否添加了校验器依赖 在使用@Valid注解校验参数之前,需要确保我们在项目中添加了校验器依赖。常用的校验器依赖是Hibern…

    Java 2023年5月20日
    00
  • java实现异步导出数据

    为了让读者更加易懂,本文将采用三个部分讲解异步导出数据。 1. 后端实现异步导出 对于导出数据这种后端耗时较长的操作,我们一般采用异步导出的方式来解决。下面是后端实现异步导出的主要步骤: 1.1 前端发起导出请求,后端生成导出任务 前端发起导出请求时,后端会先生成一个唯一的任务id,将任务id返回给前端,并把导出任务存储到数据库中。 1.2 后端异步执行导出…

    Java 2023年5月26日
    00
  • Java中实现线程间通信的实例教程

    下面我将为您详细讲解“Java中实现线程间通信的实例教程”的完整攻略。 什么是线程间通信 线程是 Java 中最基本的并发编程单元,线程之间的通信是指多个线程在访问共享资源过程中,通过某种协作机制对资源实现共享和互斥访问的过程。线程间通信是 Java 并发编程中的核心概念之一。 线程间通信实现方式 Java 中实现线程间通信一般有三种方式: 共享内存 消息传…

    Java 2023年5月18日
    00
  • Java持久化框架Hibernate与Mybatis优劣及选择详解

    Java持久化框架Hibernate与Mybatis优劣及选择详解 1. 什么是Java持久化框架? Java持久化框架是为了简化Java应用程序与关系型数据库之间数据交互的过程所设计的一套框架。通过使用Java持久化框架,在Java应用程序中可以通过对象来操作数据库,这样可以实现面向对象编程与关系型数据库的无缝对接。 2. Hibernate与Mybati…

    Java 2023年5月31日
    00
  • Java线程池的几种实现方法和区别介绍

    Java线程池的几种实现方法和区别介绍 前言 多线程是计算机领域中的重要概念,能够有效的提高程序的运行效率。但是,高并发下多线程不规则创建和销毁会消耗系统大量的CPU和内存资源。因此,使用线程池技术能够有效的降低线程创建和销毁的开销,并且控制并发线程数,从而更好的管理服务器资源。 本文将详细介绍Java线程池的几种实现方法和区别,并且提供示例说明。 Java…

    Java 2023年5月18日
    00
  • JAVA十大排序算法之基数排序详解

    JAVA十大排序算法之基数排序详解 基本概念 基数排序是按照低位先排序,也就是先排个位,再排十位,以此类推。这样从最低位开始排序,直到最高位排序完成之后,数列就变成了一个有序序列。 算法步骤 基数排序的过程可以描述如下: 取得数组中的最大数,并取得位数; arr为原始数组,从最低位开始取每个位组成radix数组; 对radix进行计数排序(利用计数排序适用于…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部