我一直在努力实现Java中的A *算法。我想要做的是加载在文本文件中创建的瓦片地图(空格,障碍物,开始和目标由不同的字符标记),从中创建节点地图并能够在地图上运行A *。但是,当我运行A *时,它会在开始列表用完之前检查开始周围的几个节点并返回null。过去几天我一直在搞乱这个问题,我无法弄清楚它失败的原因。它可能是愚蠢的,但任何帮助都会受到赞赏。
A *实施:
public ArrayList<Node> calculateShortestPath(int startHeight, int startWidth, int goalHeight, int goalWidth) {
map.setStart(startHeight, startWidth);
map.setGoal(goalHeight, goalWidth);
if (map.getGoal().isObstacle()) {
return null;
}
map.getStart().setDistanceFromStart(0);
closedList.clear();
openList.clear();
openList.add(map.getStart());
Collections.sort(openList);
while (!openList.isEmpty()) {
Node currentNode = openList.get(0); //the list should be sorted, so this should produce the node with the lowest f value
if (currentNode.equals(map.getGoal())) {
return reconstructPath(currentNode);
}
openList.remove(currentNode);
closedList.add(currentNode);
for (Node neighbour : currentNode.getNeighbourList()) {
System.out.println(neighbour);
boolean neighbourIsBetter;
if (closedList.contains(neighbour)) {
continue;
}
if (!neighbour.isObstacle()) {
float neighbourDistanceFromStart = (currentNode.getDistanceFromStart() + map.getDistanceBetweenNodes(currentNode, neighbour));
if (!openList.contains(neighbour)) {
openList.add(neighbour);
Collections.sort(openList);
neighbourIsBetter = true;
} else if (neighbourDistanceFromStart < currentNode.getDistanceFromStart()) {
neighbourIsBetter = true;
} else {
neighbourIsBetter = false;
}
if (neighbourIsBetter) {
neighbour.setPreviousNode(currentNode);
neighbour.setDistanceFromStart(neighbourDistanceFromStart);
neighbour.setHeuristicDistanceFromGoal(getEstimatedDistanceToGoal(neighbour.getHeight(), neighbour.getWidth(), goalHeight, goalWidth));
}
}
}
}
return null;
}
一些地图创建方法:
private void createMap() {
Node node;
map = new Node[mapHeight][mapWidth];
for (int x = 0; x < mapHeight; x++) {
for (int y = 0; y < mapWidth; y++) {
node = new Node(x, y);
map[x][y] = node;
if (obstacles[x][y] == 1) {
node.setObstacle(true);
}
}
}
}
private void registerEdges() {
for (int x = 0; x < mapHeight - 1; x++) {
for (int y = 0; y < mapWidth - 1; y++) {
Node node = map[x][y];
if (!(x == 0)) {
node.setNorth(map[x - 1][y]);
}
if (!(y == mapWidth)) {
node.setEast(map[x][y + 1]);
}
if (!(x == mapHeight)) {
node.setSouth(map[x + 1][y]);
}
if (!(y == 0)) {
node.setWest(map[x][y - 1]);
}
}
}
}
最后,compareTo覆盖用于对打开列表进行排序
@Override
public int compareTo(Node otherNode) {
float thisTotalDistanceFromGoal = heuristicDistanceFromGoal + distanceFromStart;
float otherTotalDistanceFromGoal = otherNode.getHeuristicDistanceFromGoal() + otherNode.getDistanceFromStart();
if (thisTotalDistanceFromGoal < otherTotalDistanceFromGoal) {
return -1;
} else if (thisTotalDistanceFromGoal > otherTotalDistanceFromGoal) {
return 1;
} else {
return 0;
}
}
所有其他方法都是getter,setter和其他支持方法。我使用曼哈顿距离作为启发式算法。我知道我通过高度然后宽度(y,x对x,y)创建和访问地图。它源于这样的事实:这是文件的读入方式(行然后是行中的字符),并且我选择在整个过程中保持它,因为来回切换最初会给我带来问题。
这里有一些关于我尝试过的内容。我在测试期间打印出了节点图,所以我知道它正在被正确创建。我还在算法中访问节点时打印出有关节点的信息,并正确分配高度和宽度值,算法从正确的位置开始。我已经使用断点来确认我的compareTo方法是被调用的方法,并且我可以在任何时候将节点添加到openList中进行排序。我根本无法发现问题。
编辑: 附加代码:
完整地图类:
public class Map {
private final int mapWidth;
private final int mapHeight;
private Node[][] map;
private final int[][] obstacles;
private int startHeight = 0;
private int startWidth = 0;
private int goalHeight = 0;
private int goalWidth = 0;
public Map (int mapWidth, int mapHeight, int[][] obstacles) {
this.mapWidth = mapWidth;
this.mapHeight = mapHeight;
this.obstacles = obstacles;
createMap();
registerEdges();
}
private void createMap() {
Node node;
map = new Node[mapHeight][mapWidth];
for (int x = 0; x < mapHeight; x++) {
for (int y = 0; y < mapWidth; y++) {
node = new Node(x, y);
map[x][y] = node;
if (obstacles[x][y] == 1) {
node.setObstacle(true);
}
}
}
}
private void registerEdges() {
for (int x = 0; x < mapHeight - 1; x++) {
for (int y = 0; y < mapWidth - 1; y++) {
Node node = map[x][y];
if (!(x == 0)) {
node.setNorth(map[x - 1][y]);
}
if (!(y == mapWidth)) {
node.setEast(map[x][y + 1]);
}
if (!(x == mapHeight)) {
node.setSouth(map[x + 1][y]);
}
if (!(y == 0)) {
node.setWest(map[x][y - 1]);
}
}
}
}
public Node[][] getNodes() {
return map;
}
public void setObstacle(int height, int width, boolean isObstacle) {
map[height][width].setObstacle(isObstacle);
}
public Node getNode(int height, int width) {
return map[height][width];
}
public void setStart(int height, int width) {
map[startHeight][startWidth].setStart(false);
map[height][width].setStart(true);
startHeight = height;
startWidth = width;
}
public void setGoal(int height, int width) {
map[goalHeight][goalWidth].setGoal(false);
map[height][width].setGoal(true);
goalHeight = height;
goalWidth = width;
}
public int getStartHeight() {
return startHeight;
}
public int getStartWidth() {
return startWidth;
}
public Node getStart() {
return map[startHeight][startWidth];
}
public int getGoalHeight() {
return goalHeight;
}
public int getGoalWidth() {
return goalWidth;
}
public Node getGoal() {
return map[goalHeight][goalWidth];
}
public float getDistanceBetweenNodes(Node node1, Node node2) {
return 1 * (mapWidth + mapHeight);
}
public int getWidth() {
return mapWidth;
}
public int getHeight() {
return mapHeight;
}
public void clear() {
startHeight = 0;
startWidth = 0;
goalHeight = 0;
goalWidth = 0;
createMap();
registerEdges();
}
}
完整的节点类:
public class Node implements Comparable<Node>{
private Map map;
private Node north;
private Node east;
private Node south;
private Node west;
private ArrayList<Node> neighbourList;
boolean visited;
float distanceFromStart;
float heuristicDistanceFromGoal;
Node previousNode;
int height;
int width;
boolean isObstacle;
boolean isStart;
boolean isGoal;
public Node(int height, int width) {
neighbourList = new ArrayList<Node>();
this.height = height;
this.width = width;
this.visited = false;
this.distanceFromStart = Integer.MAX_VALUE;
this.isObstacle = false;
this.isStart = false;
this.isGoal = false;
}
public Node (int height, int width, boolean visited, int distanceFromStart, boolean isObstacle, boolean isStart, boolean isGoal) {
neighbourList = new ArrayList<Node>();
this.height = height;
this.width = width;
this.visited = visited;
this.distanceFromStart = distanceFromStart;
this.isObstacle = isObstacle;
this.isStart = isStart;
this.isGoal = isGoal;
}
public Node getNorth() {
return north;
}
public void setNorth(Node north) {
//replace the old Node with the new one in the neighborList
if (neighbourList.contains(this.north)) {
neighbourList.remove(this.north);
}
neighbourList.add(north);
//set the new Node
this.north = north;
}
public Node getEast() {
return east;
}
public void setEast(Node east) {
//replace the old Node with the new one in the neighborList
if (neighbourList.contains(this.east)) {
neighbourList.remove(this.east);
}
neighbourList.add(east);
//set the new Node
this.east = east;
}
public Node getSouth() {
return south;
}
public void setSouth(Node south) {
//replace the old Node with the new one in the neighborList
if (neighbourList.contains(this.south)) {
neighbourList.remove(this.south);
}
neighbourList.add(south);
//set the new Node
this.south = south;
}
public Node getWest() {
return west;
}
public void setWest(Node west) {
//replace the old Node with the new one in the neighborList
if (neighbourList.contains(this.west)) {
neighbourList.remove(this.west);
}
neighbourList.add(west);
//set the new Node
this.west = west;
}
public ArrayList<Node> getNeighbourList() {
return neighbourList;
}
public boolean isVisited() {
return visited;
}
public void setVisited(boolean visited) {
this.visited = visited;
}
public float getDistanceFromStart() {
return distanceFromStart;
}
public void setDistanceFromStart(float distanceFromStart) {
this.distanceFromStart = distanceFromStart;
}
public float getHeuristicDistanceFromGoal() {
return heuristicDistanceFromGoal;
}
public void setHeuristicDistanceFromGoal(float heuristicDistanceFromGoal) {
this.heuristicDistanceFromGoal = heuristicDistanceFromGoal;
}
public Node getPreviousNode() {
return previousNode;
}
public void setPreviousNode(Node previousNode) {
this.previousNode = previousNode;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public boolean isObstacle() {
return isObstacle;
}
public void setObstacle(boolean isObstacle) {
this.isObstacle = isObstacle;
}
public boolean isStart() {
return isStart;
}
public void setStart(boolean isStart) {
this.isStart = isStart;
}
public boolean isGoal() {
return isGoal;
}
public void setGoal(boolean isGoal) {
this.isGoal = isGoal;
}
public boolean equals(Node node) {
return (node.height == this.height) && (node.width == this.width);
}
@Override
public int compareTo(Node otherNode) {
float thisTotalDistanceFromGoal = heuristicDistanceFromGoal + distanceFromStart;
float otherTotalDistanceFromGoal = otherNode.getHeuristicDistanceFromGoal() + otherNode.getDistanceFromStart();
if (thisTotalDistanceFromGoal < otherTotalDistanceFromGoal) {
return -1;
} else if (thisTotalDistanceFromGoal > otherTotalDistanceFromGoal) {
return 1;
} else {
return 0;
}
}
@Override
public String toString() {
return "Height: " + this.getHeight() + " Width: " + this.getWidth();
}
//for debugging
public void printObstacle() {
if (isObstacle) {
System.out.print("1 ");
} else {
System.out.print("0 ");
}
}
}
完成AStar课程:
public class AStar {
private Map map;
private ArrayList<Node> closedList;
private ArrayList<Node> openList;
private ArrayList<Node> wayPoints;
public AStar(Map map) {
this.map = map;
closedList = new ArrayList<Node>();
openList = new ArrayList<Node>();
}
public ArrayList<Node> calculateShortestPath(int startHeight, int startWidth, int goalHeight, int goalWidth) {
map.setStart(startHeight, startWidth);
map.setGoal(goalHeight, goalWidth);
if (map.getGoal().isObstacle()) {
return null;
}
map.getStart().setDistanceFromStart(0);
closedList.clear();
openList.clear();
openList.add(map.getStart());
Collections.sort(openList);
while (!openList.isEmpty()) {
Node currentNode = openList.get(0);
if (currentNode.equals(map.getGoal())) {
return reconstructPath(currentNode);
}
openList.remove(currentNode);
closedList.add(currentNode);
for (Node neighbour : currentNode.getNeighbourList()) {
System.out.println(neighbour);
boolean neighbourIsBetter;
if (closedList.contains(neighbour)) {
continue;
}
if (!neighbour.isObstacle()) {
float neighbourDistanceFromStart = (currentNode.getDistanceFromStart() + map.getDistanceBetweenNodes(currentNode, neighbour));
if (!openList.contains(neighbour)) {
openList.add(neighbour);
Collections.sort(openList);
neighbourIsBetter = true;
} else if (neighbourDistanceFromStart < currentNode.getDistanceFromStart()) {
neighbourIsBetter = true;
} else {
neighbourIsBetter = false;
}
if (neighbourIsBetter) {
neighbour.setPreviousNode(currentNode);
neighbour.setDistanceFromStart(neighbourDistanceFromStart);
neighbour.setHeuristicDistanceFromGoal(getEstimatedDistanceToGoal(neighbour.getHeight(), neighbour.getWidth(), goalHeight, goalWidth));
}
}
}
}
return null;
}
private ArrayList<Node> reconstructPath(Node node) {
ArrayList<Node> path = new ArrayList<Node>();
while (!(node.getPreviousNode() == null)) {
wayPoints.add(0, node);
node = node.getPreviousNode();
}
this.wayPoints = path;
return path;
}
private float getEstimatedDistanceToGoal(int startX, int startY, int goalX, int goalY) {
float dx = Math.abs(goalX - startX);
float dy = Math.abs(goalY - startY);
return dx + dy;
}
public ArrayList<Node> getWayPoints() {
return wayPoints;
}
}
相关主要代码:
文件阅读,地图创建和AStar通话:
ArrayList<ArrayList<Integer>> readMap = new ArrayList<ArrayList<Integer>>();
int height = 0;
int width = 0;
int startHeight = 0;
int startWidth = 0;
int goalHeight = 0;
int goalWidth = 0;
File fileMap = new File("./map.txt");
try (BufferedReader br = new BufferedReader(new FileReader(fileMap))) {
String line;
while ((line = br.readLine()) != null) {
char[] characters = line.toCharArray();
readMap.add(new ArrayList<Integer>());
for (char character : characters) {
if (Character.isWhitespace(character)) {
readMap.get(height).add(0);
} else if (character == 'X') {
readMap.get(height).add(1);
} else if (character == 'S') {
readMap.get(height).add(0);
startHeight = height;
startWidth = width;
} else if (character == 'G') {
readMap.get(height).add(0);
goalHeight = height;
goalWidth = width;
}
width++;
}
width = 0;
height++;
mapFile.add(line);
}
br.close();
}
catch (IOException ex) {
System.err.println("Exception: " + ex.getMessage());
}
height = 0;
width = 0;
int[][] obstacleMap = new int[readMap.size()][readMap.get(0).size()];
for (ArrayList<Integer> row : readMap) {
for (Integer column : row) {
obstacleMap[height][width] = column;
width++;
}
width = 0;
height++;
}
Map map = new Map(obstacleMap[0].length, obstacleMap.length, obstacleMap);
AStar pathfinder = new AStar(map);
shortestPath = pathfinder.calculateShortestPath(startHeight, startWidth, goalHeight, goalWidth);
运行时的邻居节点打印输出:
Height: 11 Width: 0
Height: 12 Width: 1
Height: 13 Width: 0
Height: 10 Width: 0
Height: 11 Width: 1
Height: 12 Width: 0
Height: 11 Width: 1
Height: 12 Width: 2
Height: 13 Width: 1
Height: 12 Width: 0
Height: 10 Width: 1
Height: 11 Width: 2
Height: 12 Width: 1
Height: 11 Width: 0
Height: 11 Width: 2
Height: 12 Width: 3
Height: 13 Width: 2
Height: 12 Width: 1
Height: 10 Width: 2
Height: 11 Width: 3
Height: 12 Width: 2
Height: 11 Width: 1
这就是它所做的。 (12,0)是起始节点(对于该映射)。此外,对角线路径不是必需的,因此仅查看北,西,东和南节点。