使用多个代理进行A *寻路

时间:2015-03-06 00:29:40

标签: java algorithm

我目前正在使用A *算法学习和编程寻路(在Java中)。我遇到的一个问题是,当多个实体尝试寻路时,它们都会改变previousNode(计算出Node的{​​{1}}),弄乱了算法,最终Node将指向Node ANode B将指向Node B

如何将算法更改为

  • 不使用遍布所有A *算法的Node A系统(我已经看过,就是这样)
  • 改变此系统同时使用

我试图避免让一个实体完成寻路,然后告诉下一个实体寻路,等等。就像在Java中执行previousNode - wait()对一样。

notify()

我具体谈到(public Path findPath(int startX, int startY, int goalX, int goalY) { //Path is basically just a class that contains an ArrayList, //containing Nodes, which contains the steps to reach a goal. if(map.getNode(goalX, goalY).isObstacle()) { return null; } map.getNode(startX, startY).setDistanceFromStart(0); closedList.clear(); openList.clear(); //A List with added getFirst() - gets the first Node in the list openList.add(map.getNode(startX, startY)); while(openList.size() != 0) { //Node contains a List that has all of the Nodes around this node, a //F, G, and H value, and its row(y) and column(x) Node current = openList.getFirst(); if(current.getX() == goalX && current.getY() == goalY) { return backtrackPath(current); } openList.remove(current); closedList.add(current); for(Node neighbor : current.getNeighborList()) { boolean neighborIsBetter; //If I've already searched this neighbor/node, don't check it if(closedList.contains(neighbor)) { continue; } if(!neighbor.isObstacle()) { float neighborDistanceFromStart = (current.getDistanceFromStart() + map.getDistanceBetween(current, neighbor)); if(!openList.contains(neighbor)) { openList.add(neighbor); neighborIsBetter = true; } else if(neighborDistanceFromStart < current.getDistanceFromStart()) { neighborIsBetter = true; } else { neighborIsBetter = false; } if(neighborIsBetter) { neighbor.setPreviousNode(current); neighbor.setDistanceFromStart(neighborDistanceFromStart); neighbor.setHeuristic(getManhattanDistance(neighbor.getX(), neighbor.getY(), goalX, goalY)); } } } } return null; } public Path backtrackPath(Node fromNode) { Path path = new Path(); while(fromNode.getPreviousNode() != null) { path.prependWaypoint(fromNode); fromNode = fromNode.getPreviousNode(); } return path; } 内)

findPath()

1 个答案:

答案 0 :(得分:1)

我认为你不能以某种方式为给定路径存储一个backpointer,你可以做A *(或任何寻路算法)。所以这给你留下了两个选择

  1. 要求每个代理(Thread,我假设)创建自己的图形副本以进行处理。这样,每次进行的A *调用都不会相互干扰,因为他们正在使用不同图形上相同节点的字段。
  2. 更改您的A *代码,以便能够处理多个并发呼叫。
  3. 选项1是相当不言自明的,可能是更好的选择。如果这只是为了你,你应该只使用那个(而不是试图在一个图上使A *完全并发)。这将需要添加map作为输入参数(并要求并发调用应使用不同的映射实例,如果不发生则抛出异常或具有未指定的行为)。此外,您应该将closedListopenList实例化为每个调用中的新数据结构,而不是共享列表。

    如果这不符合您的喜好 - 您真的希望将多重调用用法完全封装到方法本身中,我认为最简单的方法就是需要一个额外的id参数 - 一些独特的保证不与另一个并发调用的id相同的字符串。所以A *的标题现在看起来像:

    public Path findPath(final String ID, int startX, int startY, int goalX, int goalY) { 
    

    从那里,将Node中每个可设置路径查找字段的所有实现更改为HashMap,并将id作为关键字。从你的代码中,我猜你的Node类看起来像这样:

    public class Node{
        //Fields used by the A* call - no problem here
        private boolean obstacle;
    
        //Fields *edited* by the A* call
        private float distanceFromStart;
        private Node previous;
        private int heuristic;
    
        //other fields and stuff
    
        public boolean isObstacle(){
            return obstacle;
        }
    
        public float getDistanceFromStart(){
            return distanceFromStart;
        }
    
        public void setDistanceFromStart(float f){
            distanceFromStart = f;
        }
    
        public Node getPrevious(){
            return previous;
        }
    
        public void setPrevious(Node p){
            previous = p;
        }
    
        public int getHeuristic(){
            return heuristic;
        }
    
        public void setHeuristic(int h){
            heuristic = h;
        }
    }
    

    我们可以修改已修改字段,以便能够按id存储多个值:

    public class Node{
        //Fields used by the A* call - no problem here
        private boolean obstacle;
    
        //Fields *edited* by the A* call
        private HashMap<String,Float> distanceFromStart;
        private HashMap<String,Node> previous;
        private HashMap<String,Integer> heuristic;
    
        //other fields and stuff
    
        public boolean isObstacle(){
            return obstacle;
        }
    
        public float getDistanceFromStart(String id){
            return distanceFromStart.get(id);
        }
    
        public void setDistanceFromStart(String id, float f){
            distanceFromStart.put(id, f);
        }
    
        public Node getPrevious(String id){
            return previous.get(id);
        }
    
        public void setPrevious(String id, Node p){
            previous.put(id,p);
        }
    
        public int getHeuristic(String id){
            return heuristic.get(id);
        }
    
        public void setHeuristic(String id,int h){
            heuristic.put(id,h);
        }
    }
    

    从那里开始,只需编辑你的A *方法,在调用时将方法调用的id提供给getter和setter。只要两个并发方法调用没有相同的id值,它们就不会相互干扰。要记住三件事要正确使用:

    1. 确保每个可编辑字段都获得此处理。如果你忘了一个就行不通。不可编辑的字段(作为运行A *的副产品不会被更改的字段)可以保持单数。
    2. 如果您使用上述内容,您可能应该向清理阶段添加从图表中删除给定ID的所有信息的步骤,或者每次调用时节点的哈希图都会变大。
    3. 无论哪种方式,无论您采用何种并发方法,您仍然应该制作openListclosedList个新本地实例。制作openListclosedList共享实例没有任何好处,只有错误才能实现。

      List<Node> closedList = new LinkedList<Node>();
      List<Node> openList = new LinkedList<Node>();
      //Don't have to clear them anymore - they're new lists
      openList.add(map.getNode(startX, startY));