Java AStar pathfinding - создание одного пути в нескольких сегментах

1

У меня есть сетка, в которой я использую звезду, на которой можно успешно пересекать с одного квадрата на другой, обойдя препятствия. Тем не менее, вычисление долгого времени может вызвать определенную задержку, поэтому я подумал, вместо того, чтобы вычислять 20 длинный путь по сетке сразу, почему бы не создать первые 5 шагов, а затем в этот момент создать следующие 5 шагов и так далее.

Мой вопрос заключается в том, что вы не ожидаете, что агент по-прежнему будет следовать одному и тому же маршруту, независимо от того, вычисляет ли он 5 шагов за раз? На данный момент, если я изменю его на 5 шагов за раз, он начнет принимать разные пути, и он быстро сработает (проблема в этом лежит где-то еще, это больше зависит от пути, который мне нужно выяснить).

Ниже приведен код поиска пути, который я нашел в Интернете, который я изменил. Я не уверен, насколько это будет полезно, и есть другие части кода, которые, очевидно, работают с ним, что, по моему мнению, было бы слишком много для включения. Важно отметить, что прокомментированная строка "//|| shortestPath.getWayPointPath(). Size()> = 5" для того, чтобы судить, был ли создан список.

Пример ниже. Обычно 0 направляется прямо к стене и вокруг нее, но когда я пытаюсь изменить свой путь, в любом случае, то 0 будет просто идти прямо к нижней части карты, затем вправо. Для любой карты кажется, что она хочет взять почти самый длинный путь к цели, и я не могу понять, почему, есть ли у кого-нибудь идеи, что я могу делать неправильно или лучше поступить по этому поводу?

xxxxxxxxxxxxxxxxx
0    x      1
     x
     x
     x

Мой код для сегмента поиска пути ниже

        if (shortestPath == null) {

         openLocations.add(playerLocation);

            // While the goal has not been found yet
            while (openLocations.size() != 0 || pathFound != true) {
                // get the first node from the open list
                Node current = openLocations.get(0);
                shortestPath = reconstructPath(current);

                // check if current node is the goal node
                if (current.getX() == goalLocation.getX()
                        && current.getY() == goalLocation.getY()
                        //|| shortestPath.getWayPointPath().size() > GameInfo.getPathLength() + GameInfo.getPathCounter()
                        //|| shortestPath.getWayPointPath().size() >= 5
                        ) {
                    shortestPath = reconstructPath(current);
                    pathFound = true;

                    for(Node node: shortestPath.getWayPointPath())
                     totalClosedLocations.add(node);

                    // path has been found
                    break;
                }

                // move current node to the already searched (closed) list
                openLocations.remove(current);
                closedLocations.add(current);

                // set the current nodes neighbours
                current = setNeighbours(current);

                // Now it time to go through all of the current nodes
                // neighbours and see if they should be the next step
                for (Node neighbor : current.getNeighborList()) {
                    boolean neighborIsBetter;

                    // if we have already searched this Node, don't bother and
                    // continue to the next one
                    if (closedLocations.contains(neighbor)) {
                        continue;
                    }

                    boolean found = false;
                    for (Node neighbournode : closedLocations) {
                        if (neighbournode.getX() == neighbor.getX()
                                && neighbournode.getY() == neighbor.getY()) {
                            found = true;
                            continue;
                        }
                    }

                    if (found)
                        continue;

                    Node movable = new Node(neighbor.getX(), neighbor.getY(), 
                            neighbor.getCategory(), neighbor.getItype(), neighbor.getId());

                    if (grid[movable.getX()][movable.getY()].size() > 0) {
                        // check to make sure that the square is not of category
                        // 4(immovable object) or category 3(enemy)
                        if ((grid[movable.getX()][movable.getY()].get(0).category == 4 && grid[movable
                                .getX()][movable.getY()].get(0).itype == 0)
                                && grid[movable.getX()][movable.getY()].get(0).obsID != goalLocation.getId()
                                ) {
                            // You cannot move on this square
                            neighbor.setMoveable(false);
                        } else {
                            // You can move on this square. Set parent location
                            // as the players current position.
                            movable.setParent(playerLocation);
                        }
                    }

                    // also just continue if the neighbor is an obstacle
                    if (neighbor.getMoveable()) {

                        // calculate how long the path is if we choose this
                        // neighbor
                        // as the next step in the path
                        float neighborDistanceFromStart = (current
                                .getDistanceFromStart() + getDistanceBetween(
                                current, neighbor));

                        // add neighbor to the open list if it is not there
                        if (!openLocations.contains(neighbor)) {
                            openLocations.add(neighbor);
                            neighborIsBetter = true;
                            // if neighbor is closer to start it could also be
                            // better
                        } else if (neighborDistanceFromStart < current
                                .getDistanceFromStart()) {
                            neighborIsBetter = true;
                        } else {
                            neighborIsBetter = false;
                        }
                        // set neighbors parameters if it is better
                        if (neighborIsBetter) {
                            neighbor.setParent(current);
                            neighbor.setDistanceFromStart(neighborDistanceFromStart);
                            neighbor.setHeuristicDistanceFromGoal(heuristicStar
                                    .getEstimatedDistanceToGoal(
                                            neighbor.getX(), neighbor.getY(),
                                            goalLocation.getX(),
                                            goalLocation.getY()));
                        }
                    }

                }
            }

            System.out.println("====================");
        }
Теги:
path-finding
a-star

1 ответ

0

Причина отсутствия полного пути вместо вычисления нескольких этапов пути независимо состоит в том, чтобы гарантировать оптимальность решения. Кроме того, я считаю, что, получив путь длиной ~ 200 клеток, не должно возникать проблем с производительностью. Возможно, что-то не так с вашим кодом. Я думаю, вам будет проще использовать библиотеку поиска, которая обеспечивает реализацию для A *. Я рекомендую вам взглянуть на библиотеку Хипстера. Структура библиотеки очень дружелюбна и отделяет различные компоненты от основной логики алгоритма, что приводит к очень чистому, модульному и масштабируемому коду. Вот пример кода для решения 2D-планирования пути с помощью Hipster:

//HERE YOU DEFINE THE SEARCH PROBLEM
// The maze is a 2D map, where each tile defined by 2D coordinates x and y
// can be empty or occupied by an obstacle. We have to define de transition
// function that tells the algorithm which are the available movements from
// a concrete tile point.
SearchProblem p = ProblemBuilder.create()
   .initialState(origin)
   .defineProblemWithoutActions()
   .useTransitionFunction(new StateTransitionFunction<Point>() {
      @Override
      public Iterable<Point> successorsOf(Point state) {
         // The transition function returns a collection of transitions.
         // A transition is basically a class Transition with two attributes:
         // source point (from) and destination point (to). Our source point
         // is the current point argument. We have to compute which are the
         // available movements (destination points) from the current point.
         // Class Maze has a helper method that tell us the empty points
         // (where we can move) available:
         //TODO: FILL WITH YOUR CODE GENERATING THE NEIGHBORS, FILTERING
         //THOSE WHICH ARE NOT ACCESIBLE DUE TO OBSTACLES IN YOUR MAP
        return [...]
      }
   })
   .useCostFunction(new CostFunction<Void, Point, Double>() {
      // We know now how to move (transitions) from each tile. We need to define the cost
      // of each movement. A diagonal movement (for example, from (0,0) to (1,1)) is longer
      // than a top/down/left/right movement. Although this is straightforward, if you don't
      // know why, read this http://www.policyalmanac.org/games/aStarTutorial.htm.
      // For this purpose, we define a CostFunction that computes the cost of each movement.
      // The CostFunction is an interface with two generic types: S - the state, and T - the cost
      // type. We use Points as states in this problem, and for example doubles to compute the distances:
      @Override
      public Double evaluate(Transition<Void, Point> transition) {
         Point source = transition.getFromState();
         Point destination = transition.getState();
         // The distance from the source to de destination is the euclidean
         // distance between these two points http://en.wikipedia.org/wiki/Euclidean_distance
         return source.distance(destination);
      }
   })
   .build();

//HERE YOU INSTANTIATE THE ALGORITHM AND EXECUTE THE SEARCH
//MazeSearch.printSearch(Hipster.createAStar(p).iterator(), maze);
System.out.println(Hipster.createAStar(p).search(goal));

(Полный код здесь)

Библиотека лицензируется Apache2, вы можете увидеть примеры в репозитории Github и адаптировать функцию перехода к вашему делу. Вы найдете библиотеку очень полезной для вашей проблемы. Если у вас есть сомнения относительно того, как сгенерировать соседей точки (фильтрация тех, которые не доступны из-за препятствий на карте), взгляните на функцию validLocationsFrom(Point) которая реализована в классе Maze2D библиотеки:

/**
* Return all neighbor empty points from a specific location point.
* @param loc source point
* @return collection of empty neighbor points.
*/
public Collection<Point> validLocationsFrom(Point loc) {
   Collection<Point> validMoves = new HashSet<Point>();
   // Check for all valid movements
   for (int row = -1; row <= 1; row++) {
      for (int column = -1; column <= 1; column++) {
         try {
            if (isFree(new Point(loc.x + column, loc.y + row))) {
               validMoves.add(new Point(loc.x + column, loc.y + row));
            }
         } catch (ArrayIndexOutOfBoundsException ex) {
            // Invalid move!
         }
      }
   }
   validMoves.remove(loc);
   return validMoves;
}

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

Надеюсь, мой ответ поможет,

Ещё вопросы

Сообщество Overcoder
Наверх
Меню