dijkstra and A* search 算法心得及JS实现
目录
在BFS/DFS的基础上二者补充了什么?
二者增加了路径开销/距离这一概念,指从一点到另一点的边的长度。
dijkstra在乎当前搜索的节点离出发节点的距离,并且在进行遍历邻居节点时需要进行路径的开销(即为离起点的距离)判断。如果邻居节点中有节点已经被遍历过,此时需要判断路径开销,如果开销大于当前节点当前开销加上对应路径开销,则需要更新路径开销并更新父节点指向, 没有遍历过则直接new 新节点加入openList。在遍历完所有邻居后需要对openList中加入的节点根据节点路径开销进行排序后,取开销最小的为下一个搜索节点。而DFS/BFS则无这种操作。
A* 在dijkstra的基础上略有补充,新增一个估计距离的概念。在对openList中节点进行排序时,根据总开销 =(路径开销 + 估计距离);下面代码的估计距离可简单表示为sqrt((x1-x2)^2 + (y1-y2)^2),可根据实际情况进行调整;
地图生成(六边形可能需要人工修改)
A*
dijkstra
A* 六边形
JS代码实现
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>dijkstra search</title>
</head>
<body>
<button type="button" onclick="run()">start</button>
<button type="button" onclick="location.reload()">refresh</button>
<a id="iteration"></a>
<a id="panel" style="display: grid;"></a>
<script type="text/javascript">
const colors = ["black", "gray", "green", "yellow", "red", "purple"];
const pxSize = 20;
const timeStep = 10;
// 0 - obstacle; 1 - blank_1; 2 - start; 4 - goal; blank_3 = 3; blank_5 = 5;
const start = 2, goal = 4, obstacle = 0, blank_1 = 1; blank_3 = 3; blank_5 = 5;
const pathCost = [null, 1, 0, 3, 0, 5];
const map = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 3, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 5, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1], [1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 5, 5, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0], [1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1], [1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 3, 1, 1, 0, 1, 3, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1], [1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 3, 1, 1, 0, 1, 3, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1], [1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1], [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 3, 3, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 5, 5, 5, 5, 5, 0, 1, 1, 1, 1, 1, 1], [0, 0, 0, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1], [1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1], [3, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 3, 3, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 5, 5, 1, 1, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1], [1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 5, 5, 5, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1], [1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 5, 5, 5, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 3, 3, 1, 1, 1, 3, 3, 1, 1, 1, 1, 0, 1, 1, 5, 5, 1, 1, 1, 1, 1, 1, 1, 0, 3, 3, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1], [2, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 3, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], [4, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 3, 3, 3, 3, 0, 0, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 5, 5, 0, 0, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [0, 0, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 3, 3, 3, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 3, 3, 1, 1, 0, 1, 1, 3, 1, 1], [0, 0, 1, 1, 5, 5, 1, 1, 1, 3, 3, 3, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 3, 3, 3, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 3, 3, 1, 1, 0, 1, 1, 3, 1, 1], [0, 0, 1, 1, 5, 5, 1, 1, 1, 3, 3, 3, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 3, 3, 1, 1, 0, 1, 1, 3, 1, 1], [0, 0, 1, 1, 5, 5, 1, 1, 1, 3, 3, 3, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 3, 3, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 5, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 3, 3, 3, 1, 1, 1, 1], [1, 5, 5, 5, 5, 5, 5, 5, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], [1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]];
const row = map.length;
const col = map[0].length;
document.getElementById("panel").style.gridTemplateColumns = (pxSize + "px ").repeat(col);
document.getElementById("panel").style.gridTemplateRows = (pxSize + "px ").repeat(row);
//add grid - num at row * col
var addGridStringText = "<canvas id='myCanvas' width=" + pxSize * col + " height=" + pxSize * row + " style='position:absolute; solid #000000;'></canvas>" + "<canvas id='myCanvasForFinalPath' width=" + pxSize * col + " height=" + pxSize * row + " style='position:absolute; solid #000000;'></canvas>";
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++)
addGridStringText += "<div row='" + i + "' col='" + j + "' id='" + i + "_" + j + "' style=\"border:1px solid white; background-color:" + colors[map[i][j]] + "\"></div>";
}
document.getElementById("panel").innerHTML = addGridStringText;
function drawLine(from, to) {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.lineWidth = 5;
ctx.moveTo(pxSize * (from.colAt + 0.5), pxSize * (from.rowAt + 0.5));
ctx.lineTo(pxSize * (to.colAt + 0.5), pxSize * (to.rowAt + 0.5));
ctx.stroke();
}
function drawFinalPathLine(from, to) {
var c = document.getElementById("myCanvasForFinalPath");
var ctx = c.getContext("2d");
ctx.strokeStyle = '#cc0000';
ctx.lineWidth = 5;
ctx.moveTo(pxSize * (from.colAt + 0.5), pxSize * (from.rowAt + 0.5));
ctx.lineTo(pxSize * (to.colAt + 0.5), pxSize * (to.rowAt + 0.5));
ctx.stroke();
}
function Node(parent, rowat, colat, totalCost) {
this.parent = parent;
this.rowAt = rowat;
this.colAt = colat;
this.totalCost = totalCost;
if (parent != null) drawLine(parent, this);
this.getMapValue = function () {
return map[this.rowAt][this.colAt];
}
this.setParent = function (p) {
this.parent = p;
}
this.setTotalCost = function (cost) {
this.totalCost = cost;
}
this.set
//from left top and normal clock order
this.addNeightBor = function () {
rowAt = this.rowAt;
colAt = this.colAt;
// rightTop = [rowAt - 1, colAt + 1];
// if (rightTop[1] < col && rightTop[0] >= 0 && map[rightTop[0]][rightTop[1]] != obstacle) {
// if (getNodeFromClosed(rightTop) != null) {
// node = getNodeFromClosed(rightTop);
// costPos = map[rightTop[0]][rightTop[1]];
// if (node.totalCost > this.totalCost + costPos) {
// node.setTotalCost(this.totalCost + costPos);
// node.setParent(this);
// }
// } else {
// node = new Node(this, rightTop[0], rightTop[1], this.totalCost + pathCost[map[rightTop[0]][rightTop[1]]]);
// openList.push(node);
// createdList.push(node);
// }
// }
right = [rowAt, colAt + 1];
if (right[1] < col && map[right[0]][right[1]] != obstacle) {
if (getNodeFromClosed(right) != null) {
node = getNodeFromClosed(right);
costPos = map[right[0]][right[1]];
if (node.totalCost > this.totalCost + pathCost[costPos]) {
node.setTotalCost(this.totalCost + pathCost[costPos]);
node.setParent(this);
}
} else {
node = new Node(this, right[0], right[1], this.totalCost + pathCost[map[right[0]][right[1]]]);
openList.push(node);
createdList.push(node);
}
}
// rightBott = [rowAt + 1, colAt + 1];
// if (rightBott[0] < row && rightBott[1] < col && map[rightBott[0]][rightBott[1]] != obstacle) {
// if (getNodeFromClosed(rightBott) != null) {
// node = getNodeFromClosed(rightBott);
// costPos = map[rightBott[0]][rightBott[1]];
// if (node.totalCost > this.totalCost + costPos) {
// node.setTotalCost(this.totalCost + costPos);
// node.setParent(this);
// }
// } else {
// node = new Node(this, rightBott[0], rightBott[1], this.totalCost + pathCost[map[rightBott[0]][rightBott[1]]]);
// openList.push(node);
// createdList.push(node);
// }
// }
bott = [rowAt + 1, colAt];
if (bott[0] < row && map[bott[0]][bott[1]] != obstacle) {
if (getNodeFromClosed(bott) != null) {
node = getNodeFromClosed(bott);
costPos = map[bott[0]][bott[1]];
if (node.totalCost > this.totalCost + pathCost[costPos]) {
node.setTotalCost(this.totalCost + pathCost[costPos]);
node.setParent(this);
}
} else {
node = new Node(this, bott[0], bott[1], this.totalCost + pathCost[map[bott[0]][bott[1]]]);
openList.push(node);
createdList.push(node);
}
}
// leftBott = [rowAt + 1, colAt - 1];
// if (leftBott[1] >= 0 && leftBott[0] < row && map[leftBott[0]][leftBott[1]] != obstacle) {
// if (getNodeFromClosed(leftBott) != null) {
// node = getNodeFromClosed(leftBott);
// costPos = map[leftBott[0]][leftBott[1]];
// if (node.totalCost > this.totalCost + costPos) {
// node.setTotalCost(this.totalCost + costPos);
// node.setParent(this);
// }
// } else {
// node = new Node(this, leftBott[0], leftBott[1], this.totalCost + pathCost[map[leftBott[0]][leftBott[1]]]);
// openList.push(node);
// createdList.push(node);
// }
// }
left = [rowAt, colAt - 1];
if (left[1] >= 0 && map[left[0]][left[1]] != obstacle) {
if (getNodeFromClosed(left) != null) {
node = getNodeFromClosed(left);
costPos = map[left[0]][left[1]];
if (node.totalCost > this.totalCost + pathCost[costPos]) {
node.setTotalCost(this.totalCost + pathCost[costPos]);
node.setParent(this);
}
} else {
node = new Node(this, left[0], left[1], this.totalCost + pathCost[map[left[0]][left[1]]]);
openList.push(node);
createdList.push(node);
}
}
// leftTop = [rowAt - 1, colAt - 1];
// if (leftTop[0] >= 0 && leftTop[1] >= 0 && map[leftTop[0]][leftTop[1]] != obstacle) {
// if (getNodeFromClosed(leftTop) != null) {
// node = getNodeFromClosed(leftTop);
// costPos = map[leftTop[0]][leftTop[1]];
// if (node.totalCost > this.totalCost + costPos) {
// node.setTotalCost(this.totalCost + costPos);
// node.setParent(this);
// }
// } else {
// node = new Node(this, leftTop[0], leftTop[1], this.totalCost + pathCost[map[leftTop[0]][leftTop[1]]]);
// openList.push(node);
// createdList.push(node);
// }
// }
//can not use top, top is already defined as window class in JS
topNode = [rowAt - 1, colAt];
if (topNode[0] >= 0 && map[topNode[0]][topNode[1]] != obstacle) {
if (getNodeFromClosed(topNode) != null) {
node = getNodeFromClosed(topNode);
costPos = map[topNode[0]][topNode[1]];
if (node.totalCost > this.totalCost + pathCost[costPos]) {
node.setTotalCost(this.totalCost + pathCost[costPos]);
node.setParent(this);
}
} else {
node = new Node(this, topNode[0], topNode[1], this.totalCost + pathCost[map[topNode[0]][topNode[1]]]);
openList.push(node);
createdList.push(node);
}
}
}
}
function getNodeFromClosed(testNode) {
for (i = 0; i < createdList.length; i++) {
if (createdList[i].rowAt == testNode[0] && createdList[i].colAt == testNode[1]) return createdList[i];
}
return null;
}
var openList;
var closedList;
var createdList;
var startNode;
var searchNode;
var currentNode;
function run() {
startNode = getStartNode();
searchNode = startNode;
openList = [];
openList.push(startNode);
createdList = [];
createdList.push(startNode);
closedList = [];
iterationTime = 0;
//start iteration procedure
if (openList.length != 0) {
iteration();
}
}
function iteration() {
//remove highlight of currentNode
if (currentNode != null) document.getElementById(currentNode.rowAt + "_" + currentNode.colAt).style.border = "1px solid white";
currentNode = openList.pop();
//highlight currentNode
document.getElementById(currentNode.rowAt + "_" + currentNode.colAt).style.border = "1px solid red";
document.getElementById("iteration").innerText = "iteration:" + ++iterationTime;
if (currentNode.getMapValue() == goal) {
closedList.push(currentNode);
//find path through closedList
var node = closedList[closedList.length - 1]; //goal node
var path = new Array(node);
while (node.getMapValue != startNode.getMapValue) {
node = node.parent;
path.unshift(node);
}
for (i = path.length - 1; i > 0; i--) {
drawFinalPathLine(path[i], path[i].parent);
// console.log(path[i].rowAt + " " + path[i].colAt);
}
return;
} else setTimeout(() => {
currentNode.addNeightBor();
closedList.push(currentNode);
var i = 1;
for (; i < openList.length; i++) {
var k = i;
for (; k > 0; k--) {
if (parseInt(openList[k].totalCost) > parseInt(openList[k - 1].totalCost)) {
temp = openList[k];
openList[k] = openList[k - 1];
openList[k - 1] = temp;
}
}
}
iteration();
}, timeStep);
}
function getStartNode() {
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
if (map[i][j] == start)
return new Node(null, i, j, 0);
}
}
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>A* search</title>
</head>
<body>
<button type="button" onclick="run()">start</button>
<button type="button" onclick="location.reload()">refresh</button>
<a id="iteration"></a>
<a id="panel" style="display: grid;"></a>
<script type="text/javascript">
const colors = ["black", "gray", "green", "yellow", "red", "purple"];
const pxSize = 20;
const timeStep = 10;
// 0 - obstacle; 1 - blank_1; 2 - start; 4 - goal; blank_3 = 3; blank_5 = 5;
const start = 2, goal = 4, obstacle = 0, blank_1 = 1; blank_3 = 3; blank_5 = 5;
const pathCost = [null, 1, 0, 3, 0, 5];
const map = [[1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,5,5,5,5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,3,1,0,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,5,5,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1],[1,1,1,1,0,1,1,0,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,0,0,1,1,1,1,5,5,1,1,1,0,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0],[1,0,0,0,0,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1],[1,1,1,1,0,1,1,0,0,0,0,0,1,1,1,0,1,1,1,0,1,3,1,1,0,1,3,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1,3,1,1,1,1,1,1,1,0,1,1,0,1,1,1],[1,1,0,1,0,1,1,1,1,1,1,0,0,0,1,0,1,1,1,0,1,3,1,1,0,1,3,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1,3,1,1,1,1,1,1,1,0,1,1,0,1,1,1],[1,1,0,1,0,1,0,0,0,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1],[1,1,0,1,1,1,1,1,1,1,1,0,1,1,1,0,1,3,3,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,0,1,1,1,0,5,5,5,5,5,0,1,1,1,1,1,1],[0,0,0,1,1,1,1,3,1,1,1,1,1,1,1,1,3,3,3,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,1,1,0,1,1,0,0,1,1],[1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,3,3,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,5,5,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,0,0,1,1],[3,1,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,3,3,0,1,1,1,1,0,1,1,1,1,0,1,5,5,1,1,5,5,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0,0,1,1],[1,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,5,5,5,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1],[1,1,0,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,1,5,5,5,0,1,1,0,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,0,1,1,1,3,3,1,1,1,3,3,1,1,1,1,0,1,1,5,5,1,1,1,1,1,1,1,0,3,3,1,1,1,0,1,1,1,1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,1],[2,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,3,3,1,1,1,1,0,1,1,0,0,1,1,1,1,1,1,1,0,3,0,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1],[4,1,1,1,1,5,1,1,1,1,1,1,1,0,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,3,3,3,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,5,1,1,1,1,1,1,1,0,1,1,0,0,1,1,1,3,3,3,3,0,0,1,1,1,1,1,1,1,3,3,3,1,1,1,1,1,1,5,1,1,1,1,5,1,1,1,1,0,0,0,0,0,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,0,5,5,0,0,1,1,1,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[1,1,1,0,0,0,0,0,1,1,1,1,1,0,1,1,0,0,1,1,1,1,1,1,5,5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,1,1,1,0,1,1,5,5,5,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,5,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,1,1,1,1,1,3,3,3,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,3,3,3,1,1,0,0,0,0,1,1,1,0,0,0,1,3,3,1,1,0,1,1,3,1,1],[0,0,1,1,5,5,1,1,1,3,3,3,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,3,3,3,1,1,0,0,0,0,1,1,1,0,0,0,1,3,3,1,1,0,1,1,3,1,1],[0,0,1,1,5,5,1,1,1,3,3,3,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,1,1,1,1,1,1,1,1,1,0,0,0,1,3,3,1,1,0,1,1,3,1,1],[0,0,1,1,5,5,1,1,1,3,3,3,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,1,1,1,1,1,1,1,1,1,1,5,5,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,3,3,3,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,1,1,1,1,1,1,1,1,1,1,5,5,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,5,5,5,5,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1],[1,1,1,1,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,0,1,1,5,1,1,1,1,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,3,3,3,1,1,1,1],[1,5,5,5,5,5,5,5,1,1,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,1,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,1,5,1,1,1,1,1,1,1,1,1,1],[1,1,0,0,0,0,0,0,0,0,0,0,0,5,0,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,5,1,1,1,0,0,1,1,1,1,1],[1,1,1,1,1,0,0,1,1,1,1,1,1,5,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,0,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,5,5,5,5,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1],[1,1,1,1,1,0,0,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1]];
const row = map.length;
const col = map[0].length;
document.getElementById("panel").style.gridTemplateColumns = (pxSize + "px ").repeat(col);
document.getElementById("panel").style.gridTemplateRows = (pxSize + "px ").repeat(row);
//add grid - num at row * col
var addGridStringText = "<canvas id='myCanvas' width=" + pxSize * col + " height=" + pxSize * row + " style='position:absolute; solid #000000;'></canvas>" + "<canvas id='myCanvasForFinalPath' width=" + pxSize * col + " height=" + pxSize * row + " style='position:absolute; solid #000000;'></canvas>";
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++)
addGridStringText += "<div row='" + i + "' col='" + j + "' id='" + i + "_" + j + "' style=\"border:1px solid white; background-color:" + colors[map[i][j]] + "\"></div>";
}
document.getElementById("panel").innerHTML = addGridStringText;
function drawLine(from, to) {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.lineWidth = 5;
ctx.moveTo(pxSize * (from.colAt + 0.5), pxSize * (from.rowAt + 0.5));
ctx.lineTo(pxSize * (to.colAt + 0.5), pxSize * (to.rowAt + 0.5));
ctx.stroke();
}
function drawFinalPathLine(from, to) {
var c = document.getElementById("myCanvasForFinalPath");
var ctx = c.getContext("2d");
ctx.strokeStyle = '#cc0000';
ctx.lineWidth = 5;
ctx.moveTo(pxSize * (from.colAt + 0.5), pxSize * (from.rowAt + 0.5));
ctx.lineTo(pxSize * (to.colAt + 0.5), pxSize * (to.rowAt + 0.5));
ctx.stroke();
}
function Node(parent, rowat, colat, realCost) {
this.parent = parent;
this.rowAt = rowat;
this.colAt = colat;
this.realCost = realCost;
this.totalCost = Math.floor(Math.sqrt(Math.pow(goalNodePos[0] - this.rowAt, 2) + Math.pow(goalNodePos[1] - this.colAt, 2))) + this.realCost;
if (parent != null) drawLine(parent, this);
this.getMapValue = function () {
return map[this.rowAt][this.colAt];
}
this.getTotalCost = function () {
return this.totalCost;
}
this.setParent = function (p) {
this.parent = p;
}
this.setRealCost = function (cost) {
this.realCost = cost;
this.totalCost = Math.floor(Math.sqrt(Math.pow(goalNodePos[0] - this.rowAt, 2) + Math.pow(goalNodePos[1] - this.colAt, 2))) + this.realCost;
}
//from left top and normal clock order
this.addNeightBor = function () {
rowAt = this.rowAt;
colAt = this.colAt;
// rightTop = [rowAt - 1, colAt + 1];
// if (rightTop[1] < col && rightTop[0] >= 0 && map[rightTop[0]][rightTop[1]] != obstacle) {
// if (getNodeFromClosed(rightTop) != null) {
// node = getNodeFromClosed(rightTop);
// costPos = map[rightTop[0]][rightTop[1]];
// if (node.realCost > this.realCost + costPos) {
// node.setRealCost(this.realCost + costPos);
// node.setParent(this);
// }
// } else {
// node = new Node(this, rightTop[0], rightTop[1], this.realCost + pathCost[map[rightTop[0]][rightTop[1]]]);
// openList.push(node);
// createdList.push(node);
// }
// }
right = [rowAt, colAt + 1];
if (right[1] < col && map[right[0]][right[1]] != obstacle) {
if (getNodeFromClosed(right) != null) {
node = getNodeFromClosed(right);
costPos = map[right[0]][right[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setParent(this);
}
} else {
node = new Node(this, right[0], right[1], this.realCost + pathCost[map[right[0]][right[1]]]);
openList.push(node);
createdList.push(node);
}
}
// rightBott = [rowAt + 1, colAt + 1];
// if (rightBott[0] < row && rightBott[1] < col && map[rightBott[0]][rightBott[1]] != obstacle) {
// if (getNodeFromClosed(rightBott) != null) {
// node = getNodeFromClosed(rightBott);
// costPos = map[rightBott[0]][rightBott[1]];
// if (node.realCost > this.realCost + costPos) {
// node.setRealCost(this.realCost + costPos);
// node.setParent(this);
// }
// } else {
// node = new Node(this, rightBott[0], rightBott[1], this.realCost + pathCost[map[rightBott[0]][rightBott[1]]]);
// openList.push(node);
// createdList.push(node);
// }
// }
bott = [rowAt + 1, colAt];
if (bott[0] < row && map[bott[0]][bott[1]] != obstacle) {
if (getNodeFromClosed(bott) != null) {
node = getNodeFromClosed(bott);
costPos = map[bott[0]][bott[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setParent(this);
}
} else {
node = new Node(this, bott[0], bott[1], this.realCost + pathCost[map[bott[0]][bott[1]]]);
openList.push(node);
createdList.push(node);
}
}
// leftBott = [rowAt + 1, colAt - 1];
// if (leftBott[1] >= 0 && leftBott[0] < row && map[leftBott[0]][leftBott[1]] != obstacle) {
// if (getNodeFromClosed(leftBott) != null) {
// node = getNodeFromClosed(leftBott);
// costPos = map[leftBott[0]][leftBott[1]];
// if (node.realCost > this.realCost + costPos) {
// node.setRealCost(this.realCost + costPos);
// node.setParent(this);
// }
// } else {
// node = new Node(this, leftBott[0], leftBott[1], this.realCost + pathCost[map[leftBott[0]][leftBott[1]]]);
// openList.push(node);
// createdList.push(node);
// }
// }
left = [rowAt, colAt - 1];
if (left[1] >= 0 && map[left[0]][left[1]] != obstacle) {
if (getNodeFromClosed(left) != null) {
node = getNodeFromClosed(left);
costPos = map[left[0]][left[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setParent(this);
}
} else {
node = new Node(this, left[0], left[1], this.realCost + pathCost[map[left[0]][left[1]]]);
openList.push(node);
createdList.push(node);
}
}
// leftTop = [rowAt - 1, colAt - 1];
// if (leftTop[0] >= 0 && leftTop[1] >= 0 && map[leftTop[0]][leftTop[1]] != obstacle) {
// if (getNodeFromClosed(leftTop) != null) {
// node = getNodeFromClosed(leftTop);
// costPos = map[leftTop[0]][leftTop[1]];
// if (node.realCost > this.realCost + costPos) {
// node.setRealCost(this.realCost + costPos);
// node.setParent(this);
// }
// } else {
// node = new Node(this, leftTop[0], leftTop[1], this.realCost + pathCost[map[leftTop[0]][leftTop[1]]]);
// openList.push(node);
// createdList.push(node);
// }
// }
//can not use top, top is already defined as window class in JS
topNode = [rowAt - 1, colAt];
if (topNode[0] >= 0 && map[topNode[0]][topNode[1]] != obstacle) {
if (getNodeFromClosed(topNode) != null) {
node = getNodeFromClosed(topNode);
costPos = map[topNode[0]][topNode[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setParent(this);
}
} else {
node = new Node(this, topNode[0], topNode[1], this.realCost + pathCost[map[topNode[0]][topNode[1]]]);
openList.push(node);
createdList.push(node);
}
}
}
}
function getNodeFromClosed(testNode) {
for (i = 0; i < createdList.length; i++) {
if (createdList[i].rowAt == testNode[0] && createdList[i].colAt == testNode[1]) return createdList[i];
}
return null;
}
var openList;
var closedList;
var createdList;
var startNode;
var goalNode;
var currentNode;
function run() {
goalNodePos = getGoalNodePosition();
startNode = getStartNode();
openList = [];
openList.push(startNode);
createdList = [];
createdList.push(startNode);
closedList = [];
iterationTime = 0;
//start iteration procedure
if (openList.length != 0) {
iteration();
}
}
function iteration() {
//after 2s, remove highlight of currentNode
if (currentNode != null) document.getElementById(currentNode.rowAt + "_" + currentNode.colAt).style.border = "1px solid white";
currentNode = openList.pop();
//highlight currentNode
document.getElementById(currentNode.rowAt + "_" + currentNode.colAt).style.border = "1px solid red";
document.getElementById("iteration").innerText = "iteration:" + ++iterationTime;
if (currentNode.getMapValue() == goal) {
closedList.push(currentNode);
//find path through closedList
var node = closedList[closedList.length - 1]; //goal node
var path = new Array(node);
while (node.getMapValue != startNode.getMapValue) {
node = node.parent;
path.unshift(node);
}
for (i = path.length - 1; i > 0; i--) {
drawFinalPathLine(path[i], path[i].parent);
// console.log(path[i].rowAt + " " + path[i].colAt);
}
return;
} else setTimeout(() => {
currentNode.addNeightBor();
closedList.push(currentNode);
var i = 1;
for (; i < openList.length; i++) {
var k = i;
for (; k > 0; k--) {
if (openList[k].getTotalCost() > openList[k - 1].getTotalCost()) {
temp = openList[k];
openList[k] = openList[k - 1];
openList[k - 1] = temp;
}
}
}
iteration();
}, timeStep);
}
function getStartNode() {
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
if (map[i][j] == start)
return new Node(null, i, j, 0);
}
}
}
function getGoalNodePosition() {
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
if (map[i][j] == goal)
return [i, j];
}
}
}
</script>
</body>
</html>
六边形地图A*实现
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>A*_forHexShape</title>
</head>
<body>
<button type="button" onclick="run()">start</button>
<button type="button" onclick="location.reload()">refresh</button>
<a id="iteration"></a><br>
<a id="panel"></a>
<script type="text/javascript">
const colors = ["black", "gray", "green", "yellow", "red", "purple"];
const start = 2, goal = 4, obstacle = 0, blank_1 = 1; blank_3 = 3; blank_5 = 5;
const timeStep = 10;
const edgeLength = 15;
const pathCost = [null, 1, 0, 3, 0, 5];
// const map = [[2, 1, 0], [1, 1, 0], [0, 1, 4]];
const map = [[1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,5,5,5,5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,3,1,0,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,5,5,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1],[1,1,1,1,0,1,1,0,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,0,0,1,1,1,1,5,5,1,1,1,0,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0],[1,0,0,0,0,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1],[1,1,1,1,0,1,1,0,0,0,0,0,1,1,1,0,1,1,1,0,1,3,1,1,0,1,3,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1,3,1,1,1,1,1,1,1,0,1,1,0,1,1,1],[1,1,0,1,0,1,1,1,1,1,1,0,0,0,1,0,1,1,1,0,1,3,1,1,0,1,3,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1,3,1,1,1,1,1,1,1,0,1,1,0,1,1,1],[1,1,0,1,0,1,0,0,0,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1],[1,1,0,1,1,1,1,1,1,1,1,0,1,1,1,0,1,3,3,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,0,1,1,1,0,5,5,5,5,5,0,1,1,1,1,1,1],[0,0,0,1,1,1,1,3,1,1,1,1,1,1,1,1,3,3,3,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,1,1,0,1,1,0,0,1,1],[1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,3,3,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,5,5,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,0,0,1,1],[3,1,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,3,3,0,1,1,1,1,0,1,1,1,1,0,1,5,5,1,1,5,5,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0,0,1,1],[1,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,5,5,5,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1],[1,1,0,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,1,5,5,5,0,1,1,0,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,0,1,1,1,3,3,1,1,1,3,3,1,1,1,1,0,1,1,5,5,1,1,1,1,1,1,1,0,3,3,1,1,1,0,1,1,1,1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,1],[2,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,3,3,1,1,1,1,0,1,1,0,0,1,1,1,1,1,1,1,0,3,0,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1],[4,1,1,1,1,5,1,1,1,1,1,1,1,0,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,3,3,3,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,5,1,1,1,1,1,1,1,0,1,1,0,0,1,1,1,3,3,3,3,0,0,1,1,1,1,1,1,1,3,3,3,1,1,1,1,1,1,5,1,1,1,1,5,1,1,1,1,0,0,0,0,0,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,0,5,5,0,0,1,1,1,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[1,1,1,0,0,0,0,0,1,1,1,1,1,0,1,1,0,0,1,1,1,1,1,1,5,5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,1,1,1,0,1,1,5,5,5,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,5,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,1,1,1,1,1,3,3,3,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,3,3,3,1,1,0,0,0,0,1,1,1,0,0,0,1,3,3,1,1,0,1,1,3,1,1],[0,0,1,1,5,5,1,1,1,3,3,3,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,3,3,3,1,1,0,0,0,0,1,1,1,0,0,0,1,3,3,1,1,0,1,1,3,1,1],[0,0,1,1,5,5,1,1,1,3,3,3,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,1,1,1,1,1,1,1,1,1,0,0,0,1,3,3,1,1,0,1,1,3,1,1],[0,0,1,1,5,5,1,1,1,3,3,3,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,1,1,1,1,1,1,1,1,1,1,5,5,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,3,3,3,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,1,1,1,1,1,1,1,1,1,1,5,5,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,5,5,5,5,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1],[1,1,1,1,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,0,1,1,5,1,1,1,1,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,3,3,3,1,1,1,1],[1,5,5,5,5,5,5,5,1,1,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,1,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,1,5,1,1,1,1,1,1,1,1,1,1],[1,1,0,0,0,0,0,0,0,0,0,0,0,5,0,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,5,1,1,1,0,0,1,1,1,1,1],[1,1,1,1,1,0,0,1,1,1,1,1,1,5,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,0,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,5,5,5,5,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1],[1,1,1,1,1,0,0,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1]];
const row = map.length;
const col = map[0].length;
const sqrt_3divide2 = Math.sqrt(3) / 2;
const canvas_width = Math.round(sqrt_3divide2 * 2 * edgeLength * (col + 0.5));
const canvas_height = Math.round(edgeLength / 2 * (3 * row + 1));
document.getElementById("panel").innerHTML = "<canvas id='drawHexShape' width=" + canvas_width + " height=" + canvas_height + " style='position:absolute;'></canvas>" +
"<canvas id='drawPath' width=" + canvas_width + " height=" + canvas_height + " style='position:absolute;'></canvas>" +
"<canvas id='drawFinalPath' width=" + canvas_width + " height=" + canvas_height + " style='position:absolute;'></canvas>";
var position_information = new Array();
for (i = 0; i < row; i++) {
temp = new Array();
for (j = 0; j < col; j++) {
temp.push([Math.sqrt(3) * edgeLength * (j + (1 + i % 2) / 2), edgeLength * (1 + 3 * i / 2)]);
}
position_information.push(temp);
}
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
drawSixEdges(i, j, null);
}
}
function drawSixEdges(i, j, color) {
x = position_information[i][j][0], y = position_information[i][j][1];
c = document.getElementById("drawHexShape");
ctx = c.getContext("2d");
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(x + edgeLength * sqrt_3divide2, y - edgeLength / 2);
ctx.lineTo(x + edgeLength * sqrt_3divide2, y + edgeLength / 2);
ctx.lineTo(x, y + edgeLength);
ctx.lineTo(x - edgeLength * sqrt_3divide2, y + edgeLength / 2);
ctx.lineTo(x - edgeLength * sqrt_3divide2, y - edgeLength / 2);
ctx.lineTo(x, y - edgeLength);
ctx.lineTo(x + edgeLength * sqrt_3divide2, y - edgeLength / 2);
ctx.stroke();
if(color == null) ctx.fillStyle = colors[map[i][j]];
else ctx.fillStyle = color;
ctx.fill();
}
function drawLine(nodeP, node) {
x_from = position_information[nodeP.rowAt][nodeP.colAt][0], y_from = position_information[nodeP.rowAt][nodeP.colAt][1];
x_to = position_information[node.rowAt][node.colAt][0], y_to = position_information[node.rowAt][node.colAt][1];
c = document.getElementById("drawPath");
ctx = c.getContext("2d");
ctx.lineWidth = 2;
ctx.moveTo(x_from, y_from);
ctx.lineTo(x_to, y_to);
ctx.stroke();
}
function drawFinalPathLine(nodeP, node) {
x_from = position_information[nodeP.rowAt][nodeP.colAt][0], y_from = position_information[nodeP.rowAt][nodeP.colAt][1];
x_to = position_information[node.rowAt][node.colAt][0], y_to = position_information[node.rowAt][node.colAt][1];
c = document.getElementById("drawFinalPath");
ctx = c.getContext("2d");
ctx.lineWidth = 2;
ctx.strokeStyle = '#cc0000';
ctx.moveTo(x_from, y_from);
ctx.lineTo(x_to, y_to);
ctx.stroke();
}
function Node(parent, rowat, colat, realCost) {
this.parent = parent;
this.rowAt = rowat;
this.colAt = colat;
this.realCost = realCost;
this.totalCost = Math.floor(Math.sqrt(Math.pow((goalNodePos[0] - position_information[this.rowAt][this.colAt][0]) / edgeLength, 2) + Math.pow((goalNodePos[1] - position_information[this.rowAt][this.colAt][0]) / edgeLength, 2))) + this.realCost;
if (parent != null) drawLine(parent, this);
this.getMapValue = function () {
return map[this.rowAt][this.colAt];
}
this.setTotalCost = function() {
this.totalCost = Math.floor(Math.sqrt(Math.pow((goalNodePos[0] - position_information[this.rowAt][this.colAt][0]) / edgeLength, 2) + Math.pow((goalNodePos[1] - position_information[this.rowAt][this.colAt][0]) / edgeLength, 2))) + this.realCost;
}
this.setParent = function (p) {
this.parent = p;
}
this.setRealCost = function (cost) {
this.realCost = cost;
}
//from left top and normal clock order
this.addNeightBor = function () {
rowAt = this.rowAt;
colAt = this.colAt;
if (rowAt % 2 == 0) {
//top, lefttop, left, leftbott, bott, right
right = [rowAt, colAt + 1];
if (right[1] < col && map[right[0]][right[1]] != obstacle) {
if (getNodeFromCreated(right) != null) {
node = getNodeFromCreated(right);
costPos = map[right[0]][right[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setTotalCost();
node.setParent(this);
}
} else {
node = new Node(this, right[0], right[1], this.realCost + pathCost[map[right[0]][right[1]]]);
openList.push(node);
createdList.push(node);
}
}
bott = [rowAt + 1, colAt];
if (bott[0] < row && map[bott[0]][bott[1]] != obstacle) {
if (getNodeFromCreated(bott) != null) {
node = getNodeFromCreated(bott);
costPos = map[bott[0]][bott[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setTotalCost();
node.setParent(this);
}
} else {
node = new Node(this, bott[0], bott[1], this.realCost + pathCost[map[bott[0]][bott[1]]]);
openList.push(node);
createdList.push(node);
}
}
leftBott = [rowAt + 1, colAt - 1];
if (leftBott[1] >= 0 && leftBott[0] < row && map[leftBott[0]][leftBott[1]] != obstacle) {
if (getNodeFromCreated(leftBott) != null) {
node = getNodeFromCreated(leftBott);
costPos = map[leftBott[0]][leftBott[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setTotalCost();
node.setParent(this);
}
} else {
node = new Node(this, leftBott[0], leftBott[1], this.realCost + pathCost[map[leftBott[0]][leftBott[1]]]);
openList.push(node);
createdList.push(node);
}
}
left = [rowAt, colAt - 1];
if (left[1] >= 0 && map[left[0]][left[1]] != obstacle) {
if (getNodeFromCreated(left) != null) {
node = getNodeFromCreated(left);
costPos = map[left[0]][left[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setTotalCost();
node.setParent(this);
}
} else {
node = new Node(this, left[0], left[1], this.realCost + pathCost[map[left[0]][left[1]]]);
openList.push(node);
createdList.push(node);
}
}
leftTop = [rowAt - 1, colAt - 1];
if (leftTop[0] >= 0 && leftTop[1] >= 0 && map[leftTop[0]][leftTop[1]] != obstacle) {
if (getNodeFromCreated(leftTop) != null) {
node = getNodeFromCreated(leftTop);
costPos = map[leftTop[0]][leftTop[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setTotalCost();
node.setParent(this);
}
} else {
node = new Node(this, leftTop[0], leftTop[1], this.realCost + pathCost[map[leftTop[0]][leftTop[1]]]);
openList.push(node);
createdList.push(node);
}
}
//can not use top, top is already defined as window class in JS
topNode = [rowAt - 1, colAt];
if (topNode[0] >= 0 && map[topNode[0]][topNode[1]] != obstacle) {
if (getNodeFromCreated(topNode) != null) {
node = getNodeFromCreated(topNode);
costPos = map[topNode[0]][topNode[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setTotalCost();
node.setParent(this);
}
} else {
node = new Node(this, topNode[0], topNode[1], this.realCost + pathCost[map[topNode[0]][topNode[1]]]);
openList.push(node);
createdList.push(node);
}
}
}
if (rowAt % 2 == 1) {
//top, righttop, right, rtightbott, bott, left
rightTop = [rowAt - 1, colAt + 1];
if (rightTop[1] < col && rightTop[0] >= 0 && map[rightTop[0]][rightTop[1]] != obstacle) {
if (getNodeFromCreated(rightTop) != null) {
node = getNodeFromCreated(rightTop);
costPos = map[rightTop[0]][rightTop[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setTotalCost();
node.setParent(this);
}
} else {
node = new Node(this, rightTop[0], rightTop[1], this.realCost + pathCost[map[rightTop[0]][rightTop[1]]]);
openList.push(node);
createdList.push(node);
}
}
right = [rowAt, colAt + 1];
if (right[1] < col && map[right[0]][right[1]] != obstacle) {
if (getNodeFromCreated(right) != null) {
node = getNodeFromCreated(right);
costPos = map[right[0]][right[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setTotalCost();
node.setParent(this);
}
} else {
node = new Node(this, right[0], right[1], this.realCost + pathCost[map[right[0]][right[1]]]);
openList.push(node);
createdList.push(node);
}
}
rightBott = [rowAt + 1, colAt + 1];
if (rightBott[0] < row && rightBott[1] < col && map[rightBott[0]][rightBott[1]] != obstacle) {
if (getNodeFromCreated(rightBott) != null) {
node = getNodeFromCreated(rightBott);
costPos = map[rightBott[0]][rightBott[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setTotalCost();
node.setParent(this);
}
} else {
node = new Node(this, rightBott[0], rightBott[1], this.realCost + pathCost[map[rightBott[0]][rightBott[1]]]);
openList.push(node);
createdList.push(node);
}
}
bott = [rowAt + 1, colAt];
if (bott[0] < row && map[bott[0]][bott[1]] != obstacle) {
if (getNodeFromCreated(bott) != null) {
node = getNodeFromCreated(bott);
costPos = map[bott[0]][bott[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setTotalCost();
node.setParent(this);
}
} else {
node = new Node(this, bott[0], bott[1], this.realCost + pathCost[map[bott[0]][bott[1]]]);
openList.push(node);
createdList.push(node);
}
}
left = [rowAt, colAt - 1];
if (left[1] >= 0 && map[left[0]][left[1]] != obstacle) {
if (getNodeFromCreated(left) != null) {
node = getNodeFromCreated(left);
costPos = map[left[0]][left[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setTotalCost();
node.setParent(this);
}
} else {
node = new Node(this, left[0], left[1], this.realCost + pathCost[map[left[0]][left[1]]]);
openList.push(node);
createdList.push(node);
}
}
//can not use top, top is already defined as window class in JS
topNode = [rowAt - 1, colAt];
if (topNode[0] >= 0 && map[topNode[0]][topNode[1]] != obstacle) {
if (getNodeFromCreated(topNode) != null) {
node = getNodeFromCreated(topNode);
costPos = map[topNode[0]][topNode[1]];
if (node.realCost > this.realCost + pathCost[costPos]) {
node.setRealCost(this.realCost + pathCost[costPos]);
node.setTotalCost();
node.setParent(this);
}
} else {
node = new Node(this, topNode[0], topNode[1], this.realCost + pathCost[map[topNode[0]][topNode[1]]]);
openList.push(node);
createdList.push(node);
}
}
}
}
}
function getNodeFromCreated(testNode) {
for (i = 0; i < createdList.length; i++) {
if (createdList[i].rowAt == testNode[0] && createdList[i].colAt == testNode[1]) return createdList[i];
}
return null;
}
var openList;
var closedList;
var createdList;
var startNode;
var goalNode;
var currentNode;
function run() {
goalNodePos = getGoalNodePosition();
startNode = getStartNode();
openList = [];
openList.push(startNode);
createdList = [];
createdList.push(startNode);
closedList = [];
iterationTime = 0;
//start iteration procedure
if (openList.length != 0) {
iteration();
}
}
function iteration() {
//remove highlight of currentNode
if (currentNode != null) drawSixEdges(currentNode.rowAt, currentNode.colAt, null);
currentNode = openList.pop();
//highlight currentNode
drawSixEdges(currentNode.rowAt, currentNode.colAt, "#D81B60");
document.getElementById("iteration").innerText = "iteration:" + ++iterationTime;
if (currentNode.getMapValue() == goal) {
closedList.push(currentNode);
//find path through closedList
var node = closedList[closedList.length - 1]; //goal node
var path = new Array(node);
while (node.getMapValue != startNode.getMapValue) {
node = node.parent;
path.unshift(node);
}
for (i = path.length - 1; i > 0; i--) {
drawFinalPathLine(path[i], path[i].parent);
}
return;
} else setTimeout(() => {
currentNode.addNeightBor();
closedList.push(currentNode);
var i = 1;
for (; i < openList.length; i++) {
var k = i;
for (; k > 0; k--) {
if (openList[k].totalCost > openList[k - 1].totalCost) {
temp = openList[k];
openList[k] = openList[k - 1];
openList[k - 1] = temp;
}
}
}
iteration();
}, timeStep);
}
function getStartNode() {
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
if (map[i][j] == start)
return new Node(null, i, j, 0);
}
}
}
function getGoalNodePosition() {
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
if (map[i][j] == goal)
return position_information[i][j];
}
}
}
</script>
</body>
</html>