I am writing an A* path finding algorithm for class. It works perfectly, I am able to click my character and move them to the correct location. However, after running for about 30 seconds of enemies also calling out to the A* algorithm it throws a java.lang.StackOverflowError.
The call directly before this is to the ArrayList library function grow. I can't imagine what I am doing wrong, as I am instantiating the object and am just calling ArrayList.add(Node);
What I am thinking is happening is that there are many ai characters that rely on this functionality, and after many of them calling to it, the program runs out of memory and surpasses the maximum stack size allocated for the program. However, Eclipse is not saying that Java ran out of memory or anything like this, which I would imagine would happen if the program actually ran out of memory.
Is there something that I am doing wrong? Or am I running out of memory? If so, is there a way to increase my allocated memory (I can see that I have tons of RAM left)? Or is there a better way that I could be storing my data to reduce memory loads?
Thanks! Here is the code I have currently written up, please let me know if there is any more information needed.
public class AStar {
class Node
{
public double x, y, g, h;
public Node parent;
public Node(double _x, double _y, double _g, double _h, Node _parent)
{
x = _x;
y = _y;
g = _g;
h = _h;
parent = _parent;
}
}
double start_x, start_y, goal_x, goal_y;
private double heuristic(double s_x, double s_y)
{
double x = Math.abs(s_x - goal_x);
double y = Math.abs(s_y - goal_y);
return x + y;
}
private Node GenerateRelativeNode(Node origin, double dX, double dY, List<Node> closed, List<Node> open)
{
double newX = origin.x + dX;
double newY = origin.y + dY;
Node temp = new Node(newX, newY, origin.g+1, heuristic(newX, newY), origin);
for(int i = 0; i < closed.size(); ++i)
{
if(closed.get(i).x == temp.x && closed.get(i).y == temp.y)
return null;
}
for(int i = 0; i < open.size(); ++i)
{
if(open.get(i).x == temp.x && open.get(i).y == temp.y)
{
return null;
}
}
return temp;
}
private int GetLowestFIndex(List<Node> set)
{
double min = 1000000;
int index = -1;
for(int i = 0; i < set.size(); ++i)
{
double f = set.get(i).h + set.get(i).g;
if(f < min)
{
min = f;
index = i;
}
}
return index;
}
private List<Pair<Double, Double>> PathFromNode(Node _n)
{
List<Pair<Double, Double>> path = new ArrayList<Pair<Double, Double>>();
List<Node> nodes = new ArrayList<Node>();
Node curr = _n;
while(curr.parent != null)
{
nodes.add(curr);
curr = curr.parent;
}
//now need to reverse the list.
for(int i = nodes.size()-1; i >= 0; --i)
{
Pair<Double, Double> pair = pairFromNode(nodes.get(i));
path.add(pair);
}
return path;
}
private Pair<Double, Double> pairFromNode(Node _n)
{
return new Pair<Double, Double>(new Double(_n.x), new Double(_n.y));
}
public static int pathDistance(double start_x, double start_y, double goal_x, double goal_y,
S3PhysicalEntity i_entity, S3 the_game) {
AStar a = new AStar(start_x,start_y,goal_x,goal_y,i_entity,the_game);
List<Pair<Double, Double>> path = a.computePath();
if (path!=null) return path.size();
return -1;
}
public AStar(double sX, double sY, double gX, double gY,
S3PhysicalEntity i_entity, S3 the_game) {
start_x = sX;
start_y = sY;
goal_x = gX;
goal_y = gY;
}
public List<Pair<Double, Double>> computePath() {
double start_h = heuristic(start_x, start_y);
Node start = new Node(start_x, start_y, 0, start_h, null);
List<Node> OpenSet = new ArrayList<>();
List<Node> ClosedSet = new ArrayList<>();
OpenSet.add(start);
while(!OpenSet.isEmpty())
{
int index = GetLowestFIndex(OpenSet);
Node N = OpenSet.get(index);
OpenSet.remove(index);
if(N.x == goal_x && N.y == goal_y)
{
return PathFromNode(N);
}
ClosedSet.add(N);
Node Up = GenerateRelativeNode(N, 0, 1, ClosedSet, OpenSet);
Node Left = GenerateRelativeNode(N, -1, 0, ClosedSet, OpenSet);
Node Right = GenerateRelativeNode(N, 1, 0, ClosedSet, OpenSet);
Node Down = GenerateRelativeNode(N, 0, -1, ClosedSet, OpenSet);
if(Up != null)
OpenSet.add(Up);
if(Left != null)
OpenSet.add(Left);
if(Right != null)
OpenSet.add(Right);
if(Down != null)
OpenSet.add(Down);
}
return null;
}
}
Here is the stack at the time of the StackOverflow
The WPeasant(WTroop).moveTowardsTarget(S3, int, int) line: 202 gets called thousands of times in a row at this point. The code for this function is not in any kind of while loop or for loop or anything.
This could be getting called multiple times however, due to the game object colliding with something, as I have not taken obstructions into account in the pathfinding.
It is hard to say for sure that this is the issue, as I wrote none of this engine, I only have to implement the A*. I can try and include obstacle avoidance and see if this fixes things.
Related
This code works fine when the first creation of a new maze object. However when the BFS is complete and the game is played again( by giving the user the option to play again) the algorithm does not work as effectively. The end goal is still met however x and y coordinates are added to the array that are not part of the shortest path. I have never seen my first run ever complete the search incorrectly, however subsequent instances do. It seems as if every time the new maze object is created the maze is left affected by the previous instance. Any input?
public class Maze {
public static List<Integer> fx;
public static List<Integer> fy;
public static int listSize;
public static Point p;
public Maze(int x, int y) {
p = getPathBFS(x, y);
fx = new ArrayList<>();
fy = new ArrayList<>();
addPoint();
System.out.print("next");
}
private static class Point {
int x;
int y;
Point parent;
public Point(int x, int y, Point parent) {
this.x = x;
this.y = y;
this.parent = parent;
}
public Point getParent() {
return this.parent;
}
}
public static Queue<Point> q = new LinkedList<>();
public static Point getPathBFS(int x, int y) {
q.add(new Point(x, y, null));
while (!q.isEmpty()) {
Point p = q.remove();
if (Level.cells[p.x][p.y] == 9) {
return p;
}
if (isFree(p.x + 1, p.y)) {
Level.cells[p.x][p.y] = 2;
Point nextP = new Point(p.x + 1, p.y, p);
q.add(nextP);
}
if (isFree(p.x - 1, p.y)) {
Level.cells[p.x][p.y] = 2;
Point nextP = new Point(p.x - 1, p.y, p);
q.add(nextP);
}
if (isFree(p.x, p.y + 1)) {
Level.cells[p.x][p.y] = 2;
Point nextP = new Point(p.x, p.y + 1, p);
q.add(nextP);
}
if (isFree(p.x, p.y - 1)) {
Level.cells[p.x][p.y] = 2;
Point nextP = new Point(p.x, p.y - 1, p);
q.add(nextP);
}
}
return null;
}
public static boolean isFree(int x, int y) {
if ((x >= 0 && x < Level.cells.length) && (y >= 0 && y < Level.cells[x].length) && (Level.cells[x][y] == 0 || Level.cells[x][y] == 9)) {
return true;
}
return false;
}
public static void addPoint() {
while ((p != null)) {
System.out.println("x is " + p.x + " - y is " + p.y);
fy.add(p.x);
fx.add(p.y);
p = p.getParent();
}
}
public static int getListSize() {
listSize = fx.size();
return listSize;
}
}
It seems as if every time the new maze object is created the maze is
left affected by the previous instance
Yes, exactly. All of your fields are declared as static. Static fields are common across all instances, not just one. You can remove the static keyword in all (or almost all) instances.
public static List<Integer> fx;
public static List<Integer> fy;
public static int listSize;
public static Point p;
It looks like your Level class is suffering from the same problems:
Level.cells.length
I'd look into what 'static' actually means because it seems like you're using it without really understanding it.
I'm getting a stack overflow while trying to populate my 3D linked list. I don't understand why it's not stopping at the specified bounds, it just runs forever. It's probably a simple error, i just don't understand.
EDIT: Okay I have now updated the code and removed that silly mistake, however it's still not operating exactly as intended. It does seem to be generating the 10x10x10 list, however its running infinitely. Initialized with (10, 10, 10), it should create 10000 objects and stop. I'm just trying to create a list to represent a 3d coordinate plane and each integer coordinate is one node, accessible by direction pointer north, south, east, west, up, or down.
Any help appreciated
public class Main {
public static void main(String[] args) {
NodeController3D con = new NodeController3D(6,6, 6);
}
}
public class Node3D {
// public Node3D(Node3D... nodes) {
// if (nodes.length != 5) {
// throw new RuntimeException();
// }
// this.nodes = nodes;
// }
public Node3D[] nodes;
public int x, y, z;
public Node3D north() {
return nodes[0];
}
public Node3D south() {
return nodes[1];
}
public Node3D east() {
return nodes[2];
}
public Node3D west() {
return nodes[3];
}
public Node3D up() {
return nodes[4];
}
public Node3D down() {
return nodes[5];
}
}
public class NodeController3D {
public NodeController3D(int length, int width, int height) {
HEAD = new Node3D();
pnc(HEAD, length, width, height);
}
private void pnc(Node3D node, int xMax, int yMax, int zMax) {
if(node.nodes == null) {
node.nodes = new Node3D[5];
}
if (node.x < xMax) {
Node3D newNode = node.nodes[2] = new Node3D();
newNode.x = node.x + 1;
newNode.y = node.y;
newNode.z = node.z;
System.out.println(newNode.x + ", " + newNode.y + ", " + newNode.z);
pnc(newNode, xMax, yMax, zMax);
}
if (node.y < yMax) {
Node3D newNode = node.nodes[0] = new Node3D();
newNode.x = node.x;
newNode.y = node.y + 1;
newNode.z = node.z;
pnc(newNode, xMax, yMax, zMax);
}
if (node.z < zMax) {
Node3D newNode = node.nodes[4] = new Node3D();
newNode.x = node.x;
newNode.y = node.y;
newNode.z = node.z + 1;
pnc(newNode, xMax, yMax, zMax);
}
}
// public NodeController3D(int radius) {
//
// }
public final Node3D HEAD;
}
EDIT: Okay I have now updated the code and removed that silly mistake, however it's still not operating exactly as intended. It does seem to be generating the 10x10x10 list, however its running infinitely. Initialized with (10, 10, 10), it should create 10000 objects and stop. I'm just trying to create a list to represent a 3d coordinate plane and each integer coordinate is one node, accessible by direction pointer.
You are running into an infinite recursion.
So what is happening.
You are creating a new Array.
if(node.nodes == null) {
node.nodes = new Node3D[5];
}
You go on by using a Node3D as a newNode variable. This happens because node.x<xMax will be true. -> Node3D newNode = node.nodes[2] = new Node3D();.
You recursivly call pnc now, with this newNode.
So what happens now, node.y<yMax will be true .
Now you reassign the newNode. Node3D newNode = node.nodes[0] = new Node3D();.
and call pnc recursivly again. But you are running into a problem now. since it is a new Node3D your node.x<xMax will be true again and these two steps are happening again and ininite until you are getting your mentioned error.
To fix this error you might want to copy node.x and node.y into your newly created variable.
By changing the assignment you could jump out of the infinite recursion.
if (node.x < xMax) {
if (node.nodes[2] == null) {
node.nodes[2] = new Node3D();
}
Node3D newNode = node.nodes[2];
newNode.x = node.x + 1;
newNode.y = node.y;
newNode.z = node.z;
pnc(newNode, xMax, yMax, zMax);
}
if (node.y < yMax) {
if (node.nodes[0] == null) {
node.nodes[0] = new Node3D();
}
Node3D newNode = node.nodes[0];
newNode.x = node.x;
newNode.y = node.y + 1;
newNode.z = node.z;
pnc(newNode, xMax, yMax, zMax);
}
if (node.z < zMax) {
if (node.nodes[4] == null) {
node.nodes[4] = new Node3D();
}
Node3D newNode = node.nodes[4];
newNode.x = node.x;
newNode.y = node.y;
newNode.z = node.z + 1;
pnc(newNode, xMax, yMax, zMax);
}
But since i donĀ“t know what you are trying to achive with this specific elements at these specifics index in your array this might be a wrong solution for you.
Whenever you create a new Node3D, the variables x, y and z are not initialized, therefore they will evaluate to 0 if accessed.
In the pnc method, there will always be cases where either x < xMax, y < yMax or z < zMax since those are all set to 1.
A good practice is to make variables private and final where possible:
class Node3D {
private final Node3D[] nodes;
private final int x;
private final int y;
private final int z;
public Node3D(int x, int y, int z) {
this.nodes = new Node3D[6];
this.x = x;
this.y = y;
this.z = z;
}
}
This will prevent these kinds of errors in the future. You can create getter methods for the variables. If you really need to reassign the values for x, y or z, you could instead create a new Node3D so that the object can remain immutable.
You were navigating to nodes in the 3D grid in multiple ways. For example, you went to (1,1,0) both from (1,0,0) and (0,1,0), creating duplicates for the same coordinate. This way, you create a number of nodes equal to the total sum of cube paths for each node.
An other approach is to populate a cube of nodes, and give each node a reference to the cube that they are in. Then when you need to go North, you just let the cube return the right node, using the coordinates of the current node. You could use Apache's MultiKeyMap as a grid:
class Node3D {
private static int instances = 0;
public final int x;
public final int y;
public final int z;
public final int number;
public final MultiKeyMap<Integer, Node3D> gridMap;
public Node3D(MultiKeyMap<Integer, Node3D> gridMap, int x, int y, int z) {
this.gridMap = gridMap;
this.x = x;
this.y = y;
this.z = z;
this.number = instances++;
}
//Add/alter these methods according to your orientation
//Returns null if no value is present, you might want to handle this
public Node3D getNorthNode() {
return gridMap.get(this.x + 1, this.y, this.z);
}
//Other getters omitted
#Override
public String toString() {
return "Node3D#" + number + "[" + x + ", " + y + ", " + z + "]";
}
}
Now initialization can be done as follows:
MultiKeyMap<Integer, Node3D> gridMap = new MultiKeyMap<>();
for(int x = 0; x < xMax; x++) {
for(int y = 0; y < yMax; y++) {
for(int z = 0; z < zMax; z++) {
gridMap.put(x, y, z, new Node3D(gridMap, x, y, z));
}
}
}
System.out.println(gridMap.get(4, 4, 4).getNorthNode());
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I'm trying to implement the A* algorithm in Java. I followed this tutorial,in particular, this pseudocode: http://theory.stanford.edu/~amitp/GameProgramming/ImplementationNotes.html
The problem is my code doesn't work. It goes into an infinite loop. I really don't know why this happens... I suspect that the problem are in F = G + H function implemented in Graph constructors. I suspect I am not calculate the neighbor F correclty.
Here's my code:
List<Graph> open;
List<Graph> close;
private void createRouteAStar(Unit u)
{
open = new ArrayList<Graph>();
close = new ArrayList<Graph>();
u.ai_route_endX = 11;
u.ai_route_endY = 5;
List<Graph> neigh;
int index;
int i;
boolean finish = false;
Graph current;
int cost;
Graph start = new Graph(u.xMap, u.yMap, 0, ManhattanDistance(u.xMap, u.yMap, u.ai_route_endX, u.ai_route_endY));
open.add(start);
current = start;
while(!finish)
{
index = findLowerF();
current = new Graph(open, index);
System.out.println(current.x);
System.out.println(current.y);
if (current.x == u.ai_route_endX && current.y == u.ai_route_endY)
{
finish = true;
}
else
{
close.add(current);
open.remove(open.indexOf(current)); //EDITED LATER
neigh = current.getNeighbors();
for (i = 0; i < neigh.size(); i++)
{
cost = current.g + ManhattanDistance(current.x, current.y, neigh.get(i).x, neigh.get(i).y);
if (open.contains(neigh.get(i)) && cost < neigh.get(i).g)
{
open.remove(open.indexOf(neigh));
}
else if (close.contains(neigh.get(i)) && cost < neigh.get(i).g)
{
close.remove(close.indexOf(neigh));
}
else if (!open.contains(neigh.get(i)) && !close.contains(neigh.get(i)))
{
neigh.get(i).g = cost;
neigh.get(i).f = cost + ManhattanDistance(neigh.get(i).x, neigh.get(i).y, u.ai_route_endX, u.ai_route_endY);
neigh.get(i).setParent(current);
open.add(neigh.get(i));
}
}
}
}
System.out.println("step");
for (i=0; i < close.size(); i++)
{
if (close.get(i).parent != null)
{
System.out.println(i);
System.out.println(close.get(i).parent.x);
System.out.println(close.get(i).parent.y);
}
}
}
private int findLowerF()
{
int i;
int min = 10000;
int minIndex = -1;
for (i=0; i < open.size(); i++)
{
if (open.get(i).f < min)
{
min = open.get(i).f;
minIndex = i;
System.out.println("min");
System.out.println(min);
}
}
return minIndex;
}
private int ManhattanDistance(int ax, int ay, int bx, int by)
{
return Math.abs(ax-bx) + Math.abs(ay-by);
}
And, as I've said. I suspect that the Graph class has the main problem. However I've not been able to detect and fix it.
public class Graph {
int x, y;
int f,g,h;
Graph parent;
public Graph(int x, int y, int g, int h)
{
this.x = x;
this.y = y;
this.g = g;
this.h = h;
this.f = g + h;
}
public Graph(List<Graph> list, int index)
{
this.x = list.get(index).x;
this.y = list.get(index).y;
this.g = list.get(index).g;
this.h = list.get(index).h;
this.f = list.get(index).f;
this.parent = list.get(index).parent;
}
public Graph(Graph gp)
{
this.x = gp.x;
this.y = gp.y;
this.g = gp.g;
this.h = gp.h;
this.f = gp.f;
}
public Graph(Graph gp, Graph parent)
{
this.x = gp.x;
this.y = gp.y;
this.g = gp.g;
this.h = gp.h;
this.f = g + h;
this.parent = parent;
}
public List<Graph> getNeighbors()
{
List<Graph> aux = new ArrayList<Graph>();
aux.add(new Graph(x+1, y, g,h));
aux.add(new Graph(x-1, y, g,h));
aux.add(new Graph(x, y+1, g,h));
aux.add(new Graph(x, y-1, g,h));
return aux;
}
public void setParent(Graph g)
{
parent = g;
}
//Added later. Generated by Eclipse
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Graph other = (Graph) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}
Little Edit:
Using the System.out and the Debugger I discovered that the program ALWAYS is check the same "current" graph, (15,8) which is the (u.xMap, u.yMap) position. Looks like it keeps forever in the first step.
You don't remove your start node from open list (i mean, that after each cycle in while you need to remove node that was just eximining).
Also, some hints:
there is priority queue in java. So, you don't need to do it on list by yourself
int f,g,h; try to rename variables. it is impossible to understand their meaning in the code.
then you create neighbors, check that they are in the area (i.e: x,y>0 and less that maxX,maxY)
i implemented a simple A* and noticed that it does get into an infity loop if all 4 spots around my Character are filled. Current i am stuck how i get it work so they start running into the nearest possible spot. Any Guesses on it? (sorry for the long code)
the A*
private Node aStarSearch(int startX, int startY) {
openList.clear();
closeList.clear();
successor.clear();
Node startNode = new Node(null, startX, startY, 0, 0);
openList.add(startNode);
while (openList.size() != 0) {
// sort the list
Collections.sort(openList, nodeComperator);
Node q = openList.remove(0); // get the first object
int qx = q.x;
int qy = q.y;
// start adding the successors
// left
Node left = createNeighbor(q, qx - 1, qy);
if (left != null && !closeList.contains(left))
successor.add(left);
// right
Node right = createNeighbor(q, qx + 1, qy);
if (right != null && !closeList.contains(right))
successor.add(right);
// // down
Node down = createNeighbor(q, qx, qy - 1);
if (down != null && !closeList.contains(down))
successor.add(down);
// up
Node up = createNeighbor(q, qx, qy + 1);
if (up != null && !closeList.contains(up))
successor.add(up);
// calc
for (Node suc : successor) {
if (suc.x == (int) this.screen.character.mapPos.x
&& suc.y == (int) this.screen.character.mapPos.y)
return suc;
boolean add = true;
if (betterIn(suc, openList)) // openList und der
add = false;
if (betterIn(suc, closeList)) // closedList
add = false;
if (add)
openList.add(suc);
}
closeList.add(q);
}
return null;
}
private Node createNeighbor(Node parrent, int x, int y) {
if (x >= 0 && y >= 0 && x < this.screen.map.width
&& y < this.screen.map.height
&& this.screen.map.mapArray[x][y] != Config.CANTMOVEONPOSITION
&& this.screen.map.mapArray[x][y] != Config.MONSTERSTATE) {
Node n = new Node(parrent, x, y);
n.g = calcG(n);
n.h = calcH(n, (int) this.screen.character.mapPos.x,
(int) this.screen.character.mapPos.y);
return n;
}
return null;
}
private float calcG(Node n) {
Node p = n.getParrent();
return p.g + 1;
}
private float calcH(Node n, int targetX, int targetY) {
float dx = Math.abs(n.x - targetX);
float dy = Math.abs(n.y - targetY);
return (float) Math.sqrt((float) (dx * dx) + (dy * dy));
}
private boolean betterIn(Node n, List<Node> l) {
for (Node no : l) {
if (no.x == n.x && no.y == n.y && (no.g + no.h) <= (n.g + n.h))
return true;
}
return false;
}
My Node:
public class Node {
public int x, y;
public float g, h;
private Node parrent;
public Node(Node parrent, int x, int y, float g, float h) {
this.parrent = parrent;
this.x = x;
this.y = y;
this.g = g;
this.h = h;
}
public Node(Node parrent, int x, int y) {
this.parrent = parrent;
this.x = x;
this.y = y;
}
public Node getParrent() {
return parrent;
}
public void setParrent(Node parrent) {
this.parrent = parrent;
}
#Override
public boolean equals(Object o) {
// override for a different compare
return ((Node) o).x == this.x && ((Node) o).y == this.y;
}
#Override
public int hashCode() {
// if x and y are the same they are the same
return x + y;
}
}
If i do use nodes that are blocked but give them a high h they do not walk correct anymore so i dont know whats going wrong here.
Your A* algorithm seems a little bit screwy. But the code's not very clear -- for UP, DOWN, LEFT, RIGHT you repeat the same section (which should be broken out to a method).
It's not clear whether "discovered" nodes are clearly represented -- they should be a Set -- whereas you have "open", "closed" and "successor".
Checking each of your UP,DOWN,LEFT,RIGHT neighbors should be factored out to a method, which you call 4 times with neighborX and neighborY positions.
There isn't a single clear line which correctly tests whether a given neighbor (it's a neighbor, not a successor) has already been "discovered".
Neither am I sure about the post-processing of successors. Viz:
// calc
for (Node suc : successor) {
if (suc.x == (int) this.screen.character.mapPos.x
&& suc.y == (int) this.screen.character.mapPos.y)
return suc;
boolean add = true;
if (betterIn(suc, openList)) // openList und der
add = false;
if (betterIn(suc, closeList)) // closedList
add = false;
if (add)
openList.add(suc);
}
Since you sort "open nodes" on every iteration & pick the probable best, this doesn't really make sense to me & may be erroneous.
Presumably the algorithm should terminate promptly, when all four directions around the character are blocked. Failure to terminate implies that openList is not be processed correctly/ or incorrect nodes are being added.
Put some Log4J logging in & write a simple unit-test to verify it's correct behaviour in these conditions.
I also recommend rolling the 'createNeighbor', 'discovered check' and 'add to successor list' code into one method exploreNeighbor( Node q, int offsetX, int offsetY).
I've improved style & variable naming somewhat. You should also move towards using getters -- getX(), getY() for example.
exploreNeighbor( q, -1, 0); // left
exploreNeighbor( q, +1, 0); // right
exploreNeighbor( q, 0, -1); // up
exploreNeighbor( q, 0, +1); // down
protected boolean exploreNeighbor (Node parent, int offsetX, int offsetY) {
int x = q.getX() + offsetX;
int y = q.getY() + offsetY;
if (x < 0 || x >= screen.map.width)
return null;
if (y < 0 || y >= screen.map.height)
return false;
int content = screen.map.mapArray[x][y];
if (content == Contents.CANTMOVEONPOSITION ||
content == Contents.MONSTERSTATE) {
return false;
}
// represent Neighbor Position;
//
Node n = new Node(parent, x, y);
n.g = calcG(n);
n.h = calcH(n, (int) this.screen.character.mapPos.x,
(int) this.screen.character.mapPos.y);
// check if Discovered yet;
//
if (discoveredSet.contains( n)) {
// already queued or visited.
return false;
}
// queue it for exploration.
openQueue.add( n);
return true;
}
Good luck..
So for this project I wish to input an array containing a number of points. (no specific number). I am confused on how I should approach the methods and constructor because some of the methods call for changes such as grabbing the average of the X or Y values. Am I approaching this project in the right way? Should I be using a clone of the point list, or an array list or what... (note I am only showing part of the PolygonImpl class as an example, they all function similarly) The class point contains :
public class Point {
private double x;
private double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public Point translate(double dx, double dy) {
return new Point(x+dx, y+dy);
}
public double distanceTo(Point p) {
return Math.sqrt((p.x - x)*(p.x -x) + (p.y-y)*(p.y-y));
}
}
public class PolygonImpl implements Polygon {
private double xSum=0;
private double ySum=0;
private ArrayList<Point> points;
private Point[] pList;
private Point a;
PolygonImpl(Point[] pList) {
this.pList = pList.clone();
points = new ArrayList<Point>();
for (int index = 0; index < pList.length; index++) {
points.add(pList[index]);
xSum += pList[index].getX();
ySum += pList[index].getY();
}
}
public Point getVertexAverage() {
double xSum = 0;
ArrayList<Point> vlist = new ArrayList<Point> ();
double ySum = 0;
for (int index = 0; index < vlist.size(); index++) {
xSum = xSum + vlist.get(index).getX();
ySum = ySum + vlist.get(index).getY();
}
return new Point(xSum/getNumSides(), ySum/getNumSides());
}
public int getNumSides() {
return pList.length;
}
public void move(Point c) {
Point newCentroid = new Point(a.getX()+ c.getX(), a.getY() +c.getY());
}
public void scale(double factor) {
ArrayList<Point> points = new ArrayList<Point> ();
for (int index = 0; index < pList.length; index++) {
{ double x = pList[index].getX() *factor;
double y = pList[index].getY() * factor;
Point a = new Point(x,y);
points.add(index,a);
}
}
}
In this case, I don't think you need the extra List objects; they are redundant.
Here is the code just using the pList array.
public class PolygonImpl implements Polygon {
private double xSum=0;
private double ySum=0;
private Point[] pList;
private Point a;
PolygonImpl(Point[] pList) {
this.pList = pList.clone();
for (int index = 0; index < pList.length; index++) {
xSum += pList[index].getX();
ySum += pList[index].getY();
}
}
public Point getVertexAverage() {
double xSum = 0;
double ySum = 0;
for (int index = 0; index < pList.length; index++) {
xSum = xSum + pList[index].getX();
ySum = ySum + pList[index].getY();
}
return new Point(xSum/getNumSides(), ySum/getNumSides());
}
public int getNumSides() {
return pList.length;
}
public void move(Point c) {
Point newCentroid = new Point(a.getX()+ c.getX(), a.getY() +c.getY());
}
public void scale(double factor) {
for (int index = 0; index < pList.length; index++)
{
double x = pList[index].getX() *factor;
double y = pList[index].getY() * factor;
Point a = new Point(x,y);
pList[index] = a;
}
}
There are at least three things here that need pointing out.
The first is the difference between "clone" and "copy." .clone is a shallow copy; that means if you change the clone, you also change the original.
The second is regarding your use of collections. Since a polygon is a collection of points, either an array or an ArrayList would be approriate, but there is no point in using bother, or, if there is, think long and hard about whether it's a good point, and then explain in the inline documentation why it matters, else it will come back to bite you in the form of using one when you mean the other.
Third is scope. Your polygon class has instance variables (xSum and ySum) that are occluded by variables with the same name in getVertexAverage. The way they are used in getVertexAverage is appropriate in itself; the instance variables are only useful if you mean to cache the sums, which becomes more questionable, because every operation that changes a point invalidates the instance values.
Instance values are for storing data about an object (in this case, points are reasonable); instance methods are for operating on that data at a given state (in this case, the average).
Keeping this in mind, you can now understand how the move method isn't finished :)