等轴测图的精确 A* 搜索启发式?

发布于 2024-07-30 18:26:10 字数 831 浏览 4 评论 0原文

我已经编写了 A* 搜索算法的实现。 问题是我目前使用的启发式方法仅适用于方形网格。 由于我的地图是等轴测图,启发式方法并未考虑地图的实际布局以及单元格之间的距离。

更新:经过广泛的记录和分析(理解为花费大量时间试图找出平庸),我得出的结论是我当前的启发式效果非常好,有一点例外:真实直线和对角线运动的最终结果是相反的。

inline int Pathfinder::calculateDistanceEstimate(const CellCoord& coord) const
{
    int diagonal = std::min(abs(coord.x-goal->position.x), abs(coord.y-goal->position.y));
    int straight = (abs(coord.x-goal->position.x) + abs(coord.y-goal->position.y));
    return 14 * diagonal + 10 * (straight - 2 * diagonal);
}

这意味着,在等距地图上,直线移动的成本实际上是对角线移动的 sqrt(2) 倍,计算结果是对角线移动的成本。 问题是:如何修改当前的启发式,以便为等距布局生成正确的结果? 简单地用straight替换diagonal,反之亦然将不起作用

地图布局

I have written an implementation of the A* search algorithm. The problem is that the heuristic I'm currently using only works accurately on square grids. As my map is isometric, the heuristic doesn't take into account actual the layout of the map and thus, the distance between cells.

Update: After extensive logging and analysis (read as spending lots of time trying to figure out a banality), I have come to the conclusion that my current heuristic works quite well, with one little exception: the end result is reversed for real straight and diagonal movement.

inline int Pathfinder::calculateDistanceEstimate(const CellCoord& coord) const
{
    int diagonal = std::min(abs(coord.x-goal->position.x), abs(coord.y-goal->position.y));
    int straight = (abs(coord.x-goal->position.x) + abs(coord.y-goal->position.y));
    return 14 * diagonal + 10 * (straight - 2 * diagonal);
}

This means that a straight move, which really costs sqrt(2) times more than a diagonal move on an isometric map, is calculated to be the that of a diagonal one. The question is: how can I modify my current heuristic so it will produce correct results for an isometric layout? Simply replacing diagonal with straight and vice versa will not work.

Map layout

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

莫相离 2024-08-06 18:26:10

要尝试的一件事是将所有计算从等距坐标转换为方形网格坐标集。

假设 0,0 仍然是地图的根。 0,1 保持不变,1,2 变为 0,2; 1,3 变为 0,3; 2,3 变为 1,4; 3,3 变为 2,5; 0,2 变为-1,1; 等等。这会让你回到方形网格,这样坐标和启发式应该再次起作用。

Y 坐标变为 Y+sourceX 偏移量(3,3 位于 x=2 处;因此变为 2,5); 事实证明,以数学方式找到 sourceX 本身更加困难。

[意识流; 忽略] Y=0 处的等距坐标对于源 X 来说是准确的。因此,要计算源 X,您需要“向左/向上移动 Y 次”,这应该会产生 Y/2 的变化; 向下舍入,在 X 坐标中......粗略地表明方形坐标为:

sourceX = X - Y/2
sourceY = Y + sourceX

其中 sourceX 和 sourceY 是普通方形网格中的坐标; Y/2 是整数算术/向下舍入。

因此,从理论上讲,这将变为:

double DistanceToEnd(Point at, Point end)
{
    Point squareStart = squarify(at);
    Point squareEnd = squarify(end);
    int dx=squareStart.X-squareEnd.X;
    int dy=squareStart.Y-squareEnd.Y;
    return Math.Sqrt(dx*dx+dy*dy);
}
Point squarify(Point p1)
{
     return new Point(p1.X-p1.Y/2, p1.Y+(p1.X-p1.Y/2));
}

基于新的问题进行更新

假设您正在尝试获得距离(3,2; 3,3) < (距离(2,3;3,3)=距离(3,1;3,3)); 以下内容应该有效:(从 C# 翻译;不保证不存在拼写错误)

inline int Pathfinder::calculateDistanceEstimate(const CellCoord& coord) const
{
    int cx=coord.x - coord.y/2;
    int cy=coord.y + cx;
    int gx=goal->position.x - goal->position.y/2;
    int gy=goal->position.y + gx;
    int diagonal = std::min(abs(cx-gx), abs(cy-gy));
    int straight = (abs(cx-gx) + abs(cy-gy));
    return 14 * diagonal + 10 * (straight - 2 * diagonal);
}

编辑:再次修复了可怕的拼写错误。

One thing to try would be converting from isometric coordinates to a square grid coordinate set for all calculations.

Say that 0,0 stays the root of the map. 0,1 stays the same, 1,2 becomes 0,2; 1,3 becomes 0,3; 2,3 becomes 1,4; 3,3 becomes 2,5; 0,2 becomes -1,1; etc. This puts you back into a square grid such that the coordinates and heuristics should work again.

Y coordinate becomes Y+sourceX offset (3,3 is at x=2; so becomes 2,5); finding sourceX mathmatically is proving itself more difficult.

[Stream of consciousness; ignore] Isometric coordinates at Y=0 are accurate for source X. So, to calculate source X you need to 'move left/up Y times' which should net a change of Y/2; rounded down, in the X coordinate.... roughly suggesting that the square coordinates would be:

sourceX = X - Y/2
sourceY = Y + sourceX

Where sourceX and sourceY are the coordinates in a normal square grid; and Y/2 is integer arithmetic/rounded down.

So, in theory, this becomes:

double DistanceToEnd(Point at, Point end)
{
    Point squareStart = squarify(at);
    Point squareEnd = squarify(end);
    int dx=squareStart.X-squareEnd.X;
    int dy=squareStart.Y-squareEnd.Y;
    return Math.Sqrt(dx*dx+dy*dy);
}
Point squarify(Point p1)
{
     return new Point(p1.X-p1.Y/2, p1.Y+(p1.X-p1.Y/2));
}

Update based on new bits of question:

Assuming that you are trying to get distance(3,2; 3,3) < (distance(2,3; 3,3) = distance(3,1; 3,3)); the following should work: (translated from C#; typos not guaranteed to be non present)

inline int Pathfinder::calculateDistanceEstimate(const CellCoord& coord) const
{
    int cx=coord.x - coord.y/2;
    int cy=coord.y + cx;
    int gx=goal->position.x - goal->position.y/2;
    int gy=goal->position.y + gx;
    int diagonal = std::min(abs(cx-gx), abs(cy-gy));
    int straight = (abs(cx-gx) + abs(cy-gy));
    return 14 * diagonal + 10 * (straight - 2 * diagonal);
}

EDIT: Fixed horrible typo.... again.

小糖芽 2024-08-06 18:26:10

Amit 此处计算“六边形的曼哈顿距离”。 它是 C++ 代码,我不能保证它,但 Amit 是我以前听说过的游戏开发名字。

六边形的曼哈顿距离应该适合适当的启发式。

编辑:反转超链接的语法,哎呀。

Amit here computes the "manhattan distances for hexagons". Its C++ code, and I can't vouch for it, but Amit is a name I've heard before for game development.

The manhattan distance for hexagons should be suitable for a proper heuristic.

Edit: reversed the syntax for hyperlinking, whoops.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文