Faster Pathfinding...

Ok, well, right now I’m making a tile based dungeon crawl and my pathfinding is slow, even for a turn-based game. Right now I’m using Breadth-First-Search beacuse I tried A*, and it seemed slower…

I was wondering if anyone could hook me up with a fast pathfinding algorithm for finding the shortest open path on a tile grid from one point to the other.

Not much of what I found was in Java or tile grid specific.

Any help would be grealty appreciated. If you want to see how I’m doing my BFS search, I’ll post the code. Maybe my way is just to darn slow. :-/

Er, unless I’m horribly mistaken, but A* cannot possibly be slower than a breadth-first search. BF is the slowest way possible, and A* only degenerates into a straight BF with a bad heristic function.

I’d suggest having another look at A*. Perhaps your heristic function is bad or wrong, or you’ve made a mistake in your implementation (like not managing your open and closed lists properly).

For totally wrong heuristic (which gives opposite results from real ones) it should be worse. But even for reasonable heuristic (but non-perfect) it can be slower because of the cost of heuristic function itself.

For example, if you use euclidean distance between points as heuristic function, square root can take more time than traversing few nodes blindly without it :slight_smile: In such case manhattan metric could be used - probably good enough and a lot faster.

You probably already know it but take a look at
http://gamedev.net/reference/list.asp?categoryid=18#94
http://gamedev.net/reference/list.asp?categoryid=44
(second link especially for line of sight algorithms).

And post your code here, maybe we will discover something simple which can improve the situation. I remember that I have done screen-coord-to-hex-coord algorithm once and it got 3 times faster when I have used (int)floatVariable instead of Math.floor(floatVariable) everywhere (I could do this because floatVariable was >0).

Ok, I tried making it A* again. It runs great, except, I every now and then I get trapped in an infinite loop and I run out of memory. Also, things get slow when I leave one big room with lots enemies to go to another room with lots of enemies but not when I enter a room with lots of enemies from a room with very little…

Anyways, here’s my code so far. Please bear in mind that these are my first attempts at real pathfinding and I’ve been using Java only since May of this year. So I don’t know what I might be doing wrong.

EDIT: also, I recalculate the neighbors beacuse some enemies can walk through some terrian and some can’t.

  //
  //Constructs a list of pathNodes from npc to dx,dy
  //
  private List getPath(Npc npc,int dx,int dy){

    if( (isBlocked(dungeonMap[currFloor][dx-1][dy-1],npc) || npcGrid[currFloor][dx-1][dy-1]!=0) &&
        (isBlocked(dungeonMap[currFloor][dx][dy-1],npc) || npcGrid[currFloor][dx][dy-1]!=0) &&
        (isBlocked(dungeonMap[currFloor][dx+1][dy-1],npc) || npcGrid[currFloor][dx+1][dy-1]!=0) &&
        (isBlocked(dungeonMap[currFloor][dx-1][dy],npc) || npcGrid[currFloor][dx-1][dy]!=0) &&
        (isBlocked(dungeonMap[currFloor][dx+1][dy],npc) || npcGrid[currFloor][dx+1][dy]!=0) &&
        (isBlocked(dungeonMap[currFloor][dx-1][dy+1],npc) || npcGrid[currFloor][dx-1][dy+1]!=0) &&
        (isBlocked(dungeonMap[currFloor][dx][dy+1],npc) || npcGrid[currFloor][dx][dy+1]!=0) &&
        (isBlocked(dungeonMap[currFloor][dx+1][dy+1],npc) || npcGrid[currFloor][dx+1][dy+1]!=0)
    ){
      return null;
    }

    PathNode startNode = pathNodes[npc.getX()][npc.getY()];
    PathNode goalNode = pathNodes[dx][dy];

    // list of visited nodes
    LinkedList closedList = new LinkedList();
  
    // list of nodes to visit (sorted)
    LinkedList openList = new LinkedList();
    openList.add(startNode);
    startNode.pathParent = null;
  
    while (!openList.isEmpty()) {

      PathNode node = (PathNode)openList.removeFirst();
      if (node.x == goalNode.x && node.y == goalNode.y) {
        //path found!
        return constructPath(node);
      }


        node.neighbors.clear();
       
        // add neighbors to the open list  
        for(int yv=-1;yv<=1;yv+=1){
          for(int xv=-1;xv<=1;xv+=1){
            if(!(xv==0 && yv==0)){
              if(node.x+xv>=0 && node.y+yv>=0 && node.x+xv<MAP_WIDTH && node.y+yv<MAP_HEIGHT){

                if(!isBlocked(dungeonMap[currFloor][node.x+xv][node.y+yv],npc) && npcGrid[currFloor][node.x+xv][node.y+yv]==0){
                  node.neighbors.add(pathNodes[node.x+xv][node.y+yv]);
                }
              }
            }
          }
        }


        for(int count=0;count<node.neighbors.size();count++){
          PathNode neighborNode = (PathNode)node.neighbors.get(count);

 
          boolean isOpen = openList.contains(neighborNode);
          boolean isClosed = closedList.contains(neighborNode);

          int costFromStart=distance(node.x,node.y,npc.getX(),npc.getY())+1;

          //Check if the neighbor has not been traversed or if a shorter path to this neighboor is found
          if((!isOpen && !isClosed) || costFromStart<distance(neighborNode.x,neighborNode.y,npc.getX(),npc.getY())){
            neighborNode.pathParent=node;            
            if(isClosed){
              closedList.remove(neighborNode);
            }
            if(!isOpen){

              boolean wasAdded=false;

              for(int addCount=0;addCount<openList.size();addCount++){
                PathNode testNode = (PathNode)openList.get(addCount);
                if(distance(dx,dy,neighborNode.x,neighborNode.y)<=distance(dx,dy,testNode.x,testNode.y)){
                  openList.add(addCount,neighborNode);              
                  wasAdded=true;
                  break;
                }
              }
              if(!wasAdded)
                openList.addLast(neighborNode);              


            }


          }
 
 

        }

        closedList.add(node);
      


    }
  
    // no path found
    return null;
  }

  private List constructPath(PathNode node) {
    LinkedList path = new LinkedList();
    while (node.pathParent != null) {
      path.addFirst(pathNodes[node.x][node.y]);
      node = node.pathParent;
    }
    return path; 
  }

Pathnodes simple contain x,y List neighbors and reference to its parent. I made an array of pathnodes to cover one floor so I don’t have to create any new objects… except for when I contruct a path.

I’ll check out those links right now that you left abies.