A*算法 javascript模拟
本例子是用A*方法
先上一个4方向的A*
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html" charset="utf-8">
<title>A* javascript</title>
<style type="text/css">
.map{ background-color: #CCCCCC; }
.map td{ width : 20px; height: 20px; }
.map_close{ background-color: #000000; }
.map_open{ background-color: #FFFFFF; }
</style>
</head>
<body>
</body>
</html>
<script type="text/javascript">
var map = {
gridWidth : 30,
girdHeight : 20,
roadBlock : 0.1,
init : function() {
map.costEnergy_S = 10;
map.costEnergy_L = 14;
map.openArea = [];
map.closeArea = {};
map._createMapData();
},
/** 创建网格数据 */
_createMapData : function() {
var cache = map.cache;
map.data = [];
for (var y = 0, _arr; y < map.girdHeight; y++) {
_arr = [];
for (var x = 0, mapNode; x < map.gridWidth; x++) {
mapNode = new map.Node(x, y);
if (Math.random() < map.roadBlock) {
mapNode.isRoadBlock = true;
map.closeArea[mapNode.id] = mapNode;
};
map.cache[mapNode.id] = mapNode;
_arr.push(mapNode);
};
map.data.push(_arr);
};
},
/** 建立地图网格 */
getUI : function() {
var table = [];
var data = map.data;
table.push('<table cellpadding="0" cellspacing="1" bgcolor="0" class="map">');
table.push('<tbody>');
for (var y = 0, yl = data.length; y < yl; y++) {
table.push('<tr>');
for (var x = 0, xl = data[y].length; x < xl; x++) {
table.push('<td id="'+ data[y][x].id +'" class="'+ (data[y][x].isRoadBlock === true ? 'map_close' : 'map_open') +'"></td>')
}
table.push('</tr>');
};
table.push('</tbody>');
table.push('</table>');
map.container = document.createElement('div');
map.container.innerHTML = table.join('');
return map.container;
},
Node : function(x, y) {
this.x = x;
this.y = y;
this.id = 'map_'+ x + '_' + y;
this.isRoadBlock = false;
this.prev = null;
this.fObj = null;
},
getNode : function(x, y) {
return map.cache['map_'+ x + '_'+ y];
},
setStartNode : function(node) {
map.startNode = node;
},
setEndNode : function(node) {
map.endNode = node;
},
/** 检测当前开启列表中是否含有传进来的Node 存在则从开启列表中选中将其返回*/
_isOpenAreaExitNode : function(node) {
var openArea = map.openArea;
for (var i = 0, l = openArea.length; i < l; i++) {
if (openArea[i].id === node.id) return openArea[i];
};
return null;
},
getPath : function() {
map.getAroundNode(map.startNode);
if (map.openArea.length == 0) return;
map.search(map.endNode);
},
/** 获取当前点的F G H值 */
getF : function(cNode, aNode) {
var energyW = Math.abs(map.endNode.x - aNode.x) * map.costEnergy_S;
var energyH = Math.abs(map.endNode.y - aNode.y) * map.costEnergy_S;
var _H = energyW + energyH;
var _G = (Math.abs(aNode.x - cNode.x) === Math.abs(aNode.y - cNode.y) ? map.costEnergy_L : map.costEnergy_S);
if (cNode.fObj) _G = cNode.fObj.G + _G;
return { F : _H + _G, H : _H, G : _G };
},
/** 获取当前父节点周围的点 */
getAroundNode : function(node) {
var maxHeight = map.girdHeight;
var maxWidth = map.gridWidth;
var nodeX;
var nodeY;
for (var x = -1; x <= 1; x++) {
nodeX = node.x + x;
for (var y = -1, mapNode, _fObj, tmpNode; y <= 1; y++) {
nodeY = node.y + y;
//剔除本身 以及斜角方向
if (x === 0 && y === 0 || Math.abs(x) == Math.abs(y)) continue;
if (nodeX >= 0 && nodeY >= 0 && nodeX < maxWidth && nodeY < maxHeight) {
mapNode = map.getNode(nodeX, nodeY);
if (!map.closeArea[mapNode.id]) {
_fObj = map.getF(node, mapNode);
// 如果周围节点已在开启区域的 根据当前节点 获取新的G值 与当前点的进行比较 如果小于以前的G值 则指定当前节点为其父节点
tmpNode = map._isOpenAreaExitNode(mapNode);
if (tmpNode) {
if (tmpNode.fObj.G <= _fObj.G) continue;
};
mapNode.fObj = _fObj;
mapNode.prev = node;
map.openArea.push(mapNode);
};
};
};
};
},
/** 不断删除查找周围节点,直到找寻到结束点 */
search : function(node) {
while(!map.closeArea[node.id]) {
var _fMinNode = map._getFMin();
if (!_fMinNode) break;
map.getAroundNode(_fMinNode);
map.search(node);
};
if (map.closeArea[node.id]) {
map._drawRoad(node);
};
},
/** 绘制路线 */
_drawRoad : function(node) {
document.getElementById(node.id).style.background = '#EFA626';
if (node.prev !== map.startNode) map._drawRoad(node.prev);
},
/** 从开启列表从寻找F点最小的点 从开启列表移除 移入关闭列表 */
_getFMin : function() {
if (map.openArea.length == 0) return null;
map._orderOpenArea();
map.closeArea[map.openArea[0].id] = map.openArea[0];
//document.getElementById(map.openArea[0].id).innerHTML = map.openArea[0].fObj.F + '^' + map.openArea[0].fObj.G + '^' + map.openArea[0].fObj.H;
return map.openArea.shift();
},
/** 排序开启列表 */
_orderOpenArea : function() {
this.openArea.sort(function(objF, objN) {
return objF.fObj.F - objN.fObj.F;
});
},
data : [],
openArea : [],
closeArea : {},
cache : {},
startNode : null,
endNode : null,
container : null
};
(function() {
map.roadBlock = 0.3;
map.init();
var mapUI = map.getUI();
var startNode = null;
var isRun = false;
// 计时器
var Timer = function (){
this.startTime = + new Date;
};
Timer.prototype.stop = function(){
return + new Date - this.startTime;
};
document.getElementsByTagName('body')[0].appendChild(mapUI);
mapUI.onclick = function(event) {
event = event || window.event;
var target = event.target || event.srcElement;
if (isRun) return;
if (target.nodeName !== "TD") return;
var node = map.cache[target.id];
if (node.isRoadBlock) return;
if (!node) return;
if (startNode) {
map.setEndNode(node);
var time = new Timer;
map.getPath();
document.title = '[' + time.stop() + '毫秒] ' + document.title;
isRun = true;
target.style.backgroundColor = 'red';
} else {
startNode = node;
map.setStartNode(node);
target.style.backgroundColor = 'green';
};
};
})();
</script>
鉴于A* 还有8方向的 后来再4方向的基础上实现了下8方向
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html" charset="utf-8">
<title>A* javascript</title>
<style type="text/css">
.map{ background-color: #CCCCCC; }
.map td{ width : 30px; height: 30px; }
.map_close{ background-color: #000000; }
.map_open{ background-color: #FFFFFF; }
</style>
</head>
<body>
</body>
</html>
<script type="text/javascript">
var map = {
gridWidth : 30,
girdHeight : 20,
roadBlock : 0.1,
init : function() {
map.costEnergy_S = 10;
map.costEnergy_L = 14;
map.openArea = [];
map.closeArea = {};
map._createMapData();
},
/** 创建网格数据 */
_createMapData : function() {
var cache = map.cache;
map.data = [];
for (var y = 0, _arr; y < map.girdHeight; y++) {
_arr = [];
for (var x = 0, mapNode; x < map.gridWidth; x++) {
mapNode = new map.Node(x, y);
if (Math.random() < map.roadBlock) {
mapNode.isRoadBlock = true;
map.closeArea[mapNode.id] = mapNode;
};
map.cache[mapNode.id] = mapNode;
_arr.push(mapNode);
};
map.data.push(_arr);
};
},
/** 建立地图网格 */
getUI : function() {
var table = [];
var data = map.data;
table.push('<table cellpadding="0" cellspacing="1" bgcolor="0" class="map">');
table.push('<tbody>');
for (var y = 0, yl = data.length; y < yl; y++) {
table.push('<tr>');
for (var x = 0, xl = data[y].length; x < xl; x++) {
table.push('<td id="'+ data[y][x].id +'" class="'+ (data[y][x].isRoadBlock === true ? 'map_close' : 'map_open') +'"></td>')
}
table.push('</tr>');
};
table.push('</tbody>');
table.push('</table>');
map.container = document.createElement('div');
map.container.innerHTML = table.join('');
return map.container;
},
Node : function(x, y) {
this.x = x;
this.y = y;
this.id = 'map_'+ x + '_' + y;
this.isRoadBlock = false;
this.prev = null;
this.fObj = null;
},
getNode : function(x, y) {
return map.cache['map_'+ x + '_'+ y];
},
setStartNode : function(node) {
map.startNode = node;
},
setEndNode : function(node) {
map.endNode = node;
},
/** 检测当前开启列表中是否含有传进来的Node 存在则从开启列表中选中将其返回*/
_isOpenAreaExitNode : function(node) {
var openArea = map.openArea;
for (var i = 0, l = openArea.length; i < l; i++) {
if (openArea[i].id === node.id) return openArea[i];
};
return null;
},
getPath : function() {
map.getAroundNode(map.startNode);
if (map.openArea.length == 0) return;
map.search(map.endNode);
},
/** 获取当前点的F G H值 */
getF : function(cNode, aNode) {
var energyW = Math.abs(map.endNode.x - aNode.x) * map.costEnergy_S;
var energyH = Math.abs(map.endNode.y - aNode.y) * map.costEnergy_S;
var _H = energyW + energyH;
var _G = (Math.abs(aNode.x - cNode.x) === Math.abs(aNode.y - cNode.y) ? map.costEnergy_L : map.costEnergy_S);
if (cNode.fObj) _G = cNode.fObj.G + _G;
return { F : _H + _G, H : _H, G : _G };
},
/** 获取当前父节点周围的点 */
getAroundNode : function(node) {
var maxHeight = map.girdHeight;
var maxWidth = map.gridWidth;
var nodeX;
var nodeY;
var corner = [];
for (var x = -1; x <= 1; x++) {
nodeX = node.x + x;
for (var y = -1, mapNode, _fObj, tmpNode; y <= 1; y++) {
nodeY = node.y + y;
//剔除本身
if (x === 0 && y === 0) continue;
if (nodeX >= 0 && nodeY >= 0 && nodeX < maxWidth && nodeY < maxHeight) {
mapNode = map.getNode(nodeX, nodeY);
//查找周围的新节点, 如果新节点处于拐角则跳过
if (Math.abs(x) == Math.abs(y) && map._isCorner(mapNode, { x : x, y : y })) continue;
if (!map.closeArea[mapNode.id]) {
_fObj = map.getF(node, mapNode);
// 如果周围节点已在开启区域的 根据当前节点 获取新的G值 与当前点的进行比较 如果小于以前的G值 则指定当前节点为其父节点
tmpNode = map._isOpenAreaExitNode(mapNode);
if (tmpNode) {
if (tmpNode.fObj.G <= _fObj.G) continue;
};
mapNode.fObj = _fObj;
mapNode.prev = node;
map.openArea.push(mapNode);
};
};
};
};
},
/** 监测节点是否为拐角, 如果是 从开启列表中移除穿越拐角到达的点 */
_isCorner : function(node, obj) {
var closeArea = map.closeArea;
var x = obj.x;
var y = obj.y;
var getNode = map.getNode;
if (Math.abs(x) === Math.abs(y)) {
if (x > 0 && y < 0) {
return closeArea[new getNode(node.x, node.y + 1).id] || closeArea[new getNode(node.x - 1, node.y).id];
};
if (x < 0 && y > 0) {
return closeArea[new getNode(node.x, node.y - 1).id] || closeArea[new getNode(node.x + 1, node.y).id];
};
if (x === y && x > 0) {
return closeArea[new getNode(node.x, node.y - 1).id] || closeArea[new getNode(node.x - 1, node.y).id];
};
if (x === y && x < 0) {
return closeArea[new getNode(node.x, node.y + 1).id] || closeArea[new getNode(node.x + 1, node.y).id];
};
};
},
/** 不断删除查找周围节点,直到找寻到结束点 */
search : function(node) {
while(!map.closeArea[node.id]) {
var _fMinNode = map._getFMin();
if (!_fMinNode) break;
map.getAroundNode(_fMinNode);
map.search(node);
};
if (map.closeArea[node.id]) {
map._drawRoad(node);
};
},
/** 绘制路线 */
_drawRoad : function(node) {
document.getElementById(node.id).style.background = '#EFA626';
if (node.prev !== map.startNode) map._drawRoad(node.prev);
},
/** 从开启列表从寻找F点最小的点 从开启列表移除 移入关闭列表 */
_getFMin : function() {
if (map.openArea.length == 0) return null;
map._orderOpenArea();
map.closeArea[map.openArea[0].id] = map.openArea[0];
//document.getElementById(map.openArea[0].id).innerHTML = map.openArea[0].fObj.F + '^' + map.openArea[0].fObj.G + '^' + map.openArea[0].fObj.H;
return map.openArea.shift();
},
/** 排序开启列表 */
_orderOpenArea : function() {
this.openArea.sort(function(objF, objN) {
return objF.fObj.F - objN.fObj.F;
});
},
data : [],
openArea : [],
closeArea : {},
cache : {},
startNode : null,
endNode : null,
container : null
};
(function() {
map.roadBlock = 0.3;
map.init();
var mapUI = map.getUI();
var startNode = null;
var isRun = false;
// 计时器
var Timer = function (){
this.startTime = + new Date;
};
Timer.prototype.stop = function(){
return + new Date - this.startTime;
};
document.getElementsByTagName('body')[0].appendChild(mapUI);
mapUI.onclick = function(event) {
event = event || window.event;
var target = event.target || event.srcElement;
if (isRun) return;
if (target.nodeName !== "TD") return;
var node = map.cache[target.id];
if (node.isRoadBlock) return;
if (!node) return;
if (startNode) {
map.setEndNode(node);
var time = new Timer;
map.getPath();
document.title = '[' + time.stop() + '毫秒] ' + document.title;
isRun = true;
target.style.backgroundColor = 'red';
} else {
startNode = node;
map.setStartNode(node);
target.style.backgroundColor = 'green';
};
};
})();
</script>
关于A*算法的原理 参考 http://hi.baidu.com/jklzt/blog/item/cbfc1d3e03ab8e19baa167b4.html

浙公网安备 33010602011771号