微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

A* (A 星) - 算法 |有墙怎么回去?

如何解决A* (A 星) - 算法 |有墙怎么回去?

我目前正在尝试实施 A*(A 星) - 算法。当我没有墙壁并且只需要在墙壁旁边时,我已经让它工作了。我现在的问题是,当我将起点放在墙内时,我的算法正在无限计算,因为我认为它不会倒退。你们能帮我吗?

这里是节点类的代码

int[][] mMap; // if there is a wall => 1

int[][] mAStarField;
ArrayList<AStarNode> mAStarPath;

public class AStarNode implements Comparable<AStarNode>{ 
  public int x; 
  public int y; 
  public float c;
  public AStarNode p; 
  public AStarNode(int x,int y,float c,AStarNode p) { 
    this.x = x; //X pos
    this.y = y; //Y pos
    this.c = c; //Cost to get to the node
    this.p = p; //Parent of the node
  }
  //override the compareto method 
  public int compareto(AStarNode node) 
  { 
    if (c == node.c) 
      return 0; 
    else if (c > node.c) 
      return 1; 
    else
      return -1; 
    } 
  
  
} 

public class Node { 
  public int x; 
  public int y; 
  public int z; 
  public int w; 
  public Node(int x,int z,int w) { 
    this.x = x; 
    this.y = y;
    this.z = z; 
    this.w = w;
  }
}

这是我的 A* 算法的代码

//Pathfinding with A*
//return path length
int updateAStar() {


  //Needed for drawing:
  //Array containing the distance to the start node (filled with max int at start)
  mAStarField = new int[mMap.length][mMap[0].length];
  //List containing the found path
  mAStarPath = new ArrayList<AStarNode>();

  for (int j = 0; j < mMap.length; j++) {
    for (int k = 0; k < mMap[0].length; k++) {
      mAStarField[j][k]=Integer.MAX_VALUE;
    }
  }

//AStarNode(x,y,c,w)
  //x X pos
  //y Y pos
  //c Cost to get to the node
  //p Parent of the node

  //List can be sorted expensive but simple by c value with
  //Collections.sort(openList); 
  ArrayList<AStarNode> openList = new  ArrayList<AStarNode>();
  ArrayList<AStarNode> closedList = new  ArrayList<AStarNode>();

  int dist = abs(mStartNode[0]-mEndNode[0])+abs(mStartNode[1]-mEndNode[1]);

  //If there is any target,that isn't on my field,add start node to list
  if (dist>0 && mMap[mStartNode[0]][mStartNode[1]] != 1 && mMap[mEndNode[0]][mEndNode[1]]!=1) {
    openList.add(new AStarNode(mStartNode[0],mStartNode[1],null));
    mAStarField[mStartNode[0]][mStartNode[1]] = 0;
  }
  
// my code begins here (only everything from here on can be edited!)

  while(!openList.isEmpty())
  {
    Collections.sort(openList);
    AStarNode current = openList.get(0);
    
    if(current.x == mEndNode[0] && current.y == mEndNode[1])
    {
      return 1;
    }
    
    openList.remove(0);
    closedList.add(current);
    
    ArrayList<AStarNode> neighbors = new  ArrayList<AStarNode>();
    neighbors.add(new AStarNode(current.x - 1,current.y,current.c + 1,current));
    neighbors.add(new AStarNode(current.x + 1,current));
    neighbors.add(new AStarNode(current.x,current.y - 1,current.y + 1,current));
    
    for(AStarNode n : neighbors)
    {
      if(n.x >= 0 && n.y >= 0 && n.x < mMap.length && n.y < mMap.length && mMap[n.x][n.y] != 1){
      
      float cost = estimatedistanceEnd(n.x,n.y);
      n.c = cost;
      
      if(closedList.contains(n) && cost >= n.c) continue;
      
      if(!openList.contains(n) || cost < n.c)
      {
        n.p = current;
        
        if(!openList.contains(n)){
          mAStarField[n.x][n.y] = (int) n.c;
          openList.add(n);
          Collections.sort(openList);
        }
      }
      }
    }
    
  }
  return -1;
}

int estimatedistanceEnd(int x,int y){
   return abs(x-mEndNode[0])+abs(y-mEndNode[1]); 
}

int estimatedistanceStart(AStarNode a){
   return abs(a.x-mStartNode[0])+abs(a.y-mStartNode[1]); 
}

int estimatedistance(AStarNode a,AStarNode b){
   return abs(a.x-b.x)+abs(a.y-b.y); 
}

A picture of my current path solving result

重要提示:我只能在我标记的区域内更改代码

谢谢!

解决方法

我无法运行代码,但我对问题的根源有所了解。看,A* 算法在寻找最佳路径时不会“往回走”。它通过为它评估的每个节点计算到达终点的成本较低的方法来解决寻路问题。它首先计算最直接的行程,然后如果它不起作用,它会扩大它的选项,直到它或者,呃,找到一种方法 - 或者用完选项。

闭表原则避免对一个节点求值两次。正如您所猜测的,这里的问题是您在寻路算法的每次迭代中都在为邻居创建新节点,从而使封闭列表更难正确使用。

像自定义类这样的复杂对象可以通过 3 种方式进行比较:它要么是同一个对象(它引用同一个指针(它是同一个实例,它在计算机的内存)),或者无论指针指向什么值都是相同的,或者您可以定义规则来比较它们。这些方法是:按引用比较、按值比较和运算符重载 - 虽然最后一个在 java 中是不可能的,但你可以编写一个方法来做同样的事情。

在执行 closedList.contains(n) 时,您是通过引用进行比较(这是此类操作的默认设置)。由于所有节点都是动态创建的,即使它们的坐标相同,它们在内存中的地址也不同,这就是永远不会满足此条件的原因。

假设你不能弄乱你导师的代码,你仍然可以解决这个问题。你几乎第一次就做对了!事实上,有很多方法可以解决这个问题,因为我错过了一些上下文,所以我在我建议的方法中会很简单:您将编写一个方法来从列表中获取特定节点(例如运算符重载我谈过,但会尽最大努力),从现在开始,我们将参考工作。

首先,创建一个包含所有 AStarNode 的主列表(如果您还没有,如果有,请改用那个):

// my code begins here (only everything from here on can be edited!)
ArrayList<AStarNode> nodesList = new  ArrayList<AStarNode>();
for (int j = 0; j < mapWidth; j++) {
  for (int k = 0; k < mapHeight; k++) {
    nodesList.add(new AStarNode(j,k)); // I gimmicked the constructor for my own confort,you'll have to tweak this line so it fits in your code
  }
}

然后,自己编写一个方法,根据给定的 xy 坐标从数组中返回一个节点:

AStarNode GetAStarNodeByPosition(int x,int y,ArrayList<AStarNode> list) {
  for (AStarNode m : list) {
    if (m.x == x && m.y == y) {
      return m;
    }
  }

  return null;
}

现在,您可以使用这些通过引用来比较所有节点。因此,现在您不必一直实例化新节点,而是始终通过引用从主列表中获取它们:

ArrayList<AStarNode> neighbors = new  ArrayList<AStarNode>();
neighbors.add(GetAStarNodeByPosition(current.x - 1,current.y,nodesList));
neighbors.add(GetAStarNodeByPosition(current.x + 1,nodesList));
neighbors.add(GetAStarNodeByPosition(current.x,current.y - 1,current.y + 1,nodesList));

另外,不要忘记修复这一行:

//openList.add(new AStarNode(mStartNode[0],mStartNode[1],null));
openList.add(GetAStarNodeByPosition(mStartNode[0],nodesList));

最后,如果您知道您的数组中可能有一些,请务必记住测试 null。在这种情况下,如果您离迷宫的边界太近,GetAStarNodeByPosition 方法可以返回 null。您可以修改添加到邻居列表的方式,以便其中没有 null,或者您可以在此行上检查 null:

if(n != null && n.x >= 0 && n.y >= 0 && n.x < mMap.length && n.y < mMap.length && mMap[n.x][n.y] != 1){

老实说,我根本不会在数组中包含 null,如果您稍后进一步修改代码,那会更安全。

现在您的所有节点都将相关联,您将能够克服障碍,这需要您的算法以比直线更聪明的方式进行搜索。

玩得开心!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。