A*算法学习 附Javascript 算法实现练习

因工作需要,学习和了解一些路径算法,正好用Javascript 练练手。


 

ContractedBlock.gif ExpandedBlockStart.gif A*寻路算法演示
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    
<title>A*寻路算法演示 [http://mapig.cnblogs.com/]</title>
    
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js " type="text/javascript"></script>

    
<script type="text/javascript">

        
/*----------------------------------------------------------------
        // http://mapig.cnblogs.com/
        // 文件名:A*寻路算法实现
        // 文件功能描述:A*寻路算法实现
        // 修改标识:
        // 修改描述:2009/07/06 v0.1   , 按照基本算法简单实现,未做如任何优化 
        //----------------------------------------------------------------
*/

        Function.prototype.bind 
= function(object) {
            
var method = this;
            
return function() {
                method.apply(object, arguments);
            }
        }
        
        
//状态枚举
        var status = {
            
//网格
            grid: 0,
            
//起点
            start: 1,
            
//终点
            end: 2,
            
//路径
            path:3,
            
//障碍
            barrier: -1
        }

        
//基本配置
        var config = {
            
//网格数目x
            grid_x_num: 30,
            
//网格数目y
            grid_y_num: 20,
            
//障碍数目
            barrier_num: 150,
            
//网格尺寸宽度
            grid_size_width: 20,
            
//网格尺寸高度
            grid_size_height: 20
        }

        
//A*路径算法
        function APathFind(canvas) {
            
//状态数组
            this.aryGridInfo = new Array();
            
//查询任务
            this.aryTask = new Array();
            
//画布设置
            this.sizeX = config.grid_x_num;
            
this.sizeY = config.grid_y_num;
            
this.sizeWidth = config.grid_size_width;
            
this.sizeHeight = config.grid_size_height;
            
this.barrierNum = config.barrier_num;
            
//绘制画板
            this.canvas = canvas;
            
this.canvas.css({ position: 'relative' });
            
//Grid模板
            templeteGrid = $("<div/>").css({
                position: 
'absolute',
                width: 
this.sizeWidth + 'px',
                height: 
this.sizeHeight + 'px',
                borderStyle: 
'solid',
                borderWidth: 
'1px',
                borderColor: 
'#afafaf'
            });
            
            templeteGrid.click(
this.select.bind(this));
            
            
for (var i = 0; i < this.sizeX; i++) {
                
for (var j = 0; j < this.sizeY; j++) {
                    
var left = this.sizeWidth * i;
                    
var top = this.sizeHeight * j;
                    
var jGrid = templeteGrid.clone(true);
                    jGrid.css({ left: left 
+ 'px', top: top + 'px' });
                    jGrid.attr(
'id''grid_' + i + '_' + j).attr('x', i).attr('y', j);
                    
this.canvas.append(jGrid);
                }
            };
            
//初始化
            this.init();
            
//绘制
            this.render();
        };

        
//初始化方法
        APathFind.prototype.init = function() {
            
//开始点
            this.bx = -1;
            
this.by = -1;
            
//当前点
            this.cx = -1;
            
this.cy = -1;
            
//结束点
            this.ex = -1;
            
this.ey = -1;

            
//初始化二维数组
            for (var i = 0; i < this.sizeX; i++) {
                
this.aryGridInfo[i] = new Array();
                
for (var j = 0; j < this.sizeY; j++) {
                    
this.aryGridInfo[i][j] = {
                        status: status.grid,
                        f: Number.MAX_VALUE,
                        g: Number.MAX_VALUE,
                        h: Number.MAX_VALUE,
                        px: 
-1,
                        py: 
-1,
                        close: 
false
                    };
                }
            }
            
//在数据中标注障碍 标记-1
            for (var k = 0; k < this.barrierNum; k++) {
                
var rx = Math.floor(Math.random() * this.sizeX);
                
var ry = Math.floor(Math.random() * this.sizeY);
                
this.aryGridInfo[rx][ry].status = status.barrier;
            }
        }

        
//呈现方法
        APathFind.prototype.render = function() {
            
var grids = this.canvas.children();
            
for (var i = 0; i < grids.length; i++) {
                
var jGrid = $(grids[i]);
                
var x = jGrid.attr("x");
                
var y = jGrid.attr("y");
                
var gridInfo = this.aryGridInfo[x][y];
                
if (gridInfo.status == status.grid) {
                    jGrid.css({ backgroundColor: 
'#efefef' });
                } 
else if (gridInfo.status == status.barrier) {
                    jGrid.css({ backgroundColor: 
'#0000ff' });
                } 
else if (gridInfo.status == status.start) {
                    jGrid.css({ backgroundColor: 
'#ff0000' });
                } 
else if (gridInfo.status == status.end) {
                    jGrid.css({ backgroundColor: 
'#00ff00' });
                } 
else if (gridInfo.status == status.path) {
                    jGrid.css({ backgroundColor: 
'#00ffff' });
                }
            }
        }

        
//选择方法
        APathFind.prototype.select = function(e) {
            
var jGrid = $(e.currentTarget);
            
var x = jGrid.attr("x");
            
var y = jGrid.attr("y");
            
//有效性检测
            if (this.aryGridInfo[x][y].status != status.grid) {
                alert(
"位置选择错误!");
                
return;
            }
            
//状态检测
            if (this.bx == -1 && this.by == -1) {//起点未选择
                this.bx = x;
                
this.by = y;
                
this.aryGridInfo[x][y].status = status.start;
            } 
else if (this.ex == -1 && this.ey == -1) {//终点未选择
                this.ex = x;
                
this.ey = y;
                
this.aryGridInfo[x][y].status = status.end;
                
//路径寻找
                this.find();
            } 
else {
                alert(
"已完成路径寻找,请刷新后再试!");
            }
            
this.render();
        }

        
//路径搜索
        APathFind.prototype.find = function() {

            
this.cx = this.bx;
            
this.cy = this.by;
            
this.cGrid = this.aryGridInfo[this.cx][this.cy];

            
//关闭起点
            this.cGrid.close = true;
            
//距离起点的权重 水平垂直+10 斜角+14
            this.cGrid.g = 0;
            
//距离终点的权重 曼哈顿数 横坐标网格差*10 + 纵坐标网格差 * 10
            this.cGrid.h = Math.abs(this.ey - this.cy) * 10 + Math.abs(this.ex - this.cx) * 10;
            
//整体权重 = 距离起点的权重 + 距离终点的权重
            this.cGrid.f = this.cGrid.g + this.cGrid.h;
            
//是否移动到末尾
            var isMoveEnd = false;

            
try {
                
do {
                    
var cmp = { f: Number.MAX_VALUE, x: -1, y: -1 };

                    
this.cx = parseInt(this.cx);
                    
this.cy = parseInt(this.cy);
                    
//检测左网格
                    var lGrid = this.checkGrid(this.cx - 1this.cy, 10);
                    cmp 
= this.CmpGrid(lGrid, cmp, this.cx - 1this.cy);

                    
//检测右网格
                    var rGrid = this.checkGrid(this.cx + 1this.cy, 10);
                    cmp 
= this.CmpGrid(rGrid, cmp, this.cx + 1this.cy);

                    
//检测上网格
                    var uGrid = this.checkGrid(this.cx, this.cy - 110);
                    cmp 
= this.CmpGrid(uGrid, cmp, this.cx, this.cy - 1);

                    
//检测下网格
                    var dGrid = this.checkGrid(this.cx, this.cy + 110);
                    cmp 
= this.CmpGrid(dGrid, cmp, this.cx, this.cy + 1);

                    
//如果左网格和上网格没有障碍 左上网格
                    if (lGrid != null && uGrid != null) {
                        
this.checkGrid(this.cx - 1this.cy - 114);
                    }
                    
//如果左网格和下网格没有障碍 左下网格
                    if (lGrid != null && dGrid != null) {
                        
this.checkGrid(this.cx - 1this.cy + 114);
                    }
                    
//如果右网格和上网格没有障碍 右上网格
                    if (rGrid != null && uGrid != null) {
                        
this.checkGrid(this.cx + 1this.cy - 114);
                    }
                    
//如果右网格和下网格没有障碍 右下网格
                    if (rGrid != null && dGrid != null) {
                        
this.checkGrid(this.cx + 1this.cy + 114);
                    }

                    
if (cmp.f < Number.MAX_VALUE) {
                        
this.cx = cmp.x;
                        
this.cy = cmp.y;
                    } 
else {
                        
var grid = this.aryTask.pop();
                        
if (grid) {
                            
this.cx = grid.x;
                            
this.cy = grid.y;
                        } 
else {
                            isMoveEnd 
= true;
                        }
                    }
                    
this.cGrid = this.aryGridInfo[this.cx][this.cy];
                    
this.aryGridInfo[this.cx][this.cy].close = true;
                } 
while (isMoveEnd == false);
            } 
catch (e) { var a = 1; };


            
//绘制返回路径
            var eGrid = this.aryGridInfo[this.ex][this.ey];
            
if (eGrid.px != -1 && eGrid.py != -1) {
                
var x = eGrid.px;
                
var y = eGrid.py;
                
while (this.bx != x || this.by != y) {
                    
var grid = this.aryGridInfo[x][y];
                    grid.status 
= status.path;
                    x 
= grid.px;
                    y 
= grid.py;
                }
            } 
else {
                alert(
"没有发现合适路径!");
                
this.ex = -1;
                
this.ey = -1;
            }

//            for (var i = 0; i < this.sizeX; i++) {
//
                for (var j = 0; j < this.sizeY; j++) {
//
                    var grid = this.aryGridInfo[i][j];
//
                    var info = "x:" + i;
//
                    info += " y:" + j;
//
                    info += "<br/>f:";
//
                    info += (grid.f == Number.MAX_VALUE) ? "Max" : grid.f;
//
                    info += "<br/>g:";
//
                    info += (grid.g == Number.MAX_VALUE) ? "Max" : grid.g;
//
                    info += "<br/>h:";
//
                    info += (grid.h == Number.MAX_VALUE) ? "Max" : grid.h;
//
                    info += "<br/>status:" + grid.status;
//
                    info += " px:" + grid.px;
//
                    info += " py:" + grid.py;
//
                    $('#grid_' + i + '_' + j).html(info);
//
                }
//
            }
        }

        APathFind.prototype.checkGrid 
= function(x, y, power) {
            
if (x >= 0 && x < this.sizeX && y >= 0 && y < this.sizeY) {
                
var grid = this.aryGridInfo[x][y];
                
if (grid.status == status.grid) {//空白网格
                    //检测权重是否更低 (距离是否更近)
                    if (this.cGrid.g + power < grid.g) {
                        grid.g 
= this.cGrid.g + power;
                        grid.h 
= Math.abs(this.ex - x) * 10 + Math.abs(this.ey - y) * 10;
                        grid.f 
= grid.g + grid.h;
                        grid.px 
= this.cx;
                        grid.py 
= this.cy;
                        
//如果网格不是关闭的,加入任务列表
                        if (grid.close != true) {
                            
this.aryTask.push({ x: x, y: y });
                        }
                    }
                    
return grid;
                } 
else if (grid.status == status.end) {//遇到终点
                    grid.g = this.cGrid.g + power;
                    grid.h 
= Math.abs(this.ex - x) * 10 + Math.abs(this.ey - y) * 10;
                    grid.f 
= this.cGrid.g + this.cGrid.h;
                    grid.px 
= this.cx;
                    grid.py 
= this.cy;
                    
throw "finish";
                }
            }
            
return null;
        }

        
//比较网格 [寻找周边下一步网格]
        APathFind.prototype.CmpGrid = function(gird, cmp, x, y) {
            
if (gird != null) {
                
if (gird.f < cmp.f && gird.close != true) {
                    cmp.f 
= gird.f;
                    cmp.x 
= x;
                    cmp.y 
= y;
                }
            }
            
return cmp;
        }


        $(document).ready(
function() {
            
//创建画板
            var canvas = $("#canvas");
            
//创建对象
            var pathfind = new APathFind(canvas);

            $(
"#btnInit").click(function() { pathfind.init(); pathfind.render(); });
        });
    
</script>
</head>
<body>
<div>
    
<span>通过鼠标选取一个起点和一个终点</span>
    
<input id="btnInit" type="button" value="刷新" />
</div>

<div id="canvas"></div>
</body>
</html>

 


转载于:https://www.cnblogs.com/mapig/archive/2009/07/06/1517905.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值