【问题标题】:A* Pathfinding in a hexagonal gridA* 六边形网格中的寻路
【发布时间】:2016-10-27 04:56:37
【问题描述】:

谁能给我一个简单的例子,它在 hexagonal 网格(在 JS 中)上实现 A* path-finding algorithm。我已经让它在方形网格上工作,但是我所有让它在六边形网格上工作的尝试都失败了。

这是我的网格的样子:

我使用相同的技术来绘制网格并生成坐标,如topic 所示。

这是网格坐标数据以及开始、结束坐标:

        [0, 0] , [0, 1],  [0, 2],
    [1, 0],  [1, 1],  [1, 2],  [1, 3],
 [2, 0],  [2, 1],  [2, 2],  [2, 3],  [2, 4],
    [3, 0],  [3, 1], [3, 2],  [3, 3], 
        [4, 0], [4, 1],  [4, 2]


start_point: [0,2]
end_point: [4.0]

将曼哈顿距离计算更新为:

var dx = pos1[0] - pos0[0];
    var dy = pos1[1] - pos0[1];

    var dist;
    if ( Math.sign(dx) == Math.sign(dy) ){
        dist = Math.abs (dx + dy);
    }else{
        dist = Math.max(Math.abs(dx), Math.abs(dy))
    }

return dist;

我得到这个结果:

还有我计算最短路径的方式:

if (!Array.prototype.remove) {
    Array.prototype.remove = function(from, to) {
        var rest = this.slice((to || from) + 1 || this.length);
        this.length = from < 0 ? this.length + from : from;
        return this.push.apply(this, rest);
    };
}

var astar = {
    init: function(grid) {
        for(var x = 0; x < grid.length; x++) {
            for(var y = 0; y < grid[x].length; y++) {
                grid[x][y].f = 0;
                grid[x][y].g = 0;
                grid[x][y].h = 0;
				//grid[x][y].content = false;
                grid[x][y].visited = false;
                grid[x][y].closed = false;
                grid[x][y].debug = "";
                grid[x][y].parent = null;
				console.log([grid[x][y].coords[0],grid[x][y].coords[1]])
            }
        }
    },
    search: function(grid, start, end, heuristic) {
        this.init(grid);
        heuristic = heuristic || this.manhattan;

        var openList = [];
		
		//// find the start and end points in the grid ////
		start = grid[start.pos[0]][start.pos[1]];
		end =  grid[end.pos[0]][end.pos[1]];
		
		console.log( start, end )
		
        openList.push(start);
		
        while(openList.length > 0) {
			
            // Grab the lowest f(x) to process next
            var lowInd = 0;
            for(var i=0; i<openList.length; i++) {
                if(openList[i].f < openList[lowInd].f) { lowInd = i; }
            }
            var currentNode = openList[lowInd];

            // End case -- result has been found, return the traced path
            if( currentNode == end ) {
                var curr = currentNode;
                var ret = [];
                while(curr.parent) {
                    ret.push(curr);
                    curr = curr.parent;
                }
                return ret.reverse();
            }

            // Normal case -- move currentNode from open to closed, process each of its neighbors
            openList.remove( lowInd );
            currentNode.closed = true;

            var neighbors = this.neighbors(grid, currentNode);
            for(var i=0; i<neighbors.length;i++) {
                var neighbor = neighbors[i];

                if( neighbor.closed || neighbor.content == 2 ) { // not a valid node to process, skip to next neighbor
                    continue;
                }

                // g score is the shortest distance from start to current node, we need to check if
                //   the path we have arrived at this neighbor is the shortest one we have seen yet
                var gScore = currentNode.g + 1; // 1 is the distance from a node to it's neighbor
                var gScoreIsBest = false;

                if(!neighbor.visited) {
                    // This the the first time we have arrived at this node, it must be the best
                    // Also, we need to take the h (heuristic) score since we haven't done so yet
                    gScoreIsBest = true;
                    neighbor.h = heuristic(neighbor.coords, end.coords);
                    neighbor.visited = true;
                    openList.push(neighbor);
                }
                else if(gScore < neighbor.g) {
                    // We have already seen the node, but last time it had a worse g (distance from start)
                    gScoreIsBest = true;
                }

                if(gScoreIsBest) {
                    // Found an optimal (so far) path to this node.  Store info on how we got here and just how good it really is. ////
                    neighbor.parent = currentNode;
                    neighbor.g = gScore;
                    neighbor.f = neighbor.g + neighbor.h;
                    neighbor.debug = "F: " + neighbor.f + "<br />G: " + neighbor.g + "<br />H: " + neighbor.h;
                }
            }
        }

        // No result was found -- empty array signifies failure to find path
        return [];
    },
    manhattan: function(pos0, pos1) { //// heuristics : use manhattan distances  ////
        var dx = pos1[0] - pos0[0];
        var dy = pos1[1] - pos0[1];
		
        return  Math.abs (dx + dy);
    },
    neighbors: function(grid, node) {
        var ret = [];
        var x = node.coords[0];
        var y = node.coords[1];
		
        if(grid[x-1] && grid[x-1][y] ) {
            ret.push(grid[x-1][y]);
        }
        if( grid[x+1] && grid[x+1][y] ) {
            ret.push(grid[x+1][y]);
        }
        if( grid[x][y-1] && grid[x][y-1] ) {
            ret.push(grid[x][y-1]);
        }
        if( grid[x][y+1] && grid[x][y+1] ) {
            ret.push(grid[x][y+1]);
        }
        return ret;
    }
};

尝试在互联网上四处寻找一些好的示例或文档,但实际上找不到任何有用的东西。

【问题讨论】:

  • 需要澄清一下。你有一个你正在工作的启发式方法吗?你有现有的六边形网格吗?
  • stackoverflow.com/questions/20734438/… 这就是我的网格的样子,只是我把整个 Java 代码变成了 JS。如您所见,网格使用的是轴坐标系,这是我用来计算距离的启发式var dx = Math.abs (pos1.x - pos0.x); var dy = Math.abs (pos1.y - pos0.y); return dx + dy;
  • 这个问题中描述的备用坐标系可能会有所帮助,问题本身也会有所帮助:stackoverflow.com/questions/5084801/… 因为曼哈顿距离将是一个很好的第一个启发式方法。 *编辑:它是同一个坐标系,只是旋转了。
  • 询问示例(以及一般链接)的问题在 SO 上是题外话。赏金不会改变这一点。请发布您的代码,尤其是尝试失败的代码,告诉我们什么不起作用以及您期望什么。对于这样一个具体的问题,我们可以为您提供帮助。
  • 我已经更新了我的问题并提供了更多细节,如果还不够清楚,请告诉我。

标签: javascript html canvas path-finding a-star


【解决方案1】:

问题在于您的 neighbors 方法:虽然六边形有六个邻居 (6),但您只需将四 (4) 推到 ret 上。下图突出了这个问题。浅灰色的十六进制表示当前节点(即neighbor)。绿色六边形添加到ret,但红色六边形没有。

要解决此问题,请将以下两 (2) 个案例添加到您的 neighbors 方法中:

    if( grid[x+1][y-1] && grid[x+1][y-1] ) {
        ret.push(grid[x][y-1]);
    }
    if( grid[x-1][y+1] && grid[x-1][y+1] ) {
        ret.push(grid[x][y+1]);
    }

关于您更新的manhattan 方法:是正确的。下图使用颜色显示从当前中心六角形(红色 [0:0] 处)到其他每一个六角形的距离。例如,橙色的六角牌距离红色的一 (1) 步。黄色是从红色移动两 (2) 步。等等。

您可能会注意到这种模式:如果 x 和 y 坐标具有相同的符号,则距离等于最大坐标的大小。否则,距离是它们的绝对值之和。这正是您在 更新 manhattan 方法中计算距离的方式。所以你很好。

关于一般启发式搜索:这里有一个简单的方法来检查次优解决方案是由于启发式实现中的错误还是由于算法实现中的错误造成的。只需对所有值使用启发式值零 (0),即使用平凡的启发式。如果在使用平凡启发式算法时,路径不是最优的,那么您就知道这不是启发式问题——而是算法问题。

【讨论】:

  • 不仅是一个好的答案,也是测试启发式的好建议。
  • 为什么y坐标-在顶部,+在底部?
  • @JesseMoreland 简单的回答:它与 OP 的帖子相匹配。更多详细信息:按照计算机图形学的惯例,屏幕的左上角标记为 (0,0),x 和 y 坐标随着您向屏幕右下角移动而增加。
【解决方案2】:

正如这里有人已经提到的那样,我生成网格的方式以及坐标都不正确。

我又阅读了 Amit Patel 的实现 guide,我用他的方式生成了网格以及包括坐标在内的坐标系。转换。

generate: function(){
        var n_hex = null; 
        var row = 0, col = 0;
        for (var q = -this.grid_r; q <= this.grid_r; q++) {
            var r1 = Math.max(-this.grid_r, -q - this.grid_r);
            var r2 = Math.min(this.grid_r, -q + this.grid_r);
            col = q + this.grid_r;
            this.hexes[ col ] = [];
            for (var r = r1; r <= r2; r++) {
                row = r - r1;
                n_hex = new Hex( [q, r], [col, row], this.layout );
                this.hexes[ col ][ row ] = n_hex;
            }
        }
    },

当我开始使用立方体坐标时,我在 a* 寻路算法中唯一需要改变的是距离计算:

return Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y), Math.abs(a.z - b.z))

现在寻路功能适用于“尖”和“扁平”布局:

【讨论】:

    【解决方案3】:

    这是一个已解决的问题,有很多文献支持它。我所知道的最好的资源是 Red Blob Games:https://www.redblobgames.com/grids/hexagons/

    简而言之,最可能的原因是您选择了错误的坐标系。使用 Cube 坐标系来实现 A* 算法非常简单。请参阅上述链接上的现场演示。

    如果您真的想使用其他系统,请在需要时进行转换。

    【讨论】:

      【解决方案4】:

      “经典”寻路算法的工作原理如下:

      • 用零初始化所有单元格。
      • 从起点开始,并用数字 1 标记该点。
      • 循环从 n = 1 开始:
      • 取出所有编号为 n 的单元格,并用编号 (n+1) 标记所有包含零的相邻单元格。如果这些相邻单元中的任何一个是出口,则离开循环。如果没有找到编号为零的相邻单元格,则没有退出路径。
      • 递增 n 并转到循环

      如果找到退出,则:

      • 在出口处开始循环:
      • 搜索编号为 n 的相邻单元格并记住该单元格。
      • 递减 n 并转到循环

      所有记住的单元格构建结果路径(以相反的顺序)。

      欢呼

      亨内斯

      【讨论】:

      • 我目前在方形网格中使用的 astar pathfiinding 运行良好,但是当在六边形网格上使用相同的模式时,它给了我一些奇怪的结果(请参阅上面的更新帖子)。另外我不确定是使用网格中的 [row, col] 位置还是轴坐标来计算路径
      • 我已经在矩形 2D、3D 和三角形(而不是六边形)形状中尝试过这个算法,它总是能找到最短路径。
      • 我确信它可以工作,我的也可以在正方形/矩形网格中工作。但不是六边形。我没有否决你的答案,即使它不能解决我的问题。
      • 这是广度优先搜索,应该在十六进制网格中工作(注意:最初的问题是关于 A* 搜索)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-27
      相关资源
      最近更新 更多