package ei.game.algo; import java.util.Arrays; /** * DJK path finder across a tile map * * @author Kevin Glass */ public class DJKPathFinder implements PathFinder { /** The map being searchd */ private TileMap map; /** The tileset describing the tile properties on the map */ private TileSet set; /** The distances from the current destination on the map */ private int[] distance; /** The maximum search depth reached before giving up */ private int maxsearch; /** The starting x coordinate */ private int sx; /** The starting y coordinate */ private int sy; /** The distance from the destination of the best path found so far */ private int bestPath = 10000; /** * Create a new DJK based path finder * * @param map The map being searched * @param set The set describing the tiles on the map */ public DJKPathFinder(TileMap map,TileSet set) { this.map = map; this.set = set; distance = new int[map.getMapWidth()*map.getMapHeight()]; } /** * @see org.newdawn.util.map.PathFinder#reset() */ public void reset() { Arrays.fill(distance,0); } /** * @see org.newdawn.util.map.PathFinder#getSearchData() */ public int[] getSearchData() { return distance; } /** * @see org.newdawn.util.map.PathFinder#findPath(int, int, int, int, int) */ public synchronized Path findPath(int sx,int sy,int dx,int dy,int maxsearch) { int depth = 1; this.sx = sx; this.sy = sy; bestPath = 10000; this.maxsearch = maxsearch; if (processNode(dx,dy,depth)) { Path path = new Path(); findNextPoint(path,sx,sy,distance[sx+(sy*map.getMapWidth())]-1); return path; } else { return null; } } /** * Find the next point on the discovered path by moving * along the distances. After finding the path by DJK we need * to trace back along the distances working out how we got to the * end. * * @param path The path being built up * @param x The current x position * @param y The current y position * @param d The distance we're searching for at the moment. */ private void findNextPoint(Path path,int x,int y,int d) { if (d < 0) { return; } path.addPoint(x,y); for (int xo=-1;xo<2;xo++) { for (int yo=-1;yo<2;yo++) { if ((xo != 0) || (yo != 0)) { if ((xo == 0) || (yo == 0)) { int sx = x+xo; int sy = y+yo; if (distance[sx+(sy*map.getMapWidth())] == d) { findNextPoint(path,sx,sy,distance[sx+(sy*map.getMapWidth())]-1); return; } } } } } for (int xo=-1;xo<2;xo++) { for (int yo=-1;yo<2;yo++) { if ((xo != 0) && (yo != 0)) { int sx = x+xo; int sy = y+yo; if (distance[sx+(sy*map.getMapWidth())] == d) { findNextPoint(path,sx,sy,distance[sx+(sy*map.getMapWidth())]-1); return; } } } } } /** * Traverse the specified graph node (in this case grid cell) * * @param x The x location we're processing * @param y The y location we're processing * @param depth The depth of the search we're at * @return True if we're reached the destination */ private boolean processNode(int x,int y,int depth) { if (depth >= maxsearch) { return false; } if (depth >= bestPath) { return false; } if ((x < 0) || (y < 0) || (x >= map.getMapWidth()) || (y >= map.getMapHeight())) { return false; } if (isBlocked(x,y)) { return false; } int val = distance[x+(y*map.getMapWidth())]; if ((val != 0) && (val <= depth)) { return false; } distance[x+(y*map.getMapWidth())] = depth; if ((x == sx) && (y == sy)) { bestPath = depth; return true; } boolean found = false; for (int xo=-1;xo<2;xo++) { for (int yo=-1;yo<2;yo++) { if ((xo != 0) || (yo != 0)) { if (validMove(x,y,xo,yo)) { found |= processNode(x+xo,y+yo,depth+1); } } } } return found; } /** * Check if the move specified is valid * * @param x The x position we're starting from * @param y The y position we're starting from * @param xo The x direction we're moving in * @param yo The y direction we're moving in * @return True if the move specified is valid */ private boolean validMove(int x,int y,int xo,int yo) { if ((xo == 0) || (yo == 0)) { return true; } if (isBlocked(x+xo,y)) { return false; } if (isBlocked(x,y+yo)) { return false; } return true; } /** * Check if a particular location on the map blocks movement * * @param x The x location to check * @param y The y location to check * @return True if the locaiton is blocked */ public boolean isBlocked(int x,int y) { if (map.isBlocked(x,y)) { return true; } return set.blocksMovement(map.getTileAt(x,y,0)); } }