dijkstra and A* search 演算法心得及JS實現

順炸天發表於2020-11-28

dijkstra and A* search 演算法心得及JS實現

目錄

dijkstra and A* search

在BFS/DFS的基礎上二者補充了什麼?

JS程式碼實現

六邊形地圖A*實現


在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>

 

相關文章