A-Star Pathfinding выбирает плохие путевые точки


РЕШЕНО: извините. Я неправильно реконструировал путь. Я думал, что в CloseSet есть все путевые точки только от начала до конца, но у него есть и некоторые другие путевые точки. Я не понял концепции. Теперь все работает!


У меня все еще возникают проблемы с A*.

Мой персонаж находит свой путь, но иногда, в зависимости от того, где я нажимаю на карту, алгоритм находит кратчайший путь или путь, но со многими узлами, которые не следует выбирать.

Я пытался следовать реализации Википедии и A* Pathfinding for Beginner, но они дают тот же результат. Не знаю, эвристика это или сам алгоритм, но что-то не так.

А это пример проблемы с нажатием двух разных узлов: http://i.imgur.com/gtgxi.jpg

Вот класс Pathfind:

import java.util.ArrayList;
import java.util.Collections;
import java.util.TreeSet;

public class Pathfind {
public Pathfind(){

}

public ArrayList<Node> findPath(Node start, Node end, ArrayList<Node> nodes){

    ArrayList<Node> openSet = new ArrayList<Node>();
    ArrayList<Node> closedSet = new ArrayList<Node>();

    Node current;

    openSet.add(start);

    while(openSet.size() > 0){

        current = openSet.get(0);

        current.setH_cost(ManhattanDistance(current, end));

        if(start == end) return null;           
        else if(closedSet.contains(end)){
            System.out.println("Path found!");
            return closedSet;
        }

        openSet.remove(current);
        closedSet.add(current);

        for(Node n : current.getNeigbours()){           
            if(!closedSet.contains(n)){     
                if(!openSet.contains(n) || (n.getG_cost() < (current.getG_cost()+10))){ 
                    n.setParent(current);
                    n.setG_cost(current.getG_cost()+10);
                    n.setH_cost(ManhattanDistance(n, end));

                        if(!openSet.contains(n))
                            openSet.add(n);

                    Collections.sort(openSet);
                }
            }
        }
    }       

    return null;
}

private int ManhattanDistance(Node start, Node end){
    int cost = start.getPenalty();

    int fromX = start.x, fromY = start.y;
    int toX = end.x, toY = end.y;

    return cost * (Math.abs(fromX - toX) + Math.abs(fromY - toY));
}

}


person Jh62    schedule 14.09.2012    source источник
comment
у вас есть 5 вложенных операторов if - я бы рекомендовал использовать или условные или поместить их все в метод, который возвращает логическое значение.   -  person tehdoommarine    schedule 14.09.2012
comment
Пожалуйста, добавьте больше информации о карте. В зависимости от типа карты различная метрика расстояния может быть или не быть правильной для использования в качестве эвристики.   -  person Abhinav Sarkar    schedule 14.09.2012
comment
Просто комментарий - я считаю, что выбор closedSet как ArrayList неэффективен. каждая contains() операция равна O(n) (где n — количество закрытых узлов). Вы должны использовать Set для лучшей производительности - HashSet - разумный выбор, и если вы хотите сохранить порядок вставки - вы должны использовать LinkedHashSet. (Обратите внимание, что вам придется переопределить методы equals() и hashCode() метода Node)   -  person amit    schedule 14.09.2012


Ответы (2)


Я считаю, что ошибка связана с условием:

if(n.getCost() < current.getCost()){

Вы не должны препятствовать продвижению вперед, если стоимость (g(node)+h(node)) уменьшается по сравнению с текущей. Посмотрите на этот встречный пример: (S — источник, T — цель)

_________
|S |x1|x2|
----------
|x3|x4|x5|
---------
|x6|x7|T |
----------

Теперь предположим, что вы находитесь в точке S, вы еще не двигались, поэтому g(S) =0, и согласно эвристике манхэттенского расстояния, h(S) = 4, так что вы получаете f(S)=4

Теперь взгляните на x1,x3: если вы сделаете по одному шагу к каждому, у них будет g(x1)=g(x3)=1, и у обоих будет h(x1)=h(x3)=3 при одной и той же эвристике. Это приведет к f(x1)=f(x3)=4 - и ваше условие if не приведет к тому, что никто не "откроется", поэтому, как только вы закончите итерацию на S - вы ничего не нажмете на open - и ваш поиск прекратится.


В качестве примечания:
я считаю, что выбор closedSet в качестве ArrayList неэффективен. каждая contains() операция равна O(n) (где n — количество закрытых узлов). Вы должны использовать Set для лучшей производительности — HashSet — разумный выбор, и если вы хотите сохранить порядок вставки — вы должны использовать LinkedHashSet. (Обратите внимание, что вам придется переопределить методы equals() и hashCode() метода Node)

person amit    schedule 14.09.2012
comment
Я не беспокоюсь о скорости прямо сейчас, это просто тест. Сначала мне нужно заставить его работать. Спасибо за совет, в любом случае. Я что-то читал об этом. - person Jh62; 15.09.2012
comment
@ user1656222: Основная проблема в ответе остается в силе. Упомянутое условие неверно, поэтому алгоритм не работает. Проблема скорости - это просто примечание, вы можете пока игнорировать ее и сосредоточиться только на другой проблеме, указанной в ответе. - person amit; 19.09.2012

Ваши юниты ходят только вверх/вниз/влево/вправо, или они могут двигаться и по диагонали?

Единственное требование к эвристике A* заключается в том, что она должна быть допустимой и никогда не должна превышать — оценить фактическую длину пути. Если ваши юниты могут идти по диагонали, то манхэттен-расстояние будет переоценивать длину пути, и, таким образом, A* не гарантирует работу.

person BlueRaja - Danny Pflughoeft    schedule 14.09.2012
comment
Но почему путь не найден? A* по-прежнему полный без допустимой эвристической функции, он просто неоптимален, поэтому я подозреваю, что он нашел бы путь, но не самый короткий. Кроме того, евклидово расстояние по-прежнему недопустимо, если вы разрешаете диагонали - при условии, что диагональ по-прежнему считается одним шагом (для расстояния в один h = sqrt (2), а h * = 1) - person amit; 14.09.2012
comment
МОЙ юнит не движется по диагонали. Я загрузил новое изображение, показывающее путь, выбранный алгоритмом, соседние узлы и выбранный узел (цель). - person Jh62; 15.09.2012