Related
So I'm currently learning Java, and i struggle a lot to get my Code working.
I made a "Fun" Code in which some circles are popping up and it calculates how many of them are on the Upper Side of the Screen. (I know, some silly code.)
I'm coding it in the "Processing" Environment, Language is Java.
Here's my main File:
Circle[] circles = new Circle[50];
int index = 0;
boolean finished = false;
void setup() {
size(900, 900);
background(0);
for(int i = 0; i < circles.length; i++) {
circles[i] = new Circle();
}
if(finished = true) {
}
}
void draw() {
if(index < circles.length) {
circles[index].show(circles);
index++;
} else {
finished = true;
}
}
void count(Circle[] arr) {
int n = 0;
for(Circle c : arr) {
if(c.getY() > height / 2) {
n++;
}
}
println(n);
}
And here's the "Problem" Circle class:
class Circle {
private int x, y;
private float r = random(10, 25);
Circle() {
this.x = Math.round(random(0 + r, width - r));
this.y = Math.round(random(0 + r, height - r));
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public void show(Circle[] arr) {
if(isColliding(arr)) {
this.x = Math.round(random(0 + r, width - r));
this.y = Math.round(random(0 + r, height - r));
} else {
ellipse(this.x, this.y, r * 2, r * 2);
stroke(255);
fill(255);
}
}
public boolean isColliding(Circle[] arr) {
boolean result = false;
if(arr == null) {
result = false;
} else {
for(Circle c : arr) {
float d = dist(c.getX(), c.getY(), this.x, this.y);
if(d < r * 2) {
result = true;
println("Collision at: " + c.getX() + " " + c.getY());
break;
}
}
}
return result;
}
}
As you can see, i already have a isColliding Method, and the Outputs in the Console seem to be right, however it won't work in the show() Method, the Circles won't stop intersecting each other.
So how can i make it work, that the Position is re-calculated when it is colliding?
Are you sure your collision method works? Unless I'm missing something, it should ALWAYS return true as you're passing in an array that includes itself.
That aside, I'd start looking at how your show() logic is laid out. You are checking for overlap, and then assigning a new random position if it finds any. This new position could very likely be on a circle that has already been drawn and in a good position.
Put your re-positioning in a loop so that it checks to make sure that it didn't just place itself onto an existing circle.
public void show(Circle[] arr)
{
/*
You could potentially get into a situation where you will NEVER find an empty spot.
Add an escape method for the loop.
*/
int failLimit = 500;
while(failLimit-- > 0 && isColliding(arr))
{
this.x = Math.round(random(0 + r, width - r));
this.y = Math.round(random(0 + r, height - r));
}
ellipse(this.x, this.y, r * 2, r * 2);
stroke(255);
fill(255);
}
You could simplify this and make it a bit more efficient by spawning in circles one at a time, checking to make sure their positions are good then.
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 wrote this path finding algorithm that should be working, but I'm getting tons of java.lang.ArrayIndexOutOfBoundsException. The goal of the program is to find the shortest path from one point to another that costs the least. Here's my code:
public boolean travel(int[][] path, int cX, int cY, int eX, int eY)
{
boolean returned = false;
System.out.println("the current X position on the GRID is: "+cX+"the current y position on the GRID is: "+cY);
path[cX][cY]=1;
if(cost>lowestCost - grid[cX][cY]){
return false;
}
cost += grid[cX][cY];
if(cX>=eX && cY>=eY){
return true;
}
if(cX+1>=eX && cY+1<eY){
return false;
}
if(cY+1>=eY && cX+1<eX){
return false;
}
if(travel(path,cX+1,cY+1,eX,eY)==true){
returned=true;
replace(newBest, path);
}
if(travel(path,cX,cY+1,eX,eY)==true){
returned=true;
replace(newBest, path);
}
if(travel(path,cX+1,cY,eX,eY)==true){
returned=true;
replace(newBest, path);
}
return(returned);
}
cX is the current X position in the array, cY is the current Y position in the array, eX and eY are the destination coordinates. path[][] is the array used to store the path. If you have any answers please tell me! also don't suggest any other algorithms, just some edits to the actual code. grid[][] is the array that stores the cost to go from one to another. Thanks very much
if(travel(newBest,0,0,rows,columns)==true)
{
lowestCost=cost;
}
This is how I call the method to find the shortest path.
This is the entire applet:
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class GridWorld extends Applet implements Runnable, MouseListener, KeyListener, MouseMotionListener
{
public int worldx;
public int worldy;
public int columns;
public int rows;
public int destX, destY;
public int cost, lowestCost;
public boolean sizeD;
public int[][] grid;
public int[][] prevBest;
public int[][] newBest;
Graphics bufferGraphics; //Set up double buffer
Image offscreen;
Thread thread;//Sets up a Thread called thread
public void init()
{
worldx=1000;
worldy=1000;
cost=0;
lowestCost=5000;
sizeD=false;
columns=5;
rows=5;
destX=0;
destY=0;
grid= new int[rows][columns];
prevBest= new int[rows][columns];
newBest = new int[rows][columns];
offscreen = createImage(worldx,worldy); //create a new image that's the size of the applet DOUBLE BUFFER SET UP
bufferGraphics = offscreen.getGraphics(); //set bufferGraphics to the graphics of the offscreen image. DOUBLE BUFFER SET UP
addKeyListener(this);//setup all the listeners
addMouseListener(this);//setup all the listeners
addMouseMotionListener(this);//setup all the listeners
thread = new Thread(this); //constructs a new thread
thread.start(); //starts the thread
}//init()
public void fillGrid()
{
prevBest= new int[rows][columns];
newBest = new int[rows][columns];
lowestCost = 0;
for(int ro = 0;ro<rows;ro++)
{
for(int col = 0;col<columns;col++)
{
grid[ro][col]=(int)(Math.random()*100);
newBest[ro][col]=0;
prevBest[ro][col]=0;
if(ro==col)
{
prevBest[ro][col]=1;
lowestCost+=grid[ro][col];
}
}
}
destX=(rows-1);
destY=(columns-1);
}
public boolean baseCase(int ct, int lowct, int destR, int destC, int cX, int cY)
{
boolean returned=false;
if(ct>=lowct)
{
returned=true;
}
if(cX+1==rows)
{
returned=true;
}
if(cY+1==columns)
{
returned=true;
}
if(cX==destR && cY==destC)
{
returned=true;
}
return(returned);
}
public boolean isValid(int x, int y, int[][] path, int eX, int eY) {
//not valid if: cordinates are into grid dimensions
if (!((x >= 0 && x < grid.length) && (y >= 0 && y < grid.length)))
return false;
//valid if: not visited yet, or is destiny
if (path[x][y] == 0 || (x == eX && y == eY))
return true;
return true;
}
/*public int traverse(int steps, int destR, int destC, int curX, int curY)
{
int direction = 0;
if(cost>=lowestCost)
{
//System.out.println("Greater cost Base Case");
direction=4;
}
if(curX+1>=destR && curY+1<destC)
{
System.out.println("Reached the farthest row Base Case");
direction=1;
}
if(curY+1>=destC && curY+1<destR)
{
System.out.println("Reached the farthest columns Base Case");
direction=2;
}
if(curX+1>=destR && curY+1>=destC)
{
System.out.println("At destination Base Case");
direction=4;
}
switch(direction)
{
case 0: newBest[curX][curY]=1;
cost+=grid[curX][curY];
System.out.println("the current X position on the GRID is: "+curX+"the current y position on the GRID is: "+curY);
return(traverse(steps+1,destR,destC,curX+1,curY+1)); //diag
case 1: newBest[curX][curY]=1;
cost+=grid[curX][curY];
return(traverse(steps+1,destR,destC,curX,curY+1)); //right
case 2: newBest[curX][curY]=1;
cost+=grid[curX][curY];
return(traverse(steps+1,destR,destC,curX+1,curY));//down
case 3:
return(5000);
case 4: System.out.println("the Grid's cost is: "+cost);
return(cost);
default: return(0);
}
}*/
public int[][] replace(int[][] p1, int[][] p2)
{
for(int col=0;col<columns;col++)
{
for(int ro=0;ro<rows;ro++)
{
p1[ro][col]=p2[ro][col];
}
}
return(p1);
}
public boolean travel(int[][] path, int cX, int cY, int eX, int eY)
{
boolean returned = false;
System.out.println("cX: "+ cX+" , cY: "+ cY+", eX: "+eX+", eY: " +eY+" Path 1 length: "+path[0].length+" Path 2 length: "+path[1].length);
path[cX][cY]=1;
if(cost>lowestCost - grid[cX][cY]){
System.out.println("1");
return false;
}
cost += grid[cX][cY];
}
if(travel(path,cX+1,cY+1,eX,eY)==true && isValid(cX+1,cY+1,newBest,eX,eY)){
System.out.println("the current X position on the GRID is: "+cX+"the current y position on the GRID is: "+cY);
returned=true;
replace(newBest, path);
}
if(travel(path,cX,cY+1,eX,eY)==true && isValid(cX,cY+1,newBest,eX,eY)){
System.out.println("the current X position on the GRID is: "+cX+"the current y position on the GRID is: "+cY);
returned=true;
replace(newBest, path);
}
if(travel(path,cX+1,cY,eX,eY)==true && isValid(cX+1,cY,newBest,eX,eY)){
System.out.println("the current X position on the GRID is: "+cX+"the current y position on the GRID is: "+cY);
returned=true;
replace(newBest, path);
}
return(returned);
}
public void paint(Graphics g)
{// paint() is used to display things on the screen
setSize(worldx,worldy);
//clear the offscreen image
bufferGraphics.clearRect(0,0,worldx,worldy);
bufferGraphics.setColor(Color.black);
//bufferGraphics.fillRect(0,0,worldx,worldy);
if(sizeD==true)
{
if(travel(newBest,0,0,rows,columns)==true)
{
lowestCost=cost;
}
}
for(int ro = 0;ro<rows;ro++)
{
for(int col = 0;col<columns;col++)
{
if(sizeD==true)
{
if(newBest[ro][col]==1)
{
bufferGraphics.setColor(Color.red);
bufferGraphics.fillRect((50*col),(50*ro),50,50);
bufferGraphics.setColor(Color.black);
}
if(prevBest[ro][col]==1)
{
bufferGraphics.setColor(Color.gray);
bufferGraphics.fillRect((50*col),(50*ro),50,50);
bufferGraphics.setColor(Color.black);
}
bufferGraphics.drawLine(0,(50*ro),50*columns,(50*ro));
bufferGraphics.drawLine((50*col),0,(50*col),50*rows);
bufferGraphics.drawString(""+grid[ro][col],(50*ro)+25,(50*col)+25);
}
}
}
if(sizeD==false)
{
bufferGraphics.drawRect(200,300,100,100);
bufferGraphics.drawString("5",250,350);
bufferGraphics.drawRect(400,300,100,100);
bufferGraphics.drawString("10",450,350);
bufferGraphics.drawRect(600,300,100,100);
bufferGraphics.drawString("20",650,350);
}
g.drawImage(offscreen,0,0,worldx,worldy,this);//Draw the screen
}// paint()
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent e){
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
System.out.println("Mouse entered");
}
public void mouseExited(MouseEvent e)
{
System.out.println("Mouse exited");
}
public void mouseClicked(MouseEvent e)
{
System.out.println("Mouse clicked (# of clicks: "+ e.getClickCount() + ")");
int mX=e.getX();
int mY=e.getY();
if(new Rectangle(200,300,100,100).contains(mX,mY) && sizeD==false)
{
columns=5;
rows=5;
grid= new int[rows][columns];
fillGrid();
sizeD=true;
}
if(new Rectangle(400,300,100,100).contains(mX,mY) && sizeD==false)
{
columns=10;
rows=10;
grid= new int[rows][columns];
fillGrid();
sizeD=true;
}
if(new Rectangle(600,300,100,100).contains(mX,mY) && sizeD==false)
{
columns=20;
rows=20;
grid= new int[rows][columns];
fillGrid();
sizeD=true;
}
}
public void keyPressed( KeyEvent event )
{
String keyin; // define a non‐public variable to hold the string representing the key input
keyin = ""+event.getKeyText( event.getKeyCode());
System.out.println("Key pressed "+keyin);
}//keyPressed()
public void keyReleased( KeyEvent event )
{
String keyin;
keyin = ""+event.getKeyText( event.getKeyCode());
System.out.println ("Key released: "+ keyin);
}//keyReleased()
public void keyTyped( KeyEvent event )
{
char keyin;
keyin = event.getKeyChar(); //getKeyChar() returns the character of the printable key pressed.
System.out.println ("Key Typed: "+ keyin);
}//keyTyped()
public void update (Graphics g)
{
paint(g);
}//Update the graphics
public void run()
{
while(true) // this thread loop forever and runs the paint method and then sleeps.
{
repaint();
try {
thread.sleep(50);
}
catch (Exception e){ }
}//while
}// run()
}//Applet
You are getting tons of java.lang.ArrayIndexOutOfBoundsException because you didn't proper manage the flow to enter the last three if-block. The code will enter the last three if-block since you only return the state at return(returned); even you have set boundary check (first two if-block). So, path[cX][cY] and grid[cX][cY] may encounter index out of bound when cX and cY is a large value (depends on the index setup of path and grid).
Also, the checking logic for first four if-block is not in correct order and you should return state when the condition meet.
The first four if-block should rearrange as:
if(cost>=lowestCost){
return false;
}
if(cX==eX && cY==eY){
return true;
}
if(cX+1>=eX && cY+1<eY){
return false;
}
if(cY+1>=eY && cX+1<eX){
return false;
}
BTW, please ensure your boundary checking (cX+1>=eX && cY+1<eY and cY+1>=eY && cX+1<eX) is correct. This will makes the code cannot access points in ([eX-1,eX],[0,eY-2]) and ([0,eX-2],[eY-1,eY]).
One more point, you might get trouble for cost>=lowestCost for rare cases like all possible shortest path has cost equal to pre-set value of lowestCost. One way to handle this is remove equal sign.
One more again, you might get trouble for cost>=lowestCost for extreme cases like cost = Integer.MAX_VALUE + 1. To handle this, you may try
if(cost>lowestCost - grid[cX][cY]){
return false;
}
cost += grid[cX][cY];
Here is a recursive solution with backtraking, code is explained:
public class FindShortestPath {
//the map is represented as a graph, and the graph is represented as an adyacent matrix
int map[][] = {{-1, 1, 1, 20}, //-1 = there is no edge between two vertexs
{-1, -1, 3, 1},
{-1, -1, -1, 1},
{-1, -1, -1, -1}};
int distSol = Integer.MAX_VALUE; //we want to minimize this value
List<Integer> solution = new ArrayList<Integer>(); //path solution
public static void main(String[] args) {
new FindShortestPath().start();
}
void start() {
/* In this case we find shortest path from 0 to 3 */
findShortestPath(new boolean[map.length], 0, 3, 0, new ArrayList<Integer>());
System.out.println("Distance: " + distSol);
System.out.println(solution.toString());
}
/**
*
* #param visit auxiliary array to mark visited
* #param c actual vertex
* #param destiny destiny vertex
* #param distAct actual distance traveled
* #param path actual path traveled
*/
void findShortestPath(boolean visit[], int c, int destiny, int distAct, List<Integer> path) {
if (c == destiny) { //base case: we reach destiny
if (distAct <= distSol) { //check if actual distance is better than solution
distSol = distAct;
solution = new ArrayList<Integer>(path); //replace old solution
solution.add(destiny);
}
} else { //recursive case
path.add(c); //add actual vertex as a possible candidate into actual path
visit[c] = true;
for (int i = 0; i < map.length; i++) { //visit every adyacent vertex that was no visited yet
if (map[c][i] != -1 && !visit[i]) {
findShortestPath(visit, i, destiny, distAct + map[c][i], path);
}
}
path.remove(path.size() - 1); //remove this vertex from the path
visit[c] = false;
}
}
}
Output for this example:
Distance: 2
[0, 2, 3]
EDIT
The OP wants the map to be a grid:
public class FindShortestPath2 {
int lowestCost = Integer.MAX_VALUE;
int grid[][] = {{0, 9, 0, 0},
{ 0, 5, 1, 1},
{ 0, 0, 2, 0},
{ 0, 0, 0, 0}};
int sol[][] = new int[4][4]; //this is the path solution
public static void main(String[] args) {
new FindShortestPath2().start();
}
void start() {
travel(new int[4][4], 0, 0, 2, 2, 0);
System.out.println("Lowest cost: " + lowestCost);
printPath(sol); //print solution
}
public void travel(int[][] path, int cX, int cY, int eX, int eY, int cost) {
if (cX == eX && cY == eY) { //reach destiny cordinates
if (cost < lowestCost) {
lowestCost = cost;
path[cX][cY] = 1;
replace(sol, path);
}
} else {
path[cX][cY] = 1; //mark path
if (isValid(cX + 1, cY + 1, path, eX, eY)) { //move in diagonal (rigth-down)
travel(path, cX + 1, cY + 1, eX, eY, cost + grid[cX + 1][cY+1]);
}
if (isValid(cX, cY + 1, path, eX, eY)) { //move rigth
travel(path, cX, cY + 1, eX, eY, cost + grid[cX][cY+1]);
}
if (isValid(cX + 1, cY, path, eX, eY)) { //move down
travel(path, cX + 1, cY, eX, eY, cost + grid[cX + 1][cY]);
}
path[cX][cY] = 0; //unmark path
}
}
boolean isValid(int x, int y, int[][] path, int eX, int eY) {
//not valid if: cordinates are into grid dimensions
if (!((x >= 0 && x < grid.length) && (y >= 0 && y < grid.length)))
return false;
//valid if: not visited yet, or is destiny
if (path[x][y] == 0 || (x == eX && y == eY))
return true;
return true;
}
void replace(int[][] p1, int[][] p2) {
for (int col = 0; col < p1.length; col++) {
for (int ro = 0; ro < p1.length; ro++) {
p1[ro][col] = p2[ro][col];
}
}
}
void printPath(int[][] p) {
for (int col = 0; col < p.length; col++) {
for (int ro = 0; ro < p.length; ro++) {
System.out.print(p[col][ro] + " ");
}
System.out.println();
}
System.out.println();
}
}
Output for this example:
Lowest cost: 2
1 0 0 0
1 0 0 0
0 1 1 0
0 0 0 0
This may be off topic, but have you tried implementing Dijkstra's Algorithm in Java. In the following algorithm, the code u := vertex in Q with min dist[u], searches for the vertex u in the vertex set Q that has the least dist[u] value. length(u, v) returns the length of the edge joining (i.e. the distance between) the two neighbor-nodes u and v. The variable alt on line 17 is the length of the path from the root node to the neighbor node v if it were to go through u. If this path is shorter than the current shortest path recorded for v, that current path is replaced with this alt path. The previous array is populated with a pointer to the "next-hop" node on the source graph to get the shortest route to the source. (taken from Wikipedia's artice on Dijkstra's Algorithm. This work is not my own.
function Dijkstra(Graph, source):
dist[source] := 0 // Distance from source to source
for each vertex v in Graph: // Initializations
if v ≠ source
dist[v] := infinity // Unknown distance function from source to v
previous[v] := undefined // Previous node in optimal path from source
end if
add v to Q // All nodes initially in Q (unvisited nodes)
end for
while Q is not empty: // The main loop
u := vertex in Q with min dist[u] // Source node in first case
remove u from Q
for each neighbor v of u: // where v has not yet been removed from Q.
alt := dist[u] + length(u, v)
if alt < dist[v]: // A shorter path to v has been found
dist[v] := alt
previous[v] := u
end if
end for
end while
return dist[], previous[]
end function
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..
I am working on a method to recursively solve made up of cells.
The method just quite isn't working. Any suggestions would be appreciated.
Parameters: srow = starting x value. scol = staring y value erow = end
x value. ecol = end y value. L = Linked List of solved path points
Code:
private InputGraphicMaze2 maze;
private int R, C;
//code added by me
private String[] [] cell; //an array to keep track of cells that are proven dead ends.
public YourMazeWithPath2()
{
// an R rows x C columns maze
maze = new InputGraphicMaze2();
R=maze.Rows(); C=maze.Cols();
//code added by me
cell = new String[R+2][C+2];
for (int i=0; i<R+2; i++) {
for (int k=0; k<C+2; k++) {
cell[i][k] = "no";
}
}
// Path holds the cells of the path
LinkedList<Point> Path = new LinkedList<Point>();
// Create the path
CreatePath(maze, 1, 1, R, C, Path);
// show the path in the maze
maze.showPath(Path);
}
private void setDead(int x, int y) {
cell[x][y] = "dead";
}
private void setVisited(int x, int y) {
cell[x][y] = "visited";
}
public boolean CreatePath(InputGraphicMaze2 maze,
int srow, int scol, int erow, int ecol, LinkedList<Point> L)
{
int x = srow;
int y = scol;
Point p = new Point(x, y);
if ((x<1) || (y<1) || (x>R) || (y>C)) {
return false; //cell is out of bounds
}
else if ((x==R) && (y==C)) {
return true; // cell is the exit cell
}
else {
if ((maze.can_go(x, y, 'U')) && (x!=1) && (!cell[x-1][y].equals("dead")) && (!cell[x-1][y].equals("visited"))) {
L.addLast(p);
setVisited(x,y);
CreatePath(maze, x-1, y, R, C, L);
return false;
}
else if ((maze.can_go(x, y, 'R')) && (y!=C) && (!cell[x][y+1].equals("dead")) && (!cell[x][y+1].equals("visited"))) {
L.addLast(p);
setVisited(x, y);
CreatePath(maze, x, y+1, R, C, L);
return false;
}
else if ((maze.can_go(x, y, 'D')) && (x!=R) && (!cell[x+1][y].equals("dead")) && (!cell[x+1][y].equals("visited"))) {
L.addLast(p);
setVisited(x, y);
CreatePath(maze, x+1, y, R, C, L);
return false;
}
else if ((maze.can_go(x, y, 'L')) && (y!=1) && (!cell[x][y-1].equals("dead")) && (!cell[x][y-1].equals("visited"))) {
L.addLast(p);
setVisited(x, y);
CreatePath(maze, x, y-1, R, C, L);
return false;
}
else {
if ((maze.can_go(x, y, 'U')) && (x!=1) && (cell[x][y-1].equals("visited"))) {
setDead(x, y);
if (L.contains(p))
L.remove(p);
CreatePath(maze, x-1, y, R, C, L);
return false;
}
else if ((maze.can_go(x, y, 'R')) && (y!=C) && (cell[x][y+1].equals("visited"))) {
setDead(x, y);
if (L.contains(p))
L.remove(p);
CreatePath(maze, x, y+1, R, C, L);
return false;
}
else if ((maze.can_go(x, y, 'D')) && (x!=R) && (cell[x+1][y].equals("visited"))) {
setDead(x, y);
if (L.contains(p))
L.remove(p);
CreatePath(maze, x+1, y, R, C, L);
return false;
}
else if ((maze.can_go(x, y, 'D')) && (y!=1) && (cell[x][y-1].equals("visited"))) {
setDead(x, y);
if (L.contains(p))
L.remove(p);
CreatePath(maze, x, y-1, R, C, L);
return false;
}
else {
return false;
}
}
}
}
From Another similar thread, just for seeing the problem in a less verbose language, take a look at
this tiny JS recursive maze solver made by user #Sergey Rudenko
var map = [
[1,1,0,0,0,0,0,0],
[0,1,1,0,0,0,0,0],
[1,1,1,0,0,0,0,0],
[1,0,0,1,1,1,1,1],
[1,1,0,0,1,0,0,1],
[0,1,1,0,1,0,0,1],
[1,1,1,0,1,0,0,1],
[1,0,0,0,1,1,1,1]
]
var goalx = 7;
var goaly = 7;
function findpath(x,y){
// illegal move check
if (x < 0 || x > (map[0].length -1) || y < 0 || y > (map.length - 1)) return false; //if it is outside of map
if (map[y][x]==0) return false; //it is not open
// end move check
if (x== goalx && y== goaly){
console.log('Reached goal at: ' + x + ':' + y);
return true; // if it is the goal (exit point)
}
if(map[y][x] == 9 || map[y][x] == 8)
return false;
console.log('Im here at: ' + x + ':' + y);
map[y][x]=9; //here marking x,y position as part of solution path outlined by "9"
if(findpath(x+1,y))
return true;
if(findpath(x,y+1))
return true;
if(findpath(x,y-1))
return true;
if(findpath(x-1,y))
return true;
return false;
};
findpath(0, 0);
JSfiddle
Yep. Its tiny, simplistic, naive and lacking but heck, its recursive and it works!
Besides, you see clearly common parts to many maze traversing algorithm.
For a more serious reading, this page has excellent in-depth-but-not-scientific-paper tutorials about many game related algorithms.
There are some pertinent questions to answer when shopping for an algorithm:
Need any solution?
Need every solution?
Need the fastest?
Whats the topography of the maze? A grid? A graph?
Want to implement movement cost in the future?
Want to implement heuristics to choose best route?
Finally, if you didn't come across it yet, check a* algorithm. Very popular.
Have fun!
This is a basic graph-traversal problem. I suggest you use dfs as opposed to bfs. Pretty much any textbook on algorithms and datastructure will have the implementation.
You simply have to tweak the recursive part to stop searching once you have reached the goal. On the other hand, if you are looking for all paths to the goal, just do all-to-all path and then go from there. For hints, you can look up Bellman–Ford or Dijkstra's algorithm (http://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm). Again, any good textbook with a chapter on graphs.