在学习A算法之前,很好奇的是A为什么叫做A*。在知乎上找到一个回答,大致意思是说,在A算法之前有一种基于启发式探索的方法来提高Dijkstra算法的速度,这个算法叫做A1。后来的改进算法被称为A。*这个符号是从统计文献中借鉴来的,用来表示相对一个旧有标准的最优估计
.
博客图有一篇文章介绍原理:
https://www.cnblogs.com/iwiniwin/p/10793654.html
说有比较通俗易懂,大家参考一下。
游戏效果在我博客这里:https://www.dongee.net/static/a.html 大家可以玩一下。
以下是html5+javascript的代码,大家直接复制到浏览器上就可以运行:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>游戏自动寻路算法</title>
</head>
<style>
* {
margin: 0;
padding: 0;
}
.map {
margin: 0 auto;
border: 1px solid #aaa;
display: flex;
flex-wrap: wrap;
}
.btn {
width: 100%;
text-align: center;
margin-bottom: 5px;
display: flex;
justify-content: center;
}
.item {
width: 20px;
height: 20px;
display: inline-block;
border: 1px solid #aaa;
color: #eee;
line-height: 20px;
text-align: center;
}
.item-start {
background-color: #f00;
font-weight: bold;
color: #eee;
}
.item-end {
background-color: rgb(103, 243, 47);
font-weight: bold;
color: #eee;
}
.item-block {
background-color: #666;
}
.item-found {
background-color: rgb(248, 89, 89)
}
.item-map {
background-color: #eee;
}
</style>
<body>
<div class="btn">
<div>游戏自动寻路算法</div>
<div style="margin-left: 10px;">
地图行数<input id="txtNumber1" type="number" value="80" max="86" min="20" placeholder="地图行数" />
地图列数<input id="txtNumber2" type="number" value="40" max="42" min="20" placeholder="地图列数" />
障碍出现机率<input id="txtBlock" type="number" value="30" max="100" min="10" placeholder="障碍出现机率" />
<label for="chkDiagonal">是否可以对角走</label><input id="chkDiagonal" type="checkbox" checked />
<button onclick="buildMap()">生成地图</button>
<button onclick="start()">开始寻路</button>
</div>
<!-- <button onclick="reStart()">重新寻路</button> -->
</div>
<div id="dvMap" class="map">
</div>
</body>
<script>
let itemCountX = 0;
let itemCountY = 0;
let itemBlockCount = 0;
let itemWidth = 0;
let itemHeight = 0;
let itemIsDiagonal = true;
let itemX = 0;
let itemTemp = "<div class='item item-map'></div>";
let mapdiv = document.getElementById("dvMap");
let itemData = { i: 0, f: 0, g: 0, h: 0, p: {} };
let itemDatas = [];
let mapData = [];
let openList = [];
let closeList = [];
let startIndex = 0;
let endIndex = 0;
let isFinding = false;
function reset() {
itemCountX = parseInt(document.getElementById("txtNumber1").value);
itemCountY = parseInt(document.getElementById("txtNumber2").value);
itemBlockCount = parseInt(document.getElementById("txtBlock").value);
itemIsDiagonal = document.getElementById("chkDiagonal").checked
itemWidth = 0;
itemHeight = 0;
itemX = 0;
itemDatas = [];
mapData = [];
openList = [];
closeList = [];
startIndex = 0;
endIndex = 0;
mapdiv.innerHTML = "";
isFinding = false;
}
function buildMap(el) {
reset();
if (itemCountX < 20 || itemCountY < 20) {
alert("行列数不要小于20")
return
}
if (itemCountX > 200 || itemCountY > 200) {
alert("行列数不要大于200")
return
}
randomMap();
let count = mapData.length
for (let i = 0; i < count; i++) {
let itemData = { i, f: 0, g: 0, h: 0, p: {} };
let item = document.createElement('div')
item.id = "item" + i
if (mapData[i] == 0) {
item.className = "item item-map";
}
if (mapData[i] == 1) {
item.className = "item item-start";
addOpen(buildItem(i, null))
item.innerText = "入";
}
if (mapData[i] == 2) {
item.className = "item item-end";
item.innerText = "出";
}
if (mapData[i] == 3) {
item.className = "item item-block";
addClose(buildItem(i, null))
}
mapdiv.appendChild(item);
}
let first = mapdiv.getElementsByClassName("item")[0];
itemWidth = first.clientWidth;
itemHeight = first.clientHeight;
itemX = itemWidth + Math.floor(itemWidth / 2);
mapdiv.style.width = itemCountX * (itemWidth + 2) + 'px'
mapdiv.style.height = itemCountY * (itemHeight + 2) + 'px'
}
function randomMap() {
let len = itemCountX * itemCountY
for (let i = 0; i < len; i++) {
let r = randomNum(0, 100)
if (r < itemBlockCount) {
mapData.push(3);
}
else {
mapData.push(0);
}
}
let mid = len / 2;
startIndex = randomNum(0, mid);
endIndex = randomNum(mid, len);
mapData[startIndex] = 1;
mapData[endIndex] = 2;
}
function addOpen(item) {
let ri = openList.findIndex(r => r.i == item.i);
if (ri == -1) {
openList.push(item)
}
}
function removeOpen(item) {
let ri = openList.findIndex(r => r.i == item.i);
if (ri > -1) {
openList.splice(ri, 1)
}
}
function addClose(item) {
let ri = closeList.findIndex(r => r.i == item.i);
if (ri == -1) {
closeList.push(item)
}
}
function getParentLength(w, p) {
return itemCountX
}
function getParentLength2(w, p) {
w = w || 0
let result = w
if (p && p.g) {
result += p.g
}
return result
}
function getH(i) {
let x = Math.abs(endIndex % itemCountX - i % itemCountX)
let y = Math.abs(Math.floor(i / itemCountX) - Math.floor(endIndex / itemCountX))
return x * itemWidth + y * itemHeight;
}
function getAround(i, p) {
let len = itemCountX * itemCountY
let items = [];
let left = i - 1;
let right = i + 1;
let up = i - itemCountX;
let down = i + itemCountX;
let leftup = up - 1
let leftdown = down - 1
let rightup = up + 1
let rightdown = down + 1;
let row = Math.floor(i / itemCountX) + 1
if (left >= 0) {
let tmp = Math.floor(left / itemCountX) + 1;
if (tmp == row) {
items.push(buildItem(left, p, itemWidth));
if (itemIsDiagonal && leftdown <= len) {
items.push(buildItem(leftdown, p, itemX));
}
if (itemIsDiagonal && leftup > 0) {
items.push(buildItem(leftup, p, itemX));
}
}
}
if (right < len) {
let tmp = Math.floor(right / itemCountX) + 1;
if (tmp == row) {
items.push(buildItem(right, p, itemWidth));
if (itemIsDiagonal && rightdown <= len) {
items.push(buildItem(rightdown, p, itemX));
}
if (itemIsDiagonal && rightup > 0) {
items.push(buildItem(rightup, p, itemX));
}
}
}
if (up >= 0) {
items.push(buildItem(up, p, itemWidth));
}
if (down < len) {
items.push(buildItem(down, p, itemWidth));
}
return items;
}
function getFirstItem(items) {
if (items && items.length > 0) {
items = items.sort((n1, n2) => {
return n1.f - n2.f;
});
return items[0]
}
return null
}
function start() {
if (isFinding) {
alert("正在分板路线,请稍等");
}
else {
isFinding = true;
startFind()
}
}
function reStart() {
openList = []
closeList = []
openList.push(buildItem(startIndex, null))
mapData.map((c, i) => {
if (c == 3) {
addClose(buildItem(i, null))
}
})
isFinding = false;
start()
}
function startFind(a) {
a = a || openList[0]
console.log("开始寻找:" + a.i)
let aroundsItems = getAround(a.i, a);
if (aroundsItems.findIndex(r => r.i == endIndex) > -1) {
let item = buildItem(endIndex, a);
console.log("找完路径")
console.log(item);
printPath(item)
isFinding = false;
return
}
for (let c of closeList) {
let v = c.i
let ri = aroundsItems.findIndex(r => r.i == v);
if (ri > -1) {
aroundsItems.splice(ri, 1)
}
}
removeOpen(a)
addClose(a)
if (aroundsItems.length > 0) {
for (let ai of aroundsItems) {
addOpen(ai)
}
}
if (openList.length < 1) {
alert("找不到路径")
isFinding = false;
printPath(a)
return
}
let best = getFirstItem(openList)
startFind(best)
// console.log(best);
// console.log(openList);
// console.log(closeList);
}
function buildItem(i, parent, g) {
if (1 == mapData[i]) {
return { i, f: 0, g: 0, h: 0, p: {} }
}
let item = { i, f: 0, g: 0, h: 0, p: parent }
item.h = getH(i)
item.g = getParentLength2(g, parent)
item.f = item.g + item.h
return item;
}
function printPath(p) {
let result = [];
result.push(p.i)
let p1 = p.p;
while (p1) {
if (p1.i) {
result.push(p1.i)
}
p1 = p1.p
}
// result.reverse()
setTimeout(printDiv, 100, result)
// setTimeout()
// result.map(r => {
// if (mapData[r] == 0) {
// document.getElementById("item" + r).className = "item item-found"
// }
// })
}
function printDiv(result) {
let i = result.pop()
if (i) {
if (i != startIndex && i != endIndex) {
document.getElementById("item" + i).className = "item item-found"
}
setTimeout(printDiv, 50, result)
}
}
function init() {
buildMap();
}
function randomNum(minNum, maxNum) {
switch (arguments.length) {
case 1:
return parseInt(Math.random() * minNum + 1, 10);
break;
case 2:
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
break;
default:
return 0;
break;
}
}
function test() {
let items = getAround(parseInt(document.getElementById("txtNumber1").value))
console.log(items);
}
setTimeout(init, 0);
</script>
<script>
// let arr = [2,1,3,5,4];
// arr= arr.sort((n1,n2)=>{
// return n2-n1;
// });
// console.log(arr);
</script>
</html>