evil-inside/src/ei/game/algo/DJKPathFinder.java

211 lines
4.6 KiB
Java

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));
}
}