Related
I'm creating a 2d top down game where the enemy AI is constantly following the player and avoiding obstacles. I did some research about path-finding algorithms and I decided to implement the breadth first search, but for some reason, the xy coordinates of the path are reversed, even though the grid is correct.
Code:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BFSTest {
// 1 = normal node
// 0 = obstacle
// S = start
// D = destination
private static char[][] nodes = {
{'S', '1', '1', '1'},
{'0', '0', '0', '1'},
{'0', '0', '0', '1'},
{'1', '1', '1', 'D'}
};
public static void main(String[] args) {
shortestPath();
}
public static List<Node> shortestPath() {
// key node, value parent
Map<Node, Node> parents = new HashMap<Node, Node>();
Node start = null;
Node end = null;
// find the start node
for (int row = 0; row < nodes.length; row++) {
for (int column = 0; column < nodes[row].length; column++) {
if (nodes[row][column] == 'S') {
start = new Node(row, column, nodes[row][column]);
break;
}
}
}
if (start == null) {
throw new RuntimeException("can't find start node");
}
// traverse every node using breadth first search until reaching the destination
List<Node> temp = new ArrayList<Node>();
temp.add(start);
parents.put(start, null);
boolean reachDestination = false;
while (temp.size() > 0 && !reachDestination) {
Node currentNode = temp.remove(0);
List<Node> children = getChildren(currentNode);
for (Node child : children) {
// Node can only be visited once
if (!parents.containsKey(child)) {
parents.put(child, currentNode);
char value = child.getValue();
if (value == '1') {
temp.add(child);
} else if (value == 'D') {
temp.add(child);
reachDestination = true;
end = child;
break;
}
}
}
}
if (end == null) {
throw new RuntimeException("can't find end node");
}
// get the shortest path
Node node = end;
List<Node> path = new ArrayList<Node>();
while (node != null) {
path.add(0, node);
node = parents.get(node);
}
printPath(path);
return path;
}
private static List<Node> getChildren(Node parent) {
List<Node> children = new ArrayList<Node>();
int x = parent.getX();
int y = parent.getY();
if (x - 1 >= 0) {
Node child = new Node(x - 1, y, nodes[x - 1][y]);
children.add(child);
}
if (y - 1 >= 0) {
Node child = new Node(x, y - 1, nodes[x][y - 1]);
children.add(child);
}
if (x + 1 < nodes.length) {
Node child = new Node(x + 1, y, nodes[x + 1][y]);
children.add(child);
}
if (y + 1 < nodes[0].length) {
Node child = new Node(x, y + 1, nodes[x][y + 1]);
children.add(child);
}
return children;
}
private static void printPath(List<Node> path) {
for (int row = 0; row < nodes.length; row++) {
for (int column = 0; column < nodes[row].length; column++) {
String value = nodes[row][column] + "";
// mark path with X
for (int i = 1; i < path.size() - 1; i++) {
Node node = path.get(i);
if (node.getX() == row && node.getY() == column) {
value = "X";
break;
}
}
if (column == nodes[row].length - 1) {
System.out.println(value);
} else {
System.out.print(value + " ");
}
}
}
System.out.println("Path: " + path);
}
}
class Node {
private int x;
private int y;
private char value;
public Node(int x, int y, char value) {
this.x = x;
this.y = y;
this.value = value;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public char getValue() {
return value;
}
#Override
public String toString() {
return "(x: " + x + " y: " + y + ")";
}
#Override
public int hashCode() {
return x * y;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if (this.getClass() != o.getClass()) return false;
Node node = (Node) o;
return x == node.x && y == node.y;
}
/* Output:
* S X X X
* 0 0 0 X
* 0 0 0 X
* 1 1 1 D
* Path: [(x: 0 y: 0), (x: 0 y: 1), (x: 0 y: 2), (x: 0 y: 3), (x: 1 y: 3), (x: 2 y: 3), (x: 3 y: 3)]
*/
}
Thanks!
Think about this for a moment. Remember that your first array index is row, and the second is column. Now, in terms of conventional x and y:
the y index always refers to the row (because you count rows vertically);
the x index always refers to the column (because you count columns horizontally).
So, the correct way to index your grid is nodes[y][x]
I have the below code for a A* pathfinder, however it is taking upwards of 10 minutes to find a solution using a simple 1024 x 1024 array.
I had to comment out //Collections.sort(this.openList); as it was throwing a comparison method violates its general contract! error when running.
Is the algorithm correct and any idea why the bottleneck? Some people using C++ are getting a response time of 40ms, not 10+ mins!
When using the maze array it does it in the blink of an eye, but thats using something like a 14x10 array, rather than 1024 from the collisionMap.
Is the method flawed in some way? Or using the wrong data structures?
import java.util.List;
import javax.imageio.ImageIO;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.GraphicsConfiguration;
import java.awt.Paint;
import java.awt.image.BufferedImage;
import java.io.Console;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
class AStarTwo {
// Closed list, open list and calculatedPath lists
private final List<Node> openList;
private final List<Node> closedList;
private final List<Node> calcPath;
// Collision Map to store tha map in
private final int[][] collisionMap;
// Current node the program is executing
private Node currentNode;
// Define the start and end coords
private final int xstart;
private final int ystart;
private int xEnd, yEnd;
// Node class
static class Node implements Comparable {
public Node parent;
public int x, y;
public double g;
public double h;
Node(Node parent, int xpos, int ypos, double g, double h) {
this.parent = parent;
this.x = xpos;
this.y = ypos;
this.g = g;
this.h = h;
}
// Compare f value (g + h)
#Override
public int compareTo(Object o) {
Node that = (Node) o;
return (int)((this.g + this.h) - (that.g + that.h));
}
}
// construct and initialise
public AStarTwo(int[][] collisionMap, int xstart, int ystart) {
this.openList = new ArrayList<>();
this.closedList = new ArrayList<>();
this.calcPath = new ArrayList<>();
this.collisionMap = collisionMap;
this.currentNode = new Node(null, xstart, ystart, 0, 0);
this.xstart = xstart;
this.ystart = ystart;
}
// returns a List<> of nodes to target
public List<Node> findPathTo(int xTo, int yTo) {
this.xEnd = xTo;
this.yEnd = yTo;
// Add this to the closed list
this.closedList.add(this.currentNode);
// Add neighbours to openList for iteration
addNeigborsToOpenList();
// Whilst not at our target
while (this.currentNode.x != this.xEnd || this.currentNode.y != this.yEnd) {
// If nothing in the open list then return with null - handled in error message in main calling func
if (this.openList.isEmpty()) {
return null;
}
// get the lowest f value and add it to the closed list, f calculated when neighbours are sorted
this.currentNode = this.openList.get(0);
this.openList.remove(0);
this.closedList.add(this.currentNode);
addNeigborsToOpenList();
}
// add this node to the calculated path
this.calcPath.add(0, this.currentNode);
while (this.currentNode.x != this.xstart || this.currentNode.y != this.ystart) {
this.currentNode = this.currentNode.parent;
this.calcPath.add(0, this.currentNode);
}
return this.calcPath;
}
// Searches the current list for neighbouring nodes returns bool
private static boolean checkNeighbourHasBeenSearched(List<Node> array, Node node) {
return array.stream().anyMatch((n) -> (n.x == node.x && n.y == node.y));
}
// Calculate distance from current node to the target
private double distance(int dx, int dy) {
return Math.hypot(this.currentNode.x + dx - this.xEnd, this.currentNode.y + dy - this.yEnd); // return hypothenuse
}
// Add neighbouring nodes to the open list to iterate through next
#SuppressWarnings("unchecked")
private void addNeigborsToOpenList() {
Node node;
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
node = new Node(this.currentNode, this.currentNode.x + x, this.currentNode.y + y, this.currentNode.g, this.distance(x, y));
// if we are not on the current node
if ((x != 0 || y != 0)
&& this.currentNode.x + x >= 0 && this.currentNode.x + x < this.collisionMap[0].length // check collision map boundaries
&& this.currentNode.y + y >= 0 && this.currentNode.y + y < this.collisionMap.length
&& this.collisionMap[this.currentNode.y + y][this.currentNode.x + x] != -1) { // check if tile is walkable (-1)
// and finally check we haven't already searched the nodes
if(!checkNeighbourHasBeenSearched(this.openList, node) && !checkNeighbourHasBeenSearched(this.closedList, node)){
node.g = node.parent.g + 1.; // Horizontal/vertical cost = 1.0
node.g += collisionMap[this.currentNode.y + y][this.currentNode.x + x]; // add movement cost for this square
// Add diagonal movement cost sqrt(hor_cost² + vert_cost²) + 0.4
if (x != 0 && y != 0) {
node.g += .4;
}
// Add the node to the List<>
this.openList.add(node);
}
}
}
}
// sort in ascending order
//Collections.sort(this.openList);
}
public static void main(String[] args) {
int [][] maze =
{ {1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,-1,0,-1,0,1,0,0,0,0,0,1},
{1,0,-1,0,0,0,1,0,1,1,1,0,1},
{1,0,0,0,-1,-1,-1,0,0,0,0,0,1},
{1,0,1,0,0,0,0,0,1,1,1,0,1},
{1,0,1,0,-1,-1,-1,0,1,0,0,0,-1},
{1,0,-1,0,-1,0,0,0,-1,-1,-1,0,-1},
{1,0,1,0,-1,-1,-1,0,1,0,-1,0,-1},
{1,0,0,0,0,0,0,0,0,0,1,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1}
};
// Define the size of the grid
final int sizeOf = 20;
int[][] collisionMap = new int[sizeOf][];
for(int i=0;i < sizeOf; i++) {
// -1 = blocked
// 0+ = cost
collisionMap[i] = new int[sizeOf];
}
// set the value of the nodes
for (int k = 0; k < sizeOf; k++) {
for (int j = 0; j < sizeOf; j++) {
if(j == 30 && k < 100) {
collisionMap[k][j] = -1;
} else if (j == 50 && k > 230) {
collisionMap[k][j] = -1;
}else {
collisionMap[k][j] = 0;
}
}
}
AStarTwo as = new AStarTwo(maze, 9, 9);
List<Node> path = as.findPathTo(0,0);
if(path == null) {
System.out.println("Unable to reach target");
}
// create image buffer to write output to
BufferedImage img = new BufferedImage(sizeOf, sizeOf, BufferedImage.TYPE_INT_RGB);
// Set colours
int r = 255;
int g = 0;
int b = 0;
int colRed = (r << 16) | (g << 8) | b;
r = 0;
g = 255;
b = 0;
int colGreen = (r << 16) | (g << 8) | b;
r = 0;
g = 0;
b = 255;
int colBlue = (r << 16) | (g << 8) | b;
r = 255;
g = 255;
b = 255;
int colWhite = (r << 16) | (g << 8) | b;
int i = 0;
int j = 0;
if (path != null) {
path.forEach((n) -> {
System.out.print("[" + n.x + ", " + n.y + "] ");
maze[n.y][n.x] = 2;
});
for (int[] maze_row : maze) {
for (int maze_entry : maze_row) {
switch (maze_entry) {
// normal tile
case 0:
img.setRGB(j, i, colWhite);
break;
// final path
case 2:
img.setRGB(j, i, colBlue);
break;
// Object to avoid
case -1:
img.setRGB(j, i, colRed);
break;
// Any other value
default:
img.setRGB(j, i, colWhite);
}
j++;
}
// count j - reset as if it were a for loop
if(i != 12) {
j=0;
}
i++;
System.out.println();
}
}
// output file
File f = new File("aStarPath.png");
try {
ImageIO.write(img, "PNG", f);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("i: " + i + ", j: " + j);
}
}
I suspect your problem is this line:
return array.stream().anyMatch((n) -> (n.x == node.x && n.y == node.y));
which is called around O(n^2) times and will take time proportional to the size of the array (which will also be O(n^2) in the worst case for a n by n maze).
You want a faster method of performing this test.
For example:
Use a set to hold the open and closed lists instead of list
Or use an extra field in the node structure to indicate if it is in the open or closed list
Background: I am making a 2D moba game and i need pathfind for all the monsters in the game. I want to give a startPos and a endPos and have the monster travel there avoiding objects.
Question:
I have been trying to implement path finding into my game for awhile now and i just cant see to get it working. All i want is some method/class where i can give it a 2d array of values (ie. true = occupied & false = free), startPos, endPos and it gives me a list of moves to get to the end. All my implementations have failed thus far. Can anyone help by giving me code that is easy to implement?
Note:
So far i have tried implementing A and it either ignored walls or sent the character into a completely random direction.
*I did get it working but in a ugly and wrong way. I had the charater move forward until it hit and wall. Then it turned right and kept moving until it could turn left and continue towards the destination. This works but i dont think people want their teams monsters running around on walls
Edit:
Code below is now working! I found that for some reason the points were backwards so i had to invert the Point list. No all i need to do is interpolate between points to give smooth movement. However, I do ask is there any way i cant add more bias towards walls. For example making it so the point never goes within 1 unit of a wall?
package NavMesh;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import toolbox.Maths;
public class MovementPath {
private Node[][] mapOriginal;
private Node[][] mapPath;
public boolean solving = true;
public int startX, startY, finishX, finishY, cells;
private int checks = 0;
private int length = 0;
int realStartX, realStartY, realFinishX, realFinishY;
NavMesh mesh;
private Algorithm alg;
List<Point> path = new ArrayList<Point>();
public MovementPath(NavMesh mesh,int startX, int startY, int finishX, int finishY) {
this.mapOriginal = mesh.getMapCopy();
this.mesh = mesh;
this.startX = startX;
this.startY = startY;
this.finishX = finishX;
this.finishY = finishY;
this.cells = mapOriginal.length;
realStartX = startX;
realStartY = startY;
realFinishX = finishX;
realFinishY = finishY;
this.startX = (int) (Math.floor((float) startX / (float) mesh.cellWidth));
this.startY = (int) (Math.floor((float) startY / (float) mesh.cellHeight));
this.finishX = (int) (Math.floor((float) finishX / (float) mesh.cellWidth));
this.finishY = (int) (Math.floor((float) finishY / (float) mesh.cellHeight));
mapPath = new Node[mapOriginal.length][mapOriginal.length];
System.arraycopy(mapOriginal, 0, mapPath, 0, mapOriginal.length);
mapPath[this.startX][this.startY] = new Node(0,this.startX,this.startY);;
mapPath[this.finishX][this.finishY] = new Node(1,this.finishX,this.finishY);
addPointCentered(realFinishX,realFinishY);
alg = new Algorithm();
//alg.AStar();
alg.Dijkstra();
addPointCentered(realStartX,realStartY);
mesh.drawMap(Integer.toString(Maths.randomRange(0, 1000)), mapPath);
}
public Path getPath(){
//System.out.println("Returning path with " + getPathPoints().size() + " points");
return new Path(getPathPoints());
}
private void addPointCentered(int x, int y) {
path.add(new Point(x+(mesh.cellWidth/2),y+(mesh.cellHeight/2)));
}
public List<Point> getPathPoints(){
List<Point> rPath = new ArrayList<Point>();
for(int i = path.size()-1; i >= 0; i--) {
rPath.add(path.get(i));
}
return rPath;
}
class Algorithm { //ALGORITHM CLASS
//A STAR WORKS ESSENTIALLY THE SAME AS DIJKSTRA CREATING A PRIORITY QUE AND PROPAGATING OUTWARDS UNTIL IT FINDS THE END
//HOWEVER ASTAR BUILDS IN A HEURISTIC OF DISTANCE FROM ANY NODE TO THE FINISH
//THIS MEANS THAT NODES THAT ARE CLOSER TO THE FINISH WILL BE EXPLORED FIRST
//THIS HEURISTIC IS BUILT IN BY SORTING THE QUE ACCORDING TO HOPS PLUS DISTANCE UNTIL THE FINISH
public void AStar() {
ArrayList<Node> priority = new ArrayList<Node>();
priority.add(mapPath[startX][startY]);
while(solving) {
if(priority.size() <= 0) {
solving = false;
break;
}
int hops = priority.get(0).getHops()+1;
ArrayList<Node> explored = exploreNeighbors(priority.get(0),hops);
if(explored.size() > 0) {
priority.remove(0);
priority.addAll(explored);
} else {
priority.remove(0);
}
sortQue(priority); //SORT THE PRIORITY QUE
}
}
public void Dijkstra() {
ArrayList<Node> priority = new ArrayList<Node>(); //CREATE A PRIORITY QUE
priority.add(mapPath[startX][startY]); //ADD THE START TO THE QUE
while(solving) {
if(priority.size() <= 0) { //IF THE QUE IS 0 THEN NO PATH CAN BE FOUND
solving = false;
break;
}
int hops = priority.get(0).getHops()+1; //INCREMENT THE HOPS VARIABLE
ArrayList<Node> explored = exploreNeighbors(priority.get(0), hops); //CREATE AN ARRAYLIST OF NODES THAT WERE EXPLORED
if(explored.size() > 0) {
priority.remove(0); //REMOVE THE NODE FROM THE QUE
priority.addAll(explored); //ADD ALL THE NEW NODES TO THE QUE
} else { //IF NO NODES WERE EXPLORED THEN JUST REMOVE THE NODE FROM THE QUE
priority.remove(0);
}
}
}
public ArrayList<Node> sortQue(ArrayList<Node> sort) { //SORT PRIORITY QUE
int c = 0;
while(c < sort.size()) {
int sm = c;
for(int i = c+1; i < sort.size(); i++) {
if(sort.get(i).getEuclidDist(finishX,finishY)+sort.get(i).getHops() < sort.get(sm).getEuclidDist(finishX,finishY)+sort.get(sm).getHops())
sm = i;
}
if(c != sm) {
Node temp = sort.get(c);
sort.set(c, sort.get(sm));
sort.set(sm, temp);
}
c++;
}
return sort;
}
/*
public ArrayList<Node> exploreNeighbors(Node current, int hops) { //EXPLORE NEIGHBORS
ArrayList<Node> explored = new ArrayList<Node>(); //LIST OF NODES THAT HAVE BEEN EXPLORED
for(int a = -1; a <= 1; a++) {
for(int b = -1; b <= 1; b++) {
int xbound = current.getX()+a;
int ybound = current.getY()+b;
if((xbound > -1 && xbound < cells) && (ybound > -1 && ybound < cells)) { //MAKES SURE THE NODE IS NOT OUTSIDE THE GRID
Node neighbor = mapPath[xbound][ybound];
if((neighbor.getHops()==-1 || neighbor.getHops() > hops) && neighbor.getType()!=2) { //CHECKS IF THE NODE IS NOT A WALL AND THAT IT HAS NOT BEEN EXPLORED
explore(neighbor, current.getX(), current.getY(), hops); //EXPLORE THE NODE
explored.add(neighbor); //ADD THE NODE TO THE LIST
}
}
}
}
return explored;
}
*/
public ArrayList<Node> exploreNeighbors(Node current, int hops) { //EXPLORE NEIGHBORS
ArrayList<Node> explored = new ArrayList<Node>(); //LIST OF NODES THAT HAVE BEEN EXPLORED
//test(hops, current, explored,current.getX(),current.getY());
//test(hops, current, explored,current.getX()+1,current.getY());
//test(hops, current, explored,current.getX()-1,current.getY());
//test(hops, current, explored,current.getX(),current.getY()+1);
//test(hops, current, explored,current.getX(),current.getY()-1);
for(int a = -1; a <= 1; a++) {
for(int b = -1; b <= 1; b++) {
test(hops, current, explored,current.getX()+a,current.getY()+b);
}
}
return explored;
}
private void test(int hops, Node current, ArrayList<Node> explored, int xbound, int ybound) {
if((xbound > -1 && xbound < cells) && (ybound > -1 && ybound < cells)) { //MAKES SURE THE NODE IS NOT OUTSIDE THE GRID
Node neighbor = mapPath[xbound][ybound];
if((neighbor.getHops()==-1 || neighbor.getHops() > hops) && neighbor.getType()!=2) { //CHECKS IF THE NODE IS NOT A WALL AND THAT IT HAS NOT BEEN EXPLORED
explore(neighbor, current.getX(), current.getY(), hops); //EXPLORE THE NODE
explored.add(neighbor); //ADD THE NODE TO THE LIST
}
}
}
public void explore(Node current, int lastx, int lasty, int hops) { //EXPLORE A NODE
if(current.getType()!=0 && current.getType() != 1) //CHECK THAT THE NODE IS NOT THE START OR FINISH
current.setType(4); //SET IT TO EXPLORED
current.setLastNode(lastx, lasty); //KEEP TRACK OF THE NODE THAT THIS NODE IS EXPLORED FROM
current.setHops(hops); //SET THE HOPS FROM THE START
checks++;
if(current.getType() == 1) { //IF THE NODE IS THE FINISH THEN BACKTRACK TO GET THE PATH
backtrack(current.getLastX(), current.getLastY(),hops);
}
}
public void backtrack(int lx, int ly, int hops) { //BACKTRACK
length = hops;
while(hops > 1) { //BACKTRACK FROM THE END OF THE PATH TO THE START
Node current = mapPath[lx][ly];
current.setType(5);
addPointCentered(lx*mesh.cellWidth,ly*mesh.cellHeight);
//System.out.println("New Point: " + path.get(path.size()-1).toString());
lx = current.getLastX();
ly = current.getLastY();
hops--;
}
solving = false;
}
}
}
Try A*, I used that for a path finding problem. It is easy to implement for grid based movement and very fast. I implemented it using the pseudocode on the wikipedia page.
So I am trying to construct a 3D KD Tree from a list of randomly generated points. I am trying to accomplish this task recursively as well. But in my recursion I am facing an error when I'm trying to partition my list of points. My code is as follows:
public class Scratch {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
ArrayList<Point> points = new ArrayList<Point>();
Random rand = new Random(System.currentTimeMillis());
for (int i = 0; i < 100; i++) {
double x = rand.nextInt(100);
double y = rand.nextInt(100);
double z = rand.nextInt(100);
Point point = new Point(x, y, z);
points.add(point);
}
Node root = kdtree(points, 0);
System.out.println("Done");
}
static class Node {
Node leftChild;
Node rightChild;
Point location;
Node() {
}
Node(Node leftChild, Node rightChild, Point location) {
this.leftChild = leftChild;
this.rightChild = rightChild;
this.location = location;
}
}
public static Node kdtree(ArrayList<Point> points, int depth) {
int axis = depth % 3;
switch (axis) {
case 0:
Collections.sort(points, Point.X_COMPARATOR);
break;
case 1:
Collections.sort(points, Point.Y_COMPARATOR);
break;
case 2:
Collections.sort(points, Point.Z_COMPARATOR);
break;
default:
break;
}
int middle = points.size() / 2;
Point median = points.get(middle);
ArrayList<Point> greater = new ArrayList<Point>(points.subList(0, (middle - 1)));
ArrayList<Point> lesser = new ArrayList<Point>(points.subList((middle + 1), (points.size())));
Node node = new Node();
node.location = median;
if(greater.size() == 0 || lesser.size() == 0) {
node.leftChild = null;
node.rightChild = null;
} else {
node.leftChild = kdtree(lesser, depth + 1);
node.rightChild = kdtree(greater, depth + 1);
}
return node;
}
}
The class point is a simple object which contains an x, y and z coordinate. And three comparators used based on the depth of the tree.
The error I am getting is as follows:
Exception in thread "main" java.lang.IllegalArgumentException: fromIndex(0) > toIndex(-1)
at java.util.ArrayList.subListRangeCheck(ArrayList.java:1006)
at java.util.ArrayList.subList(ArrayList.java:996)
at scratch.Scratch.kdtree(Scratch.java:71)
at scratch.Scratch.kdtree(Scratch.java:80)
at scratch.Scratch.kdtree(Scratch.java:79)
at scratch.Scratch.kdtree(Scratch.java:79)
at scratch.Scratch.kdtree(Scratch.java:79)
at scratch.Scratch.kdtree(Scratch.java:79)
at scratch.Scratch.main(Scratch.java:32)
Im gonna go on a guess here and say that if you have only one point (or 0) on the points array, when you do middle=points.size/2 that equals 0 (integer division) and then when you try to make a sublist from 0 to -1 it throws that Exception.
This is for a data structures class in java, and I need to create a graph that reads text input, converts this information into a graph, and from there print the adjacency list and print a max spanning tree, which I have done. However, the final aspect is to
The full routing table that your program must produce will have for every pair [i,j] (i != j) of computers in the network, the first computer on an optimum route from i to j. If i and j have a direct connection, then j will be the result (table entry) for that route (pair [i,j]). If the optimum route from i to j is i -> a -> b -> j, then a will be the result (table entry) for that route. One could then construct the route by looking at the value from [i,j] (=a), then [a,j] (=b), then [b,j] (=j).
So basically Dijksta's algorithm. However, I cannot use any Java APIs that deal with graphs (all others are allowed). So far I have
import java.util.ArrayList;
//import java.util.Stack;
public class Graph2 {
ArrayList<Vertex> vert = new ArrayList<Vertex>();
private static int counter = 0;
private int edges;
private Edge[] allEdges = new Edge[50];
private class Vertex{
String ip;
private int id;
ArrayList<Edge> nb = new ArrayList<Edge>();
/**
* Constructor for Vertex
* #param String of IP
*/
public Vertex(String s){
ip = s;
id = counter;
counter++;
}
/**
* Gets the ID of a vertex
* #return unique ID
*/
public int getId(){
return id;
}
/*
public boolean isSame(Vertex v){
if(this.ip.equals(v.getIp()))
return true;
else
return false;
}
*/
/**
* Gets the IP of a vertex
* #return the IP of the vertex
*/
public String getIp(){
return this.ip;
}
/**
* Adds an edge to nb
* #param edge to be added
*/
public void addList(Edge e){
nb.add(e);
}
/**
* Determines if an edge exists
* #param edge to be checked
* #return true if exists, false if not
*/
public boolean exists(Edge e){
if(nb.indexOf(e) != -1){
return true;
}
else
return false;
}
/**
* Gets the size of an edge
* #return size of edge
*/
public int edgeSize(){
return nb.size();
}
/**
* Gets the edges
* #return String of edges
*/
public String getEdges(){
String all = "";
Edge e;
for(int i = 0; i < nb.size(); i++){
e = nb.get(i);
int ti = this.getId();
int fin = e.getFinish().getId();
if(ti == fin)
all += e.getStart().getId() + " ";
else
all += e.getFinish().getId() + " ";
}
return all;
}
/**
* Overrides toString method
* #return ID and IP of vertex
*/
public String toString(){
return id + " " + " " + ip;
}
}
private class Edge{
int weight;
Vertex finish;
Vertex start;
/**
* Constructor of Edge
* #param vertex 1, start vertex
* #param vertex 2, endpoint vertex
* #param weight of edge
*/
public Edge(Vertex v, Vertex v2, int w){
start = v;
finish = v2;
weight = w;
}
/**
* Gets the start of an edge
* #return edge start
*/
public Vertex getStart(){
return start;
}
/**
* Gets the endpoint of an edge
* #return endpoint of edge
*/
public Vertex getFinish(){
return finish;
}
/**
* Gets the weight of an edge
* #return weight of edge
*/
public int getWeight(){
return weight;
}
/**
* Overrides toString
* #return start of edge and endpoint of edge
*/
public String toString(){
return start + " " + finish;
}
}
/**
* Adds an edge to 2 verticies
* #param s, starting vertex IP
* #param t, enpoint vertex IP
* #param w, weight of edge
*/
public void add(String s, String t, int w){
Vertex v3, v4;
v3 = exists(new Vertex(s));
v4 = exists(new Vertex(t));
Edge e;
if(v3 == null && v4 == null){
v3 = new Vertex(s);
v4 = new Vertex(t);
vert.add(v3);
vert.add(v4);
e = new Edge(v3, v4, w);
v3.addList(e);
v4.addList(e);
}
else if(v3 != null && v4 == null){
counter--;
v4 = new Vertex(t);
vert.add(v4);
e = new Edge(v3, v4, w);
v3.addList(e);
v4.addList(e);
}
else if(v3 == null && v4 !=null){
counter--;
v3 = new Vertex(s);
vert.add(v3);
e = new Edge(v3, v4, w);
v3.addList(e);
v4.addList(e);
}
else{
counter -= 2;
e = new Edge(v3, v4, w);
if(!v3.exists(e)){
v3.addList(e);
v4.addList(e);
}
}
allEdges[edges] = e;
edges++;
}
/**
* Determines if an edge already exists
* #param vertex to be checked
* #return vertex if exists, null if not
*/
public Vertex exists(Vertex v){
for(int i = 0; i < vert.size(); i++){
if(v.getIp().equals(vert.get(i).getIp()))
return vert.get(i);
}
counter--;
return null;
}
/**
* Puts vert ArrayList into an array
* #return String array of vert
*/
public String[] toArray(){
String[] all = new String[vert.size()];
for(int i = 0; i < vert.size(); i++){
all[i] = vert.get(i).toString();
}
return all;
}
/**
* Determines if a vertex is adjacent to another vertex
* #return String array of adjacent verticies
*/
public String[] adjaceny(){
String[] all = new String[vert.size()];
Vertex v1;
for(int i = 0; i < vert.size(); i++){
v1 = vert.get(i);
all[i] = v1.getEdges();
}
return all;
}
/**
* Determines which vertex is in which cluster
* #return String array of clusters
*/
/*
public String[] cluster(){
Vertex[] temp = (Vertex[]) vert.toArray();
Sorter sort = new Sorter();
sort.heapsort(temp);
String[] cluster = new String[vert.size()];
int[] verts = new int[vert.size()];
for(int i = 0; i < vert.size();i++)
verts[i] = i;
return null;
}
*/
/**
* Gets the max spanning tree of the graph
* #return spanning tree of graph
*/
public String[] maxTree(){
sortEdges();
String[] max = new String[vert.size() -1];
Edge e;
for(int i = 0; i < vert.size()-1; i++){
e = allEdges[i];
max[i] = e.getStart().getId() + ", " + e.getFinish().getId() + ", " + e.getWeight();
}
return max;
}
/**
* Sorts edges by max weight
*/
private void sortEdges(){
Sorter sort = new Sorter();
sort.heapsort(allEdges);
}
public class Sorter{
/**
* Heapsorts the Object array
* #param Object array to be sorted
*/
public void heapsort(Object[] a)
{
for(int i = edges / 2; i >= 0; i-- ) /* buildHeap */
percDown(a, i, edges);
for( int i = edges - 1; i > 0; i-- )
{
swapReferences( a, 0, i ); /* deleteMax */
percDown( a, 0, i );
}
}
/**
* Performs swapping of elements
* #param Object array
* #param index1
* #param index2
*/
public void swapReferences(Object[] a, int index1, int index2 )
{
if(a[0] instanceof Edge){
Edge tmp = (Edge)a[index1];
a[index1] = a[index2];
a[index2] = tmp;
}
else if(a[0] instanceof Vertex){
Vertex temp = (Vertex)a[index1];
a[index1] = a[index2];
a[index2] = temp;
}
}
/**
* Internal method for heapsort.
* #param i the index of an item in the heap.
* #return the index of the left child.
*/
private int leftChild(int i)
{
return 2 * i + 1;
}
/**
* Internal method for heapsort that is used in
* deleteMax and buildHeap.
* #param a an array of Comparable items.
* #int i the position from which to percolate down.
* #int n the logical size of the binary heap.
*/
private void percDown(Object[] a, int i, int n)
{
int child;
if(a[0] instanceof Edge){
Edge tmp;
for( tmp = (Edge) a[i]; leftChild(i) < n; i = child )
{
child = leftChild(i);
if( child != n - 1 && ((Edge)a[child]).getWeight() - ((Edge)(a[child + 1])).getWeight() > 0 )
child++;
if(tmp.getWeight() - ((Edge)a[child]).getWeight() > 0 )
a[i] = a[child];
else
break;
}
a[i] = tmp;
}
else if(a[0] instanceof Vertex){
Vertex temp;
for(temp = (Vertex) a[i]; leftChild(i) < n; i = child){
child = leftChild(i);
if(child != n-1 && ((Vertex)a[child]).edgeSize() - ((Vertex)a[child+1]).edgeSize() > 0)
child++;
if(temp.edgeSize() - ((Vertex)a[child]).edgeSize() > 0)
a[i] = a[child];
else
break;
}
a[i] = temp;
}
}
}
}
}
With a main consisting of:
import java.util.*;
import java.io.*;
public class pg6main {
public static void main(String[] args) throws IOException{
String filename;
String first, second;
int weight;
Graph2 graph = new Graph2();
Scanner kb = new Scanner(System.in);
boolean go = false;
Scanner infile = null;
PrintWriter outfile = new PrintWriter(new FileWriter("results.txt"));
do{
try{
System.out.print("Enter a file to read from: ");
filename = kb.nextLine();
infile = new Scanner(new FileReader(filename));
}catch(Exception e){
go = true;
System.out.println("file doesn't exist");
}
}while(go);
while(infile.hasNext()){
first = infile.next().trim();
second = infile.next().trim();
weight = Integer.parseInt(infile.nextLine().trim());
graph.add(first, second, weight);
}
outfile.println("IP and their unique IDs: ");
String[] a = graph.toArray();
for(int i = 0; i < a.length; i++)
outfile.println(a[i]);
outfile.println("Adjaceny List: ");
String[] adj = graph.adjaceny();
for(int j = 0; j < adj.length; j++)
outfile.println(j + ": " + adj[j]);
outfile.println("Max spanning tree: ");
String[] max = graph.maxTree();
for(int k = 0; k < max.length; k++)
outfile.println("(" + max[k] + ") ");
/*
//Attempting to do clusters based on length of string of neighbors, does not work
for(int x = 0; x < adj.length; x++){
if(adj[x].length() > adj[x+1].length()){
outfile.println("WHAT I DID: " + adj[x]);
}
else if(adj[x].length() == adj[x+1].length()){
adj[x] = adj[x+1];
outfile.println("WHAT I DID: " + adj[x]);
}
else if(adj[x].length() < adj[x+1].length()){
adj[x] = adj[x+1];
outfile.println("WHAT I DID: " + adj[x]);
}
*/
/*//Attempted to do neighbors slighly different way
String[] cluster = graph.cluster();
for(int x = 0; x < cluster.length; x++){
if(cluster[x] != null)
outfile.println(cluster[x]);
*/
outfile.close();
}//end main
}//end pg6main
I was wondering if anyone could help, I've never worked with graphs until today. So far, I believe my code works as intended, but there could potentially be some errors with my logic. Basically my issue is most of the implementations of Dijkstra's have parameters as a graph, and I am unsure if this is actually how I should be doing this. Thanks for any help.
You should use the Floyd algorithm instead, it gives you a full table of shortest routes, exactly as you described in the requirements.
As you can see from the pseudocode, it's really simple and all you need is to create a two-dimensional array and initialise it with the edges. (So basically it's your weighted adjacency table.)