Java-игра: алгоритм A * (поиск только в ячейках перед символом)
Тип игры: карта мозаики сетки, основанная на повороте
Допустимые направления: влево, вперед, вправо (для изменения направления вы должны использовать два левых или два правых) - и влево, и вправо перемещаются по диагонали, но меняют лицо корабля в зависимости от исходного лица)
Слоты: в зависимости от размера корабля для этого конкретного корабля есть определенное количество слотов, в которые пользователь может войти, чтобы корабль мог перемещаться на такое количество мест за ход (т. Е. 3 слота == 3 хода за ход).
Пример:
Стартовая позиция: 2,2
Стартовая грань: север
Перемещение размещено: влево
Конечный результат: позиция: 1,3; Лицо: Запад
Проблема: алгоритм использует все 8 плиток для поиска пути; но следует искать только плитки, которые находятся впереди (в зависимости от лица корабля)
Класс узла:
public class AStarNode {
public Position position;
public VesselFace face;
public AStarNode parent;
public double fCost, gCost, hCost;
public AStarNode(Position position, VesselFace face, AStarNode parent, double gCost, double hCost) {
this.position = position;
this.face = face;
this.parent = parent;
this.gCost = gCost;
this.hCost = hCost;
this.fCost = this.gCost + this.hCost;
}
}
Расчет поиска пути:
private Comparator<AStarNode> nodeSorter = new Comparator<AStarNode>() {
@Override
public int compare(AStarNode n0, AStarNode n1) {
if(n1.fCost < n0.fCost) return 1;
if(n1.fCost > n0.fCost) return -1;
return 0;
}
};
public List<AStarNode> findPath(Position start, Position goal){
List<AStarNode> openList = new ArrayList<AStarNode>();
List<AStarNode> closedList = new ArrayList<AStarNode>();
AStarNode current = new AStarNode(start, null, 0, start.distance(goal));
openList.add(current);
while(openList.size() > 0) {
Collections.sort(openList, nodeSorter);
current = openList.get(0);
if(current.position.equals(goal)) {
List<AStarNode> path = new ArrayList<AStarNode>();
while(current.parent != null) {
path.add(current);
current = current.parent;
}
openList.clear();
closedList.clear();
return path;
}
openList.remove(current);
closedList.add(current);
for(int i = 0; i < 9; i++) {
if (i == 4)continue;
int x = current.position.getX();
int y = current.position.getY();
int xi = (i % 3) - 1;
int yi = (i / 3) - 1;
int at = context.getMap().getTile(x + xi, y + yi);
if(at == 1 || at == 2) continue; // ignore rocks
Position a = new Position(x + xi, y + yi);
double gCost = current.gCost + current.position.distance(a);
double hCost = a.distance(goal);
AStarNode node = new AStarNode(a, current, gCost, hCost);
if(positionInList(closedList, a) && gCost >= node.gCost) continue;
if(!positionInList(openList, a) || gCost < node.gCost) openList.add(node);
}
}
closedList.clear();
return null;
}
private boolean positionInList(List<AStarNode> list, Position position) {
for(AStarNode n : list) {
if(n.position.equals(position)) return true;
}
return false;
}
Выполнение:
@Override
public void calculateRoute() {
Position destination = new Position(3,3); // replace with cluster
if(this.equals(destination)) {
return;
}based
path = context.getPlayerManager().findPath(this, destination);
VesselFace face = getFace();
if(path != null) {
if(path.size() > 0) {
int numberOfMoves = getVessel().has3Moves() ? 3 : 4;
Position currentPosition = this.copy();
for(int slot = 0; slot <= numberOfMoves; slot++) { //moves to enter
int positionIndex = (path.size() - 1) - (slot); //subtract slot to allow multiple moves
if(positionIndex < 0 || path.size() < slot) { // make sure it doesn't count too far
return;
}
Position pos = path.get(positionIndex).position;
Position left = MoveType.LEFT.getFinalPosition(currentPosition, face);
Position right = MoveType.RIGHT.getFinalPosition(currentPosition, face);
Position forward = MoveType.FORWARD.getFinalPosition(currentPosition, face);
if(left.equals(pos)) {
currentPosition.add(left.getX() - getX(), left.getY() - getY());
getMoves().setMove(slot, MoveType.LEFT);
switch(face) {
case NORTH:
face = VesselFace.WEST;
break;
case SOUTH:
face = VesselFace.EAST;
break;
case WEST:
face = VesselFace.SOUTH;
break;
case EAST:
face = VesselFace.NORTH;
break;
}
}else if(right.equals(pos)) {
currentPosition.add(right.getX() - getX(), right.getY() - getY());
getMoves().setMove(slot, MoveType.RIGHT);
switch(face) {
case NORTH:
face = VesselFace.EAST;
break;
case SOUTH:
face = VesselFace.WEST;
break;
case WEST:
face = VesselFace.NORTH;
break;
case EAST:
face = VesselFace.SOUTH;
break;
}
}else if(forward.equals(pos)){
currentPosition.add(forward.getX() - getX(), forward.getY() - getY());
getMoves().setMove(slot, MoveType.FORWARD);
switch(face) {
case NORTH:
face = VesselFace.NORTH;
break;
case SOUTH:
face = VesselFace.SOUTH;
break;
case WEST:
face = VesselFace.WEST;
break;
case EAST:
face = VesselFace.EAST;
break;
}
}
}
}
}
}
Я использую оператор switch и метод currentPosition.add (), чтобы, когда вы делаете 3 хода для этого конкретного хода; он знает, где это должно закончиться. Наверное, не лучшая практика.
Заявление, которое добавляет ход в конкретный слот
getMoves().setMove(slot, MoveType.FORWARD);
Плитки, которые следует проверять каждый ход в зависимости от лица корабля:

Ответы
Это лишь частичная попытка дать более подробную информацию о моем комментарии.
A * ищет по графу узлов, которые содержат "состояние" корабля. В большинстве руководств (включая мой, извините) состояние - это только позиция. Но в вашем случае я думаю, что состояние - это и позиция, и направление взгляда. Вам нужно знать направление взгляда, чтобы рассчитать три позиции перед ним. А затем, после движения, у вас будет и позиция, и новое направление взгляда.
Node
в настоящее время имеет позицию; измените его, чтобы иметь и position
и facing
. Вот примерный вариант for(int i = 0; i < 9; i++)
петли для поиска соседей. Вместо того, чтобы проходить через 9 соседей, каждое из 4 направлений будет иметь ровно 3 соседа. (Да, их 12, а не 8! Потому что это зависит от того, в каком направлении вы смотрели раньше)

int x = current.position.getX();
int y = current.position.getY();
List<Node> neighbors = new ArrayList<Node>();
switch (current.facing) {
case NORTH:
neighbors.add(new Node(new Position(x-1, y-1), WEST, …));
neighbors.add(new Node(new Position(x, y-1), NORTH, …));
neighbors.add(new Node(new Position(x+1, y-1), EAST, …));
break;
case EAST:
neighbors.add(new Node(new Position(x+1, y-1), NORTH, …));
neighbors.add(new Node(new Position(x+1, y), EAST, …));
neighbors.add(new Node(new Position(x+1, y+1), SOUTH, …));
break;
case SOUTH:
neighbors.add(new Node(new Position(x-1, y+1), WEST, …));
neighbors.add(new Node(new Position(x, y+1), SOUTH, …));
neighbors.add(new Node(new Position(x+1, y+1), EAST, …));
break;
case WEST:
neighbors.add(new Node(new Position(x-1, y-1), NORTH, …));
neighbors.add(new Node(new Position(x-1, y), WEST, …));
neighbors.add(new Node(new Position(x-1, y+1), SOUTH, …));
break;
}
/* for each of the nodes in the neighbors list, use the same
logic you already have:
1. check if it's a rock, and ignore if it is
2. calculate g cost, store it in the node
3. calculate h cost, store it in the node
4. consider adding the node to openList
*/