У меня есть сетка, в которой я использую звезду, на которой можно успешно пересекать с одного квадрата на другой, обойдя препятствия. Тем не менее, вычисление долгого времени может вызвать определенную задержку, поэтому я подумал, вместо того, чтобы вычислять 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("====================");
}
Причина отсутствия полного пути вместо вычисления нескольких этапов пути независимо состоит в том, чтобы гарантировать оптимальность решения. Кроме того, я считаю, что, получив путь длиной ~ 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 * должна найти оптимальный путь всего за несколько миллисекунд. Алгоритм генерирует узлы, используемые алгоритмом поиска, динамически, сохраняя большую память в больших проблемах.
Надеюсь, мой ответ поможет,