211 lines
4.6 KiB
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));
|
|
}
|
|
}
|