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日

相关文章

  • 混乱的Java日志体系及集成jar包梳理分析

    混乱的Java日志体系及集成jar包梳理分析是一篇旨在帮助Java开发者理解Java日志体系和集成jar包的文章。本文将围绕Java日志体系的问题、集成jar包的例子、分析Java日志框架的实现等多方面展开讲解。 一、Java日志体系的问题 在Java开发过程中,我们经常需要使用日志来帮助我们进行调试。但是,Java日志体系却十分混乱,不同的日志框架都有着自…

    Java 2023年5月19日
    00
  • jsp内置对象

    一、什么是jsp内置对象 JSP(JavaServer Pages)内置对象是指在JSP页面中可以直接使用的特定对象,它们被JSP容器创建和初始化,用于向开发人员提供对其环境的访问。JSP内置对象是Java语言的一个重要的保留成分,通过使用内置对象可以简化JSP开发过程,同时也能够提高程序的运行效率。 二、jsp内置对象的分类 JSP内置对象分为9种,具体如…

    Java 2023年6月15日
    00
  • java定时任务的实现方法

    下面是针对”Java定时任务的实现方法”的详细攻略,主要介绍如何使用Java实现定时任务。 什么是定时任务? 定时任务是指在预定时期或时间,按照一定轨迹执行一些预定的操作或服务。 Java中实现定时任务的方法 1. Timer类 Java中提供了java.util.Timer类,它可以帮助我们实现简单的定时任务。 public class TimerTask…

    Java 2023年5月20日
    00
  • 解决Jackson解析嵌套类问题(MismatchedInputException)

    解决Jackson解析嵌套类问题(MismatchedInputException)可以分为以下几个步骤: 1. 确认报错信息 在开始处理问题之前,我们首先需要确认MismatchedInputException报错信息的内容,以便能够更加准确地定位问题和解决问题。报错信息通常包含以下关键信息: 错误原因:报错信息说明了当前出现了什么错误; 错误位置:报错信…

    Java 2023年5月26日
    00
  • CAS操作的作用是什么?

    CAS (Compare-and-Swap) 操作是计算机系统中的一种并发原语,可以用来实现多线程同步,防止多线程同时修改同一个共享变量而导致数据不一致的问题。 CAS 操作主要使用于多线程环境下对共享变量的原子操作,可以保证多线程并发读写时的安全性。 该操作一般由三个参数组成:共享内存变量 V、预期值 A 和新值 B。操作的目的是:如果当前 V 的值等于 …

    Java 2023年5月10日
    00
  • Spring-Security对HTTP相应头的安全支持方式

    Spring Security 提供了许多机制来增强 Web 应用程序的安全性。其中一个是它支持将标准 HTTP 相应头设置为提高 Web 应用程序的安全性。这包括常见的头,如 X-Content-Type-Options、X-XSS-Protection、X-Frame-Options、Strict-Transport-Security 等。在本文中,我们…

    Java 2023年5月20日
    00
  • Kafka Producer中的消息缓存模型图解详解

    以下是关于“Kafka Producer中的消息缓存模型图解详解”的完整攻略: Kafka Producer中的消息缓存模型图解详解 什么是Kafka Producer? Kafka是目前人气逐渐上升的一个分布式流媒体平台,其中包括Kafka Producer、Kafka Consumer、Kafka Connect、Kafka Streams和Kafka …

    Java 2023年5月20日
    00
  • java mybatis框架实现多表关系查询功能

    Java MyBatis框架是一个Java持久层框架,可以帮助我们更轻松地管理数据库。在多表关系查询的情况下,通过使用MyBatis框架可以使查询更加高效且易于维护。下面是详细的攻略供你参考。 1.创建MyBatis映射文件 创建MyBatis映射文件是实现多表关系查询的第一步。MyBatis提供了多种映射器类型,例如XML映射器和注解映射器。在这里,我们使…

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