diff --git a/src/ei/game/algo/AStar.java b/src/ei/game/algo/AStar.java deleted file mode 100644 index caeea0c..0000000 --- a/src/ei/game/algo/AStar.java +++ /dev/null @@ -1,141 +0,0 @@ -package ei.game.algo; - -import java.util.LinkedList; -import java.util.List; -import java.util.Random; - -import ei.engine.math.Vector2i; -import ei.engine.util.MultiPrintStream; -import ei.game.gamestate.InGameState; - -public class AStar{ - private static final long serialVersionUID = 1L; - private AStarPathfinder pathfinder; - - private int width; - private int hight; - private static AStarNode[][] map; - - protected boolean randomizeMap = true; - protected Random RAND = new Random(0); - - public AStar() { - width = InGameState.getMap().getSize().getX(); - hight = InGameState.getMap().getSize().getY(); - initializePathfinder(); - if(map != null){ - reset(); - } - else{ - initializeMap(); - } - } - - public List startSearch(Vector2i start, Vector2i goal){ - //checks that the start and goul are inside the map - if(start.getX() >= map.length) - start.setX(map.length-1); - if(start.getX() < 0) - start.setX(0); - - if(start.getY() >= map[0].length) - start.setY(map[0].length-1); - if(start.getY() < 0) - start.setY(0); - - if(goal.getX() >= map.length) - goal.setX(map.length-1); - if(goal.getX() < 0) - goal.setX(0); - - if(goal.getY() >= map[0].length) - goal.setY(map[0].length-1); - if(goal.getY() < 0) - goal.setY(0); - - map[goal.getX()][goal.getY()].setBlocked(false); - return pathfinder.findPath(map[start.getX()][start.getY()], map[goal.getX()][goal.getY()]); - } - - protected void initializePathfinder() { - System.out.println("Initializing pathfinder"); - - // Create the pathfinder. - pathfinder = new AStarPathfinder(); - } - - protected void reset(){ - for(int y = 0, nodeId = 0; hight > y; y++) { - MultiPrintStream.out.println(""); - for(int x = 0; width > x; x++, nodeId++) { - if(!InGameState.getMap().isPosEmpty(x, y)) { - map[x][y].setBlocked(true); - MultiPrintStream.out.print(1); - } - else{ - MultiPrintStream.out.print(""+0); - map[x][y].setBlocked(false); - } - map[x][y].setVisited(false); - } - } - } - - protected void initializeMap() { - System.out.println("Initializing map"); - // Create the map. - map = new AStarNode2D[width][hight]; - for(int y = 0, nodeId = 0; hight > y; y++) { - MultiPrintStream.out.println(""); - for(int x = 0; width > x; x++, nodeId++) { - map[x][y] = new AStarNode2D(x, y, nodeId); - - if(!InGameState.getMap().isPosEmpty(x, y)) { - map[x][y].setBlocked(true); - MultiPrintStream.out.print(""+1); - } - else MultiPrintStream.out.print(""+0); - } - } - MultiPrintStream.out.println(""); - /* - for(int x = 0, nodeId = 0; width > x; x++) { - for(int y = 0; hight > y; y++, nodeId++) { - map[x][y] = new AStarNode2D(x, y, nodeId); - - if(!InGameState.getMap().isPosEmpty(x, y)) { - map[x][y].setBlocked(true); - } - } - } - */ - - // Create the neighbours. - for(int x = 0; width > x; x++) { - for(int y = 0; hight > y; y++) { - - List neighbours = new LinkedList(); - - if(y-1 >= 0) - neighbours.add(new AStarNeighbour(map[x][y-1], AStarNeighbour.Location.Diagonal)); // North. - if(x+1 < width && y-1 > 0) - neighbours.add(new AStarNeighbour(map[x+1][y-1], AStarNeighbour.Location.Adjacent)); // North-East. - if(x+1 < width) - neighbours.add(new AStarNeighbour(map[x+1][y], AStarNeighbour.Location.Diagonal)); // East. - if(x+1 < width && y+1 < hight) - neighbours.add(new AStarNeighbour(map[x+1][y+1], AStarNeighbour.Location.Adjacent)); // South-East. - if(y+1 < hight) - neighbours.add(new AStarNeighbour(map[x][y+1], AStarNeighbour.Location.Diagonal)); // South. - if(x-1 >= 0 && y+1 < hight) - neighbours.add(new AStarNeighbour(map[x-1][y+1], AStarNeighbour.Location.Adjacent)); // South-West. - if(x-1 >= 0) - neighbours.add(new AStarNeighbour(map[x-1][y], AStarNeighbour.Location.Diagonal)); // West. - if(x-1 >= 0 && y-1 > 0) - neighbours.add(new AStarNeighbour(map[x-1][y-1], AStarNeighbour.Location.Adjacent)); // North-West. - - map[x][y].setNeighbours(neighbours); - - } - } - } -} diff --git a/src/ei/game/algo/AStarNeighbour.java b/src/ei/game/algo/AStarNeighbour.java deleted file mode 100644 index aab10b3..0000000 --- a/src/ei/game/algo/AStarNeighbour.java +++ /dev/null @@ -1,26 +0,0 @@ -package ei.game.algo; - -public class AStarNeighbour { - - private Location location; - private AStarNode node; - - public AStarNeighbour(AStarNode node, Location location) { - this.node = node; - this.location = location; - } - - public AStarNode getNode() { - return node; - } - - public Location getLocation() { - return location; - } - - public enum Location { - Adjacent, - Diagonal - } - -} diff --git a/src/ei/game/algo/AStarNode.java b/src/ei/game/algo/AStarNode.java deleted file mode 100644 index 79e9cb9..0000000 --- a/src/ei/game/algo/AStarNode.java +++ /dev/null @@ -1,167 +0,0 @@ -package ei.game.algo; - -import java.util.List; - -import ei.game.algo.AStarNeighbour.Location; - -/** - * This Node class defines the Node that make up grids, paths etc. - * - * @author Árni Arent - * - */ -public abstract class AStarNode implements Comparable { - - protected int nodeId; - protected List neighbours; - protected AStarNode parent; - protected boolean blocked; - protected boolean visited; - protected int visitOrder; - protected boolean partOfPath; - public float costFromStart; - protected float estimatedCostToGoal; - - /** - * - * @param nodeId - */ - public AStarNode(int nodeId) { - this.nodeId = nodeId; - } - - /** - * - * @return - */ - public AStarNode getParent() { - return parent; - } - - /** - * - * @param parent - */ - public void setParent(AStarNode parent) { - this.parent = parent; - } - - /** - * Returns all the neighbours of this node. - * Usually in grid maps the neighbours are positioned in north, - * north-east, east, south-east, south, south-west, west and north-west. - * - * @return the neighbours of this node. - */ - public List getNeighbours() { - return neighbours; - } - - /** - * Set the neighbours of this Node. - * Usually in grid maps the neighbours are positioned in north, - * north-east, east, south-east, south, south-west, west and north-west. - * - * @param neighbours the neighbours of this Node. - */ - public void setNeighbours(List neighbours) { - this.neighbours = neighbours; - } - - /** - * Gives the id of the Node. The id is a unique integer number - * defining the number of the node. - * Grid maps of size 64x64 have 4096 nodes, so each node has a - * id ranging from 0 to 4095. - * This can usually be thought of as a auto incremental unique - * identifier number, each Node created gets a incremented unique - * id. - * - * @return the id of this Node. - */ - public int getNodeId() { - return nodeId; - } - - - /** - * Tells if this Node is blocked or not. - * - * @return true if the node is blocked, false if not. - */ - public boolean isBlocked() { - return blocked; - } - - /** - * Defined if this Node should be blocked or not. - * - * @param blocked true if it should be blocked, false if not. - */ - public void setBlocked(boolean blocked) { - this.blocked = blocked; - } - - public boolean isVisited() { - return visited; - } - - public void setVisited(boolean visited) { - this.visited = visited; - } - - public void setVisitOrder(int order) { - this.visitOrder = order; - } - - public int getVisitOrder() { - return visitOrder; - } - - - - - public boolean isPartOfPath() { - return partOfPath; - } - - public void setPartOfPath(boolean partOfPath) { - this.partOfPath = partOfPath; - } - - public void reset() { - visited = false; - visitOrder = 0; - parent = null; - partOfPath = false; - estimatedCostToGoal = 0; - } - - @Override - public String toString() { - return "[Node " + nodeId + " ("+(blocked?"X":" ")+")]"; - } - - public float getEstimatedCostFromStartToGoal() { - return costFromStart+estimatedCostToGoal; - } - - public abstract float getEstimatedCostTo(AStarNode node, AStarNode startNode); - - public abstract float getCost(AStarNode node, Location location); - - public int compareTo(Object n) { - float cost = getEstimatedCostFromStartToGoal() - ((AStarNode)n).getEstimatedCostFromStartToGoal(); - - if(cost > 0) - return 1; - else if(cost < 0) - return -1; - else - return 0; - } - - public abstract int getX(); - - public abstract int getY(); -} diff --git a/src/ei/game/algo/AStarNode2D.java b/src/ei/game/algo/AStarNode2D.java deleted file mode 100644 index a853ed8..0000000 --- a/src/ei/game/algo/AStarNode2D.java +++ /dev/null @@ -1,117 +0,0 @@ -package ei.game.algo; - -import ei.game.algo.AStarNeighbour.Location; - -/** - * - * @author Árni Arent - * - */ -public class AStarNode2D extends AStarNode { - protected final static float adjacentCost = 1; - protected final static float diagonalCost = (float)Math.sqrt(2)*adjacentCost; - protected final static float tieBreaker = adjacentCost/(1024f/1024f); - - public enum Heuristic { - Manhattan, - Euclidean, - Diagonal, - DiagonalWithTieBreaking, - DiagonalWithTieBreakingCrossProduct, - } - - public static Heuristic heuristic = Heuristic.Euclidean; - - protected int x, y; - - public AStarNode2D(int x, int y, int nodeId) { - super(nodeId); - - this.x = x; - this.y = y; - } - - public int getX() { - return x; - } - - public int getY() { - return y; - } - - - @Override - public float getEstimatedCostTo(AStarNode node, AStarNode startNode) { - AStarNode2D goal = (AStarNode2D)node; - AStarNode2D start = (AStarNode2D)startNode; - - float h = 0; - - /* - * Manhattan distance heuristics. (no diagonal treatment) - */ - if(heuristic == Heuristic.Manhattan) { - h = adjacentCost * (Math.abs(x-goal.x) + Math.abs(y-goal.y)); - } - - /* - * Euclidean distances (move at any angle, again no diagonal treatment). - */ - else if(heuristic == Heuristic.Euclidean) { - h = adjacentCost * (float)Math.sqrt(Math.pow(x-goal.x, 2) + Math.pow(y-goal.y,2)); - } - - /* - * Diagonal distance heuristic. - */ - else if(heuristic == Heuristic.Diagonal || - heuristic == Heuristic.DiagonalWithTieBreaking || - heuristic == Heuristic.DiagonalWithTieBreakingCrossProduct) { - float diagonal = Math.min(Math.abs(x-goal.x), Math.abs(y-goal.y)); - float straight = (Math.abs(x-goal.x) + Math.abs(y-goal.y)); - h = (diagonalCost * diagonal) + (adjacentCost * (straight - (2*diagonal))); - - - /* - * Normal tie-breaking. - */ - if(heuristic == Heuristic.DiagonalWithTieBreaking) { - h *= (1.0 + tieBreaker); - } - - /* - * Add tie-breaking cross-product to the heuristics. - * (Produces nicer looking diagonal paths, but weird with obstacles) - */ - else if(heuristic == Heuristic.DiagonalWithTieBreakingCrossProduct) { - float dx1 = x - goal.x; - float dy1 = y - goal.y; - float dx2 = start.x - goal.x; - float dy2 = start.y - goal.y; - float cross = Math.abs(dx1*dy2 - dx2*dy1); - h += cross*0.001; - } - } - - // Return the heuristic. - return h; - } - - @Override - public float getCost(AStarNode node, Location location) { - - if(heuristic == Heuristic.Manhattan || heuristic == Heuristic.Euclidean) { - return adjacentCost; - } else { - if(location == Location.Adjacent) { - return adjacentCost; - } else { - return diagonalCost; - } - } - } - - - - -} diff --git a/src/ei/game/algo/AStarPathFinder.java b/src/ei/game/algo/AStarPathFinder.java new file mode 100644 index 0000000..bc7bbc9 --- /dev/null +++ b/src/ei/game/algo/AStarPathFinder.java @@ -0,0 +1,294 @@ +package ei.game.algo; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; + +/** + * A* path finder across a tile map + * + * @author Kevin Glass + */ +public strictfp class AStarPathFinder implements PathFinder { + /** The map being searched */ + private TileMap map; + /** The set describing the properties of the tiles on the map */ + private TileSet set; + /** The distance from the end point each point on the map is */ + private int[] distance; + /** The maximum search depth before giving up */ + private int maxsearch; + /** The starting x coordinate */ + private int sx; + /** The starting y coordiante */ + private int sy; + /** The callback notified as nodes are processed */ + private PathFinderCallback callback; + /** The currently open nodes */ + private ArrayList open = new ArrayList(); + + /** + * Create a new path finder based on the A* algorithm + * + * @param map The map being searched + * @param set The set describing the tiles on the map + */ + public AStarPathFinder(TileMap map,TileSet set) { + this(map,set,null); + } + + /** + * Create a new path finder based on the A* algorithm + * + * @param map The map being searched + * @param set The set describing the tiles on the map + * @param callback The callback notified as nodes are traversed + */ + public AStarPathFinder(TileMap map,TileSet set,PathFinderCallback callback) { + this.map = map; + this.set = set; + this.callback = callback; + distance = new int[map.getMapWidth()*map.getMapHeight()]; + } + + /** + * @see org.newdawn.util.map.PathFinder#reset() + */ + public void reset() { + open.clear(); + 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) { + this.sx = sx; + this.sy = sy; + + this.maxsearch = maxsearch; + + Step step = new Step(null,dx,dy); + open.add(step); + + return processNodes(); + } + + /** + * Process the nodes of the search graph + * + * @return The path found or null if no path could be found + */ + private Path processNodes() { + Step step = findBest(); + + while (!step.is(sx,sy)) { + for (int x=-1;x<2;x++) { + for (int y=-1;y<2;y++) { + if ((x != 0) || (y != 0)) { + int xp = step.x + x; + int yp = step.y + y; + + if (!checkDiaganolBlock(step.x,step.y,x,y)) { + if (validNode(xp,yp)) { + open.add(new Step(step,xp,yp)); + } + } + } + } + } + + step = findBest(); + if (step == null) { + return null; + } + + if (callback != null) { + distance[step.x+(step.y*map.getMapWidth())] = 1; + callback.fireNode(step.x,step.y); + } + } + + Path path = new Path(); + + while (step.parent != null) { + path.addPoint(step.x,step.y); + step = step.parent; + } + path.addPoint(step.x,step.y); + + System.out.println(); + for(int y=0; y 9) || (best.depth > maxsearch)) { + open.remove(best); + return findBest(); + } + + return best; + } + + /** + * Evaluate the heuristic for a particular node + * + * @param x The x position of the node to evaluate + * @param y The y position of the node to evaluate + * @return The heuristic for the specified node + */ + @SuppressWarnings("unused") + private int evalH(int x,int y) { + return Math.abs(sx-x) + Math.abs(sy-y); + } + + /** + * A step on the search path + * + * @author Kevin Glass + */ + private class Step implements Comparable { + /** The x position of this step */ + public int x; + /** The y position of this step */ + public int y; + /** The heuristic for this step's node */ + public int h; + /** The number of times this step has been used as a source */ + public int count; + /** The depth of this search */ + public int depth; + /** The parent step this step was spawned from */ + public Step parent; + + /** + * Create a new step + * + * @param parent The step we came from + * @param x The x position we've moved to + * @param y The y position we've moved to + */ + public Step(Step parent,int x,int y) { + this.x = x; + this.y = y; + this.parent = parent; + if (parent != null) { + depth = parent.depth+1; + } else { + depth = 0; + } + + h = Math.abs(sx-x) + Math.abs(sy-y); + + if (depth <= maxsearch) { + distance[x+(y*map.getMapWidth())] = 2; + } + } + + /** + * Like an equals method, only for the data inside + * + * @param x The x position to check against + * @param y The y position to check against + * @return True if this steps position is that specified + */ + public boolean is(int x,int y) { + return (x == this.x) && (y == this.y); + } + + /** + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + public int compareTo(Object o) { + return h - ((Step) o).h; + } + } + + + /** + * 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(x == sx && y == sy){ + return false; + } + if (map.isBlocked(x,y)) { + return true; + } + + return set.blocksMovement(map.getTileAt(x,y,0)); + } +} diff --git a/src/ei/game/algo/DJKPathFinder.java b/src/ei/game/algo/DJKPathFinder.java new file mode 100644 index 0000000..d224d17 --- /dev/null +++ b/src/ei/game/algo/DJKPathFinder.java @@ -0,0 +1,211 @@ +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)); + } +} diff --git a/src/ei/game/algo/Path.java b/src/ei/game/algo/Path.java new file mode 100644 index 0000000..7259bcd --- /dev/null +++ b/src/ei/game/algo/Path.java @@ -0,0 +1,88 @@ +package ei.game.algo; + +import java.util.ArrayList; +import java.util.LinkedList; + +import ei.engine.math.Vector2i; + +/** + * A path from one point to another generated by the path finding code + * + * @author Kevin Glass + */ +public class Path { + /** The maximum path length */ + private static final int MAX_PATH = 200; + /** The path coordinates */ + private int[][] path = new int[MAX_PATH][2]; + /** The size of the path */ + private int size; + + /** + * Create a new empty path + */ + public Path() { + } + + /** + * Add a point to the path + * + * @param x The x coordinate of the point being added + * @param y The y coordinate of the point being added + */ + public void addPoint(int x,int y) { + path[size][0] = x; + path[size][1] = y; + + size++; + } + + /** + * Get the X coordinate of the point at the specified index + * + * @param index The index of the point to retrieve + * @return The x coordinate of the point at the specified index + */ + public int getX(int index) { + return path[index][0]; + } + + /** + * Get the Y coordinate of the point at the specified index + * + * @param index The index of the point to retrieve + * @return The y coordinate of the point at the specified index + */ + public int getY(int index) { + return path[index][1]; + } + + /** + * Get the size/length of the path + * + * @return The size of the path + */ + public int getSize() { + return size; + } + + public ArrayList getArray(){ + ArrayList array = new ArrayList(); + + for(int i=0; i getList(){ + LinkedList array = new LinkedList(); + + for(int i=0; ireset(). + * + * This data is probably only useful for test/debug tools. + * + * @return An array of distances from the end point of the last search + * for different points on the map. + */ + public abstract int[] getSearchData(); + + /** + * Find a path from a starting location to a destination point. + * + * @param sx The starting x coordinate + * @param sy The starting y coordinate + * @param dx The destination x coordinate + * @param dy The destination y coordinate + * @param maxsearch The maximum search depth that will be reached + * during the search. This is helpful for restricting the amount of + * time a search may take + * @return The path found or null if no path could be determined + */ + public abstract Path findPath(int sx, int sy, int dx, int dy, int maxsearch); +} \ No newline at end of file diff --git a/src/ei/game/algo/PathFinderCallback.java b/src/ei/game/algo/PathFinderCallback.java new file mode 100644 index 0000000..dba4db9 --- /dev/null +++ b/src/ei/game/algo/PathFinderCallback.java @@ -0,0 +1,17 @@ +package ei.game.algo; + +/** + * A simple interface for testing path finding. This allows you + * to watch the nodes being searched by the finder + * + * @author Kevin Glass + */ +public interface PathFinderCallback { + /** + * Notification that a particular node has been reached + * + * @param x The x coordinate of the node reached + * @param y The y coordinate of the node reached + */ + public void fireNode(int x,int y); +} diff --git a/src/ei/game/algo/TestPathFinder.java b/src/ei/game/algo/TestPathFinder.java new file mode 100644 index 0000000..fc881cb --- /dev/null +++ b/src/ei/game/algo/TestPathFinder.java @@ -0,0 +1,243 @@ +package ei.game.algo; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +/** + * A test for the different path finding implementations defined + * + * Note: Like all the tests this isn't designed to be a robust piece + * of software, rather a testing tool. + * + * @author Kevin Glass + */ +public class TestPathFinder extends JPanel implements TileSet, TileMap, PathFinderCallback { + private static final long serialVersionUID = 1L; + + /** The depth of search allowed */ + private static final int SEARCH_DEPTH = 500; + + /** The map being searched */ + private int[][] map = new int[100][100]; + /** The current start x coordinate */ + private int sx = 5; + /** The current start y coordinate */ + private int sy = 5; + /** The current end x coordinate */ + private int dx = 5; + /** The current end y coordinate */ + private int dy = 5; + /** The finder searching the map */ + private PathFinder finder; + /** The last path found */ + private Path path; + + /** + * Create a test for path finding + */ + public TestPathFinder() { + generateData(map); + + finder = new AStarPathFinder(this,this,this); + //finder = new DJKPathFinder(this,this); + + JFrame frame = new JFrame(); + frame.setContentPane(new JScrollPane(this)); + + addMouseListener(new MouseHandler()); + setPreferredSize(new Dimension(1000,1000)); + + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + + frame.setSize(800,600); + frame.setVisible(true); + } + + /** + * Generate a random map for testing against, worst + * alogrithm ever? + * + * @param data The data array to generate the map into + */ + private void generateData(int[][] data) { + for (int x=0;x<100;x++) { + for (int y=0;y<100;y++) { + data[x][y] = 1; + } + } + + int count = (int) (Math.random() * 110) + 40; + for (int i=0;i 0) && (y > 0) && (x < 99) && (y < 99)) { + data[x][y] = 0; + } + } + } + data[2][1] = 1; + data[1][1] = 0; + data[1][2] = 0; + data[1][3] = 0; + data[2][3] = 0; + data[2][4] = 0; + data[2][5] = 0; + data[2][6] = 0; + } + } + + /** + * @see java.awt.Component#paint(java.awt.Graphics) + */ + public void paint(Graphics g) { + g.setColor(Color.black); + g.fillRect(0,0,1000,1000); + + for (int x=0;x<100;x++) { + for (int y=0;y<100;y++) { + + if (isBlocked(x,y)) { + g.setColor(Color.white); + g.fillRect(x*10,y*10,10,10); + } + } + } + + if (finder.getSearchData() != null) { + int[] data = finder.getSearchData(); + + for (int x=0;x<100;x++) { + for (int y=0;y<100;y++) { + if (!isBlocked(x,y)) { + if (data[x+(y*100)] != 0) { + g.setColor(Color.blue); + g.fillRect(x*10,y*10,10,10); + } + } + } + } + } + + if (path != null) { + for (int i=0;i= width || y < 0 || y >= hight){ + return true; + } + return false; + } + + public boolean blocksMovement(int tile) { + return tile != 0; + } + + public void fireNode(int x, int y) { + //System.out.println(x+" "+y+" "+getTileAt(x,y,0)+" "+blocksMovement(getTileAt(x,y,0))); + } } diff --git a/src/ei/game/scene/units/Unit.java b/src/ei/game/scene/units/Unit.java index 7bc8a7d..3483afc 100644 --- a/src/ei/game/scene/units/Unit.java +++ b/src/ei/game/scene/units/Unit.java @@ -7,8 +7,9 @@ import ei.engine.math.Vector2i; import ei.engine.math.Vector3f; import ei.engine.scene.Node; import ei.engine.sound.Sound; -import ei.game.algo.AStar; -import ei.game.algo.AStarNode; +import ei.game.algo.AStarPathFinder; +import ei.game.algo.Path; +import ei.game.algo.PathFinder; import ei.game.gamestate.InGameState; import ei.game.player.Player; import ei.game.scene.GameEntity; @@ -38,7 +39,9 @@ public abstract class Unit extends GameEntity{ private int weponTimer; private int autoAttackTimer; // The path to travel - private LinkedList path; + private LinkedList path; + /** The finder searching the map */ + private static PathFinder pathfinder; private Vector2i target; private boolean autoAttack; @@ -53,6 +56,9 @@ public abstract class Unit extends GameEntity{ unitNode.setLocation(Map.getPixelByPos(pos.getX(), pos.getY())); setPos(pos.getX(), pos.getY()); autoAttack = true; + if(pathfinder == null){ + pathfinder = new AStarPathFinder(InGameState.getMap(),InGameState.getMap(),InGameState.getMap()); + } init(); initGraphics(); } @@ -191,10 +197,15 @@ public abstract class Unit extends GameEntity{ if(b){ attack = null; } - path = (LinkedList) new AStar().startSearch(oldPos,new Vector2i(x,y)); + + System.out.println(oldPos.getX()+" "+oldPos.getY()+"-"+x+" "+y); + Path p = pathfinder.findPath(oldPos.getX(),oldPos.getY(),x,y,600); + pathfinder.reset(); + if(p != null) path = p.getList(); + else path = null; if(path != null && !path.isEmpty() && moveTo == null){ - AStarNode temp = path.poll(); + Vector2i temp = path.poll(); oldVect = new Vector2i((int)unitNode.getLocation().getX(), (int)unitNode.getLocation().getY()); moveTo = Map.getPixelByPos(temp.getX(), temp.getY()); setPos(temp.getX(), temp.getY()); @@ -317,7 +328,7 @@ public abstract class Unit extends GameEntity{ moveTo = null; } else if(path != null && !path.isEmpty()){ - AStarNode temp = path.poll(); + Vector2i temp = path.poll(); if(InGameState.getMap().isPosEmpty(temp.getX(), temp.getY())){ oldVect = new Vector2i((int)moveTo.getX(), (int)moveTo.getY()); moveTo = Map.getPixelByPos(temp.getX(), temp.getY());