Bfs game Map implementation in java - java

I'm trying to implement a bfs algorithm in Java,but it doesn't work as it should be.
I've made a game map comprised of HexTile objects(custom objects,similar to matrix elements). Each HexTile includes one adjacency list containing references to the elements that it's connected to, one function that returns those elements and one function that computes the distance between two HexTiles. The bfs algorithm is excecuted in another class called unit(units are placed in HexTiles) and finds every unit available in a given range from the room(currentTile). It then creates an ArrayList with the given units.
class HexTile {
static final int MAX_NEIGHBOURS = 6;
private HexTile[] neighbours;
public HexTile[] getNeighbours() {
return this.neighbours;
}
public double distanceFromTarget(HexTile target) {
double distance = Math.sqrt(Math.pow((this.getRow() - target.getRow()), 2) + Math.pow((this.getCol() - target.getCol()), 2));
return distance;
}
}
class Unit {
private ArrayList<Unit> unitsWithinRange = new ArrayList<Unit>();
private void findUnitsWithinRange(HexTile currentTile, int attackRange) {
Queue<HexTile> queue = new LinkedList<>();
ArrayList<HexTile> visited = new ArrayList<HexTile>();
queue.add(currentTile);
visited.add(currentTile);
while (!queue.isEmpty()) {
HexTile aux = queue.poll();
for (HexTile auxNeigh : aux.getNeighbours()) {
if (auxNeigh != null && (!visited.contains(auxNeigh))) {
visited.add(auxNeigh);
queue.add(auxNeigh);
}
}
if (aux != null && (currentTile.distanceFromTarget(aux) <= attackRange)) {
Unit auxUnit = aux.getUnitOnTile();
this.unitsWithinRange.add(auxUnit);
}
}
queue.clear();
visited.clear();
}
}
What happens whenever findUnitsWithinRange is excecuted is that it return a list of units,but the units that are in range 1 are not included(direct neighbours to root).Sometimes the program crashes,because units need to be able to know if there are any nearby units,to excecute some other functions.Any advice would be appreciated!

Related

Dijkstra's algorithm in Java - Multiple requests/threads

I am trying to implement Dijkstra’s algorithm in Java REST webservice, I used this link for my help.
In this link, it is creating only one graph and calculating one route successfully. But in my program, I am creating multiple graphs and using different variables as cost for each graph. Then I use ExecutorService (total threads = total number of graphs) to find all paths in parallel. My program is working fine when I call it to test paths.
The problem is, when this algorithm receives multiple requests at the same time, it is returning “Unreached” message in printPath() function for some requests, and returning correct paths for other requests successfully. I tested each path one by one, and it is returning correct path each time without any error. Problem occurs only when webservice receives multiple requests at the same time. Below is my code, I only posted classes structure which I am using, rest of the code is same as in above mentioned link.
This is how I am using ExecutorService to find path:
List<RouteFutureResult> rfutureResult = new ArrayList();
executorService = Executors.newFixedThreadPool(graphs.size());//number of threads is equal to number of graphs
for (final Graph g : graphs) {
CalcRoutes calcRoutes = new CalcRoutes(g, other_parameters);
Future<String> submit = executorService.submit(calcRoutes);
rfutureResult.add(new RouteFutureResult(submit));
}
executorService.shutdown();
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES);
//reading response from future object
List<String> st = new ArrayList();
for (RouteFutureResult resQuery : rfutureResult) {
String path = resQuery.getFuture().get();
st.add(path);
}
This is my CalcRoutes.java call function:
#Override
public String call() throws Exception {
List<Double> djst = new ArrayList();
g.dijkstra(s);
List<Double> st = g.printPath(d, djst);
//other processing and returning results
}
Here is the Graph class which I used from mentioned link:
class Graph {
private final Map<Double, Vertex> graph;
public class Edge {
public final double v1, v2;
public final double dist;
}
public class Vertex implements Comparable<Vertex> {
public final double name;
double gid;
public double dist = Double.MAX_VALUE;
public Vertex previous = null;
public final Map<Vertex, Double> neighbours = new HashMap<>();
private void printPath(List<Double> st) {
if (this == this.previous) {
st.add(this.name);
} else if (this.previous == null) {
System.out.printf("%s(unreached)", this.name);//this is where I am getting a problem when service receives multiple requests at same time
} else {
this.previous.printPath(st);
st.add(this.name);
}
}
}
public Graph(HashMap<Double, RouteResult> edges) {
}
public void dijkstra(double startName) {
if (!graph.containsKey(startName)) {
System.err.printf("Graph doesn't contain start vertex \"%s\"\n", startName);
return;
}
final Vertex source = graph.get(startName);
NavigableSet<Vertex> q = new TreeSet<>();
for (Vertex v : graph.values()) {
v.previous = v == source ? source : null;
v.dist = v == source ? 0 : Double.MAX_VALUE;
q.add(v);
}
dijkstra(q);
}
private void dijkstra(final NavigableSet<Vertex> q) {
Vertex u, v;
while (!q.isEmpty()) {
}
}
public List<Double> printPath(double endName, List<Double> st) {
if (!graph.containsKey(endName)) {
System.err.printf("Graph doesn't contain end vertex \"%s\"\n", endName);
}
graph.get(endName).printPath(st);
return st;
}
}
This is how I am creating multiple graphs:
Graph cost = new Graph(MRoute); //MRoute is the hashmap
graphs.add(cost);//graphs is a list containing multiple graphs
I also checked already posted questions about Dijkstra's algorithm, but I couldn't find any relevant question to my problem. Somehow, this algorithm is unable to handle multiple requests, please guide me, any help would be highly appreciated.

Avoiding duplicate entries in an array

I am writing a method that adds Vertex objects to an array. I need to check if the vertex I am adding already exists in the array. I am not sure where I am going wrong. Here is my method:
public void addVertex(Vertex v) {
if (activeVertices >= maxVertices) {
System.out.println("Graph full");
return;
}
for(int i=1; i<vertices.length; i++) {
if(vertices[i] != vertices[i-1]){
vertices[activeVertices] = v; // add vertex to list of vertices
v.graphIndex = activeVertices; // record index of vertex in graph
activeVertices++; // increment vertex count
}
}
}
Vertex class:
public class Vertex {
public String name;
public int graphIndex; // index of adjacency matrix position of node in graph
public Vertex (String s) {
name = s;
graphIndex = -1; // invalid position by default
}
public String toString() {
return name;
}
}
The class that contains the addVertex() method:
public class Graph {
private int maxVertices;
private Vertex[] vertices; // array of nodes
private int[][] edges; // adjacency matrix
int activeVertices;
public Graph(int maxSize) {
maxVertices = maxSize;
vertices = new Vertex[maxVertices];
activeVertices = 0;
}
public void addVertex(Vertex v) {
if (activeVertices >= maxVertices) {
System.out.println("Graph full");
return;
}
for(int i=1; i<vertices.length; i++) {
if(vertices[i] != vertices[i-1]){
vertices[activeVertices] = v; // add vertex to list of vertices
v.graphIndex = activeVertices; // record index of vertex in graph
activeVertices++; // increment vertex count
}
}
}
public void addEdge(Vertex v1, Vertex v2, int w) {
edges[v1.graphIndex][v2.graphIndex] = w;
edges[v2.graphIndex][v1.graphIndex] = w;
}
public Graph minimumSpanningTree() {
Graph mst = new Graph(maxVertices); // create new graph
int[] set = new int[activeVertices];
for (int i=0; i<activeVertices; i++) { // copy nodes to graph
mst.addVertex(vertices[i]);
set[i]=i; // assign each node to its own set
}
PriorityQueue q = new PriorityQueue(maxVertices*maxVertices); // create priority queue
for (int i=0; i<activeVertices; i++) { // copy edges to queue
for (int j=0; j<activeVertices; j++) {
if (edges[i][j]!=0) {
q.enqueue(new Edge(vertices[i],vertices[j],edges[i][j]));
}
}
}
while (!q.isEmpty()) { // iterate over all edges in priority order
Edge e = q.dequeue(); // consider next edge
if (set[e.source.graphIndex]!=set[e.destination.graphIndex]) { // skip edges not connecting different sets
mst.addEdge(e.source, e.destination, e.weight); // add edge to MST
System.out.println("adding "+e);
int setToMerge=set[e.destination.graphIndex]; // rename nodes from "other" set
for (int i=0; i<activeVertices; i++) {
if (set[i]==setToMerge) { // find nodes from "other" set
set[i]=set[e.source.graphIndex]; // reassign nodes
}
}
}
}
return mst;
}
public void print() {
System.out.format(" ");
for (int i=0; i<activeVertices; i++) {
System.out.format("%3s ", vertices[i].name);
}
System.out.format("\n");
for (int j=0; j<activeVertices; j++) {
System.out.format("%3s ", vertices[j].name);
for (int i=0; i<activeVertices; i++) {
System.out.format("%3d ", edges[i][j]);
}
System.out.format("\n");
}
}
}
First, you should be using equals instead of ==. You should write a proper equals method in your Vertex class (use Google to find plenty of tutorials on how to do this).
For example, if you wanted two Vertex objects to be considered equal only when their names were the same, then your equals method would look something like this:
public boolean equals(Object obj) {
if(obj == null) {
return false;
}
if(obj instanceof Vertex) {
Vertex otherVertex = (Vertex) obj;
if(this.name.equals(otherVertex.name)) {
return true;
}
}
return false;
}
If you wanted to compare graphIndex as well, then you would need to check that in the equals method as well.
Assuming you have a proper equals method in your Vertex class, the simplest solution would be to use the ArrayUtils.contains method, from the Apache Commons library (Apache Commons has TONS of useful methods, which can save you a lot of time. You should check it out). This method takes in an array and an Object, and checks if the array contains the object or not.
You're always checking vertices[1] against vertices[0] and adding based on the result. You're not checking for v, and not actually looking in the whole array.
If an == check (identity, not equivalence) is really what you want, then:
public void addVertex(Vertex v) {
if (activeVertices >= maxVertices) {
System.out.println("Graph full");
return;
}
for(int i=0; i<vertices.length; i++) {
if(vertices[i] == v){
// Already have it
return;
}
}
vertices[activeVertices] = v; // add vertex to list of vertices
v.graphIndex = activeVertices; // record index of vertex in graph
activeVertices++; // increment vertex count
}
If you want equivalence instead, replace
if(vertices[i] == v){
with
if(v.equals(vertices[i])){
Side note: Based on your having an activeVertices variable, I suspect you may be better off with ArrayList<Vertex> rather than Vertex[]. That would also give you the contains method (which uses equals), which may be able to replace your loop (if you want an equals, not ==, check).
Whenever you write a value class, i.e. a class that represents a value or quantity of something, you should always override the following methods for your class:
equals(Object o);
hashCode();
Not all classes are value classes. Some represent system resources and others represent actions or processes, but whenever a class is written as an abstraction for a collection of data you should always consider writing the above methods.
The reason is straightforward. Whereas Java primitives have only value, Java reference types (which include all the instances of classes you write yourself) have both value and location. This confers the properties of both equality and identity to reference types and they are very different.
By default, the equals() method in the Object class performs an identity comparison and NOT an equality comparison ... and it's a good thing too. Because any subclass of Object can have vastly different notions of "how instances can be considered equal" there is no straightforward way that Object could have a superclass method that would test equality for any arbitrary Java object. In contrast, it is always straightforward to test for identity. If any two references indicate the same location, then their objects are identical. This exemplifies the different notions of equality and identity.
You need to be able to test whether your Vertex instances are equal and not whether they are identical. For this reason, you really do need to override the equals(Object o) method. If you also override hashCode() (which you should), then you may be able to store your vertices in a HashSet, which would guarantee that no two vertices would be equal.

Edge of undirected graph in Java

Suppose I am writing a Java class to represent an edge of undirected graph. This class Edge contains two vertices to and from.
class Edge<Vertex> {
private final Vertex to, from
public Edge(Vertex to, Vertex from) {
this.to = to;
this.from = from;
}
... // getters, equals, hashCode ...
}
Obviously e1 = new Edge(v1, v2) and e2 = new Edge(v2, v1) are actually the same in an undirected graph. Does it make sense? How would you implement class Edge to meet that requirement?
Perform a sort on the vertices in the constructor based on some unique identifier. This way they are stored consistently regardless of order.
I find this preferable to noMAD's solution because all code interacting with these objects will treat them identically, not just your implementation of equals.
Also, calling your class members to and from is confusing because it sounds like a directed graph. I would rename these to something more generic like vertex1 and vertex2.
public Edge(Vertex x, Vertex y) {
if (vertex2.getId() > vertex1.getId()) {
this.vertex1 = x;
this.vertex2 = y;
} else {
this.vertex1 = y;
this.vertex2 = x;
}
}
I actually wouldn't have this logic in my Edge class but rather some sort of over-seeing class such as a Graph class. The reason for this is because an Edge is just an object with 2 vertices. It doesn't know anything about the rest of the edges in the graph.
So, to expand on #noMad's answer, I would actually put his checkIfSameEdge method in my Graph class:
public class Graph {
private List<Edge> edges;
....
public void addEdge(Edge e) {
for (Edge edge : edges) {
if (isSameEdge(edge, e) {
return; // Edge already in Graph, nothing to do
}
edges.add(e);
}
private boolean isSameEdge(Edge edge1, Edge edge2) {
return ((edge1.to.equals(edge2.to) && edge1.from.equals(edge2.from))
|| (edge1.to.equals(edge2.from) && edge1.from.equals(edge2.to)))
}
}
BTW: I would rename to and from to vertex1 and vertex2 because it is an undirected graph and to and from indicate direction, but that's just my opion.
Well, of the top of my head, the naivest method would be:
protected boolean checkIfSameEdge(Vertex to, Vertex from) {
if(to.equals(this.from) && from.equals(this.to) || to.equals(this.to) && from.equals(this.from)) {
return true;
return false;
}
Obviously you would have to override equals and hashcode
Presumably the nodes contain some sort of scalar values - sort the parameters based on these values (using the compareTo method) and use a factory to create a new instance or return an existing instance.

deep copying a graph structure

I have a graph class with Node's, where each Node can connect to others:
public class Node {
List<Node> connections;
}
I would like to make a deep copy of the entire graph. As a first attempt, I tried making a copy constructor like:
public Node(Node other) {
connections = new ArrayList<Node>();
for (Node n : other.connections) {
connections.add(new Node(n));
}
}
So deep copying a graph would just be:
public Graph deepCopy () {
Graph g = new Graph();
g.nodes = new ArrayList<Node>();
for (Node n : nodes) {
g.nodes.add(new Node(n));
}
}
But that doesn't work as that destroys the connection relationship among the nodes. I am wondering if anyone has suggestions to do this in a simple way? Thanks.
The problem is that you need to copy the identities of the nodes, not just their values. Specifically, when you're copying some node, you need to deal with the identities of the nodes it refers to; that means that a copy constructor, or some other kind of purely local copying mechanism, can't do the job, because it only deals with one node at a time. I'm not sure that makes any sense, but I've typed it and my backspace key doesn't work.
Anyway, what you can do is pass around some other object which can tell which new node corresponds to which old node. If you wanted to be fancy (and who doesn't?) you could refer to this as a graph isomorphism. This can be something as simple as a map. As in this completely untested code:
// in Graph
public Graph deepCopy () {
Graph g = new Graph();
g.nodes = new ArrayList<Node>();
Map<Node, Node> isomorphism = new IdentityHashMap<Node, Node>();
for (Node n : nodes) {
g.nodes.add(n.deepCopy(isomorphism));
}
return g;
}
// in Node
public Node deepCopy(Map<Node, Node> isomorphism) {
Node copy = isomorphism.get(this);
if (copy == null) {
copy = new Node();
isomorphism.put(this, copy);
for (Node connection: connections) {
copy.connections.add(connection.deepCopy(isomorphism));
}
}
return copy;
}
Sergii mentions using serialization; serialization actually does something pretty similar when it traverses an object graph.
Yep, deep copy in java ( not only in java) can be made using memory serialization/deserialization
like this
public static Object copy(Object orig) {
Object obj = null;
try {
// Write the object out to a byte array
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(orig);
out.flush();
out.close();
// Make an input stream from the byte array and read
// a copy of the object back in.
ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(bos.toByteArray()));
obj = in.readObject();
}
catch(IOException e) {
e.printStackTrace();
}
catch(ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
return obj;
}
Kinda late input. But I had a similar problem but came to a different solution. But not shure if its bulletproof. So please feel free to comment so I can learn!
I have a Type called "Numbers" because I have no creativity naming stuff.
Each object of type "Numbers" has an internal list that can carry additional objects of type "Numbers" of which each has a list of additional "Numbers" of which each... and so on.
Basicaly you can make a tree structure similar to this:
I solved the deep copy problem by using a recursive copy-constructor inside the "Numbers" class.
Numbers class:
import java.util.ArrayList;
public class Numbers {
private ArrayList<Numbers> numbers = new ArrayList<>();
private int number;
public Numbers(int number) {
this.number = number;
}
public Numbers(Numbers numToCopy) {
this.number = numToCopy.getNumber();
ArrayList<Numbers> list = numToCopy.getNumbers();
for(int i = 0; i < list.size(); i++) {
Numbers n = new Numbers(list.get(i));
numbers.add(n);
}
}
public void addNumber(Numbers i) {
numbers.add(i);
}
public ArrayList<Numbers> getNumbers() {
return numbers;
}
public void setNumber(int i) {
this.number = i;
}
public int getNumber() {
return number;
}
public ArrayList<Numbers> getAllNumbers(ArrayList<Numbers> list) {
int size = numbers.size();
list.addAll(numbers);
for(int i = 0; i < size; i++) {
numbers.get(i).getAllNumbers(list);
}
return list;
}
}
Usage:
import java.util.ArrayList;
public class NumbersTest {
public NumbersTest() {
}
public static void main(String[] args) {
Numbers num0 = new Numbers(0);
Numbers num1 = new Numbers(1);
Numbers num2 = new Numbers(2);
Numbers num3 = new Numbers(3);
Numbers num4 = new Numbers(4);
Numbers num5 = new Numbers(5);
Numbers num6 = new Numbers(6);
num0.addNumber(num1);
num0.addNumber(num2);
num1.addNumber(num3);
num1.addNumber(num4);
num2.addNumber(num5);
num2.addNumber(num6);
num4.addNumber(num6);
//Deep copy here!
Numbers numCopy = new Numbers(num0);
//Change deep down in graph of original
num0.getNumbers().get(0).getNumbers().get(1).getNumbers().get(0).setNumber(799);
//Printout of copy to show it was NOT affected by change in original.
for(Numbers n : numCopy.getAllNumbers(new ArrayList<Numbers>())) {
System.out.println(n.getNumber());
}
}
}
Usage code shows that changing deep inside the "graph" of the original num0 object, does not change the copy made of it.
Theres two sixes (6) in the graph, and thats ok since they are on different branches.
Downside is if same number would repeat through one of the paths, like if there was a (1) somewhere under the first 1. It would then end up in an infinite loop.
Please do comment! :)

A* algorithm not working properly

I need some help with my A* algorithm implementation.
When I run the algorithm it does find the goal, but the path is definately not the shortest :-P
Here is my code, please help me spot the bugs!
I think it might be the reconstruct path that is my problem but I'm not sure.
public class Pathfinder {
public List<Node> aStar(Node start, Node goal, WeightedGraph graph) {
Node x, y;
int tentative_g_score;
boolean tentative_is_better;
FScoreComparator comparator = new FScoreComparator();
List<Node> closedset = new ArrayList<Node>();
Queue<Node> openset = new PriorityQueue<Node>(10, comparator);
openset.add(start);
start.g_score = 0;
start.h_score = heuristic_cost_estimate(start, goal);
start.f_score = start.h_score;
while (!openset.isEmpty()) {
x = openset.peek();
if (x == goal) {
return reconstruct_path(goal);
}
x = openset.remove();
closedset.add(x);
for (Edge e : graph.adj(x)) {
if (e.v == x) {
y = e.w;
} else {
y = e.v;
}
if (closedset.contains(y) || y.illegal) {
continue;
}
tentative_g_score = x.g_score + e.weight;
if (!openset.contains(y)) {
openset.add(y);
tentative_is_better = true;
} else if (tentative_g_score < y.g_score) {
tentative_is_better = true;
} else {
tentative_is_better = false;
}
if (tentative_is_better) {
y.g_score = tentative_g_score;
y.h_score = heuristic_cost_estimate(y, goal);
y.f_score = y.g_score + y.h_score;
y.parent = x;
}
}
}
return null;
}
private int heuristic_cost_estimate(Node start, Node goal) {
return Math.abs(start.x - goal.x) + Math.abs(start.y - goal.y);
}
private List<Node> reconstruct_path(Node current_node) {
List<Node> result = new ArrayList<Node>();
while (current_node != null) {
result.add(current_node);
current_node = current_node.parent;
}
return result;
}
private class FScoreComparator implements Comparator<Node> {
public int compare(Node n1, Node n2) {
if (n1.f_score < n2.f_score) {
return 1;
} else if (n1.f_score > n2.f_score) {
return -1;
} else {
return 0;
}
}
}
}
Thanks to everyone for all the great answers!
My A* algorithm now works perfectly thanks to you guys! :-)
This was my first post and this forum is really amazing!
You are changing the priority of an element in the PriorityQueue after having inserted it. This isn't supported, as the priority queue isn't aware that an object has changed. What you can do is remove and re-add the object when it changes.
The priority is changed in the line: y.f_score = y.g_score + y.h_score;. This line happens after adding y to the priority queue. Note that simply moving the line openset.add(y); to after calculating the cost won't be enough, since y may have been added in a previous iteration.
It also isn't clear from your code whether the heuristic you used is admissible. If it isn't it will also cause you to get suboptimal paths.
Finally, a performance note: The contains method on ArrayList and PriorityQueue takes linear time to run, which will make the running time of your implememtation non-optimal. You can improve this by adding boolean properties to the nodes to indicate if they are in the closed/open sets, or by using a set data structure.
Priority queue does not update position of item when you change its priority.
Therefore heap property does not hold.
Changed priority affect additions/removals of other items, but it does not repair heap property.
therefore you does not get best item from open -> you don't find shortest path.
You can:
1) write your own heap and maintain index into it
2) add another object into PQ and mark the old one as invalid (you must instead of node put some object with validity flag and referencing node into queue).
2) have worse performance and I advise against it, but some navigation software use this approach (or at least few years back it used).
edit: Best practice is, insert immutable (or at least with imutable parts that means priority) objects into PriorityQueue

Categories

Resources