【问题标题】:Java - A* Search IssueJava - A* 搜索问题
【发布时间】:2015-04-03 17:49:53
【问题描述】:

问题已解决。

我在一个简单的基于网格的游戏中实现了一个 A* 搜索算法用于寻路。这是我第一次这样做,并且大多数时候实施效果很好。但是,有时(尽管很少)当有可用路径时它会卡住。当然,它被卡住的事实使它不适合目的。我假设我的实现中遗漏了一些东西。

我已经找了几天这个问题,但无济于事。我的最后期限快到了,还有很多事情要做,我不想再浪费时间来修复这个错误了。

编辑:我创建了a quick video 来演示这个问题,这样你就可以准确地看到发生了什么。它包括字幕。
编辑:getPath 方法:

/**
 * @param currentPosition - the vector the avatar currently occupies.
 * @param targetPosition - the vector the avatar is aiming to reach.
 * @param levelMap - a clip map of the level.
 * 
 * @return an {@code ArrayList} of {@link ACTIONS} that the avatar can follow to reach its destination.
 **/
public static ActionPath getPath(Vector2d currentPosition, Vector2d targetPosition, LevelMap levelMap) {
    openTiles = new ArrayList<AStarTile>();
    closedTiles = new ArrayList<AStarTile>();
    targetMet = false;

    AStarTile originTile = AStarTile.fromVector(currentPosition, levelMap.getBlockSize()),
             targetTile = AStarTile.fromVector(targetPosition, levelMap.getBlockSize()),
             currentTile = null,
             parentTile = null;

    ActionPath actionPath = new ActionPath(targetTile);

    if (originTile.equals(targetTile)) {
        targetMet = true;
        return null;
    }

    GVGLogger.logInfo("Creating path from tile " + originTile + " to tile " + targetTile + " (" + currentPosition + " to " + targetPosition + ").");

    /*
     * Start the search.
     */
    openTile(originTile);
    originTile.calculateGeneration();// The origin tile will always be generation 0.
    closeTile(originTile);

    parentTile = originTile;

    while(!targetMet) {
        for (int i = 0; i != 4; i++) {
            currentTile = parentTile.move(i);// Checks an adjacent tile - up, down, left, and right respectively

            if (levelMap.inBounds(currentTile) && levelMap.isAccessible(currentTile) && !isClosed(currentTile)) {
                if (isOpen(currentTile)) {
                    // Check to see if this path to this tile is a better one.
                    currentTile = getOpen(currentTile);

                    if (currentTile.getGeneration() > parentTile.getGeneration() + 1) {
                        // The open version's generation is higher than this version's generation - it's a better path
                        currentTile.setParentTile(parentTile);
                        currentTile.calculateGeneration();
                        currentTile.calculateFinalScore();
                    }
                }
                else {
                    currentTile.setParentTile(parentTile);
                    currentTile.setHeuristic(currentTile.distanceSquared(targetTile));
                    currentTile.calculateGeneration();
                    currentTile.calculateFinalScore();
                    openTile(currentTile);
                }
            }
        }

        if (openTiles.size() > 0) {
            parentTile = getBestOption();
            closeTile(parentTile);

            if (parentTile.equals(targetTile)) {
                targetMet = true;
            }
        }
        else {
            GVGLogger.logWarning("Target unreachable!");
            return null;
        }
    }

    //Convert the path of tiles into ACTIONS that the avatar can take to reach it.

    for (int i = 0; i != closedTiles.size(); i++) {
        Vector2i difference = getDifference(closedTiles.get(i), (i != closedTiles.size() - 1 ? closedTiles.get(i + 1) : targetTile));

        if (difference.equals(1, 0)) {
            actionPath.add(ACTIONS.ACTION_LEFT);
        }
        else if (difference.equals(-1, 0)) {
            actionPath.add(ACTIONS.ACTION_RIGHT);
        }
        else if (difference.equals(0, -1)) {
            actionPath.add(ACTIONS.ACTION_DOWN);
        }
        else if (difference.equals(0, 1)) {
            actionPath.add(ACTIONS.ACTION_UP);
        }
        else if (difference.equals(0, 0)) {
            return actionPath;
        }
        else {
            GVGLogger.logWarning("Error in path-finding - found a difference of " + difference + "!");
        }
    }
    return null;
}

private static Vector2i getDifference(AStarTile tileA, AStarTile tileB) {
    return new Vector2i(tileA.getX() - tileB.getX(), tileA.getY() - tileB.getY());
}

public static boolean targetMet() {
    return targetMet;
}

private static void openTile(AStarTile toOpen) {
    if (isClosed(toOpen)) {
        closedTiles.remove(getOpen(toOpen));
    }
    if (!isOpen(toOpen)) {
        openTiles.add(toOpen);
    }
}

private static void closeTile(AStarTile toClose) {
    if (isOpen(toClose)) {
        openTiles.remove(getOpen(toClose));
    }
    if (!isClosed(toClose)) {
        closedTiles.add(toClose);
    }
}

private static boolean isClosed(AStarTile toCheck) {
    return getClosed(toCheck) != null;
}

private static boolean isOpen(AStarTile toCheck) {
    return getOpen(toCheck) != null;
}

/**
 * @return the open tile with the lowest 'final' score.
 **/
private static AStarTile getBestOption() {
    try {
        Collections.sort(openTiles);
        return openTiles.get(0);
    }
    catch(Exception e) {
    }
    return null;
}

private static AStarTile getClosed(AStarTile t) {
    for (AStarTile p : closedTiles) {
        if (p.equals(t)) {
            return t;
        }
    }
    return null;
}

private static AStarTile getOpen(AStarTile t) {
    for (AStarTile p : openTiles) {
        if (p.equals(t)) {
            return t;
        }
    }
    return null;
}

}

此方法返回一个“动作”列表,化身可以用来丰富目标图块。如果您想查看任何其他方法,请询问。

我在阅读了在 policyalmanac.org 上找到的 Patrick Lester 的解释/教程后编写了这个实现(“A* Pathfinding for Beginners”)。

如果您能浏览一下我的实现并指出任何问题,我将不胜感激,尤其是如果您对 A* 搜索算法有经验。我认为代码是自我记录的,但如有必要,请随时要求我详细说明。

感谢您的宝贵时间。

【问题讨论】:

  • 请不要在外部资源上发布您的代码。而是直接在此处发布与问题相关的内容。
  • @tnw 代码没有按预期工作,因此它属于代码审查。
  • 对,吸取教训。我只是认为这样会更整洁,以避免用代码填充问题。我还认为我最好将所有这些都添加到有问题的方法中,以防有人希望检查其他方法。

标签: java algorithm search a-star


【解决方案1】:

有几件事看起来很可疑:

一个

currentTile.getGeneration() > parentTile.getGeneration() + 1

这应该是 >= 而不是 >?

两个

currentTile.setHeuristic(currentTile.distanceSquared(targetTile));

平方距离不是一种可接受的启发式方法,因为它会高估距离。尝试使用曼哈顿距离(或简单地将启发式设置为 0 以实现效率较低但更可靠的搜索)。

【讨论】:

  • 非常感谢您的回复。它是 > 因为它是 >=,备用路径可能是相同的长度,而我正在检查 更好 路径。啊,好的,我看看能不能调整一下。然而,将启发式设置为 0 完全破坏了它。
【解决方案2】:

我已经对其进行了排序 - 我在关闭磁贴时添加了对生成的检查。现在它会稍微回溯而不是卡住。

解决方案:

    if (!closedTiles.isEmpty() && toClose.getGeneration() != lastClosed().getGeneration() + 1) {
        openTiles.remove(getOpen(toClose));
        return;
    }

我很高兴我已经修复了它。非常感谢任何花时间阅读和/或回答问题的人! :)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-12
    • 2011-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-09
    • 1970-01-01
    相关资源
    最近更新 更多