I'm writing a sliding block solver that has a list of block objects (which contains block size and location of upper left corner), and a 2D array that represents the tray. Wherever there is a block, that location in the array points to the block object, otherwise it is null.
In my solver I'm generating possible moves that haven't been seen, hashing them, then choosing one to do (which changes the tray layout) and calling the solver recursively on the new tray layout. When there are no more possible move layouts that haven't been seen before I return the call, reverse the last move and continue checking the previous call, and so on until either it is solved or I run out of moves (no solution).
The problem is, I'm getting a Null Pointer Exception when I make a move. The weird thing is that it only happens after quite a few recursive calls. The program runs through several calls/moves fine, and then it seems to mess up.
generateMoves() tests if a move has been seen before by calling move(), and then reversing the move once it has checked. I think the Null Pointer is happening after it calls move(), and move() is setting toMove = layout[][]. Evidently it is looking up a position in the array that is null instead of one with the block. It seems there is a discrepancy between the list of blocks and the Tray array... Because when move() then calls setTrayAfterMove() it throws the exception. What I can't figure out is why it works for several recursive calls to solveHelper() but then breaks.
import java.io.*;
import java.util.*;
public class Solver {
Tray initial;
Tray goal;
HashSet<Integer> visited;
LinkedList<Integer> movesToSolution; // list of moves leading to solution
int recursionCounter;
boolean isSolved;
public Solver(String initial, String goal) {
this.initial = new Tray(initial);
this.goal = new Tray(this.initial, goal);
visited = new HashSet<Integer>();
movesToSolution = new LinkedList<Integer>();
recursionCounter = 0;
isSolved = false;
}
public void solve() {
if (goal.equals(initial)) {
System.out.println("Solver finished no moves");
return;
}
solveHelper(initial);
if (movesToSolution.isEmpty()) {
System.out.println("No solution");
System.exit(1);
}
printMoves();
System.out.println("Solver finished");
}
private void solveHelper(Tray t) {
Stack<Integer> possibleMoves = new Stack<Integer>();
int lastMoveMade = 0;
if (recursionCounter > 5000 || isSolved) {
return;
}
if (goal.equals(t)) {
isSolved = true;
// movesToSolution.addFirst(lastMoveMade);
return;
}
recursionCounter++;
LinkedList<Integer> movesToAdd = t.generateMoves();
Iterator<Integer> movesIter = movesToAdd.iterator();
while (movesIter.hasNext()) {
possibleMoves.push(movesIter.next());
}
while (!possibleMoves.isEmpty()) {
lastMoveMade = possibleMoves.pop();
boolean isMoved = t.move(lastMoveMade, false);
if (isMoved) {
int moveHash = t.hashCode();
visited.add(moveHash);
solveHelper(t);
}
if (isSolved) {
movesToSolution.addFirst(lastMoveMade);
return;
}
}
t.move(lastMoveMade, true);
return;
}
public void printMoves() {
for (Integer move : movesToSolution) {
System.out.println(move);
}
}
public class Tray {
private int length; // number of rows
private int width; // number of columns
private LinkedList<Block> blocks;
private Block[][] layout;
public Tray(String file) {
blocks = new LinkedList<Block>();
try {
Scanner s = new Scanner(new FileReader(file));
length = s.nextInt();
width = s.nextInt();
layout = new Block[width][length];
while (s.hasNextLine()) {
int l = s.nextInt();
int w = s.nextInt();
int r = s.nextInt();
int c = s.nextInt();
Block b = new Block(l, w, r, c);
blocks.add(b);
for (int blockX = b.col; blockX < b.col + b.width; blockX++) {
for (int blockY = b.row; blockY < b.row + b.length; blockY++) {
layout[blockX][blockY] = b;
}
}
s.nextLine();
// isOK();
}
} catch (FileNotFoundException e) {
System.out.println("File not found");
}
}
public Tray(Tray t, String file) {
blocks = new LinkedList<Block>();
try {
this.length = t.length;
this.width = t.width;
Scanner s = new Scanner(new FileReader(file));
layout = new Block[this.width][this.length];
while (s.hasNextLine()) {
int l = s.nextInt();
int w = s.nextInt();
int r = s.nextInt();
int c = s.nextInt();
Block b = new Block(l, w, r, c);
blocks.add(b);
for (int blockX = b.col; blockX < b.col + b.width; blockX++) {
for (int blockY = b.row; blockY < b.row + b.length; blockY++) {
layout[blockX][blockY] = b;
}
}
s.nextLine();
// isOK();
}
} catch (FileNotFoundException e) {
System.out.println("File not found");
}
}
public void print() {
for (Block b : blocks) {
System.out.println(b.length + " " + b.width + " " + b.col + " "
+ b.row);
}
}
public boolean equals(Object o) {
for (int x = 0; x < this.width; x++) {
for (int y = 0; y < this.length; y++) {
if (this.layout[x][y] != null
&& (((Tray) o).layout[x][y] == null || !((Tray) o).layout[x][y]
.equals(this.layout[x][y]))) {
return false;
}
}
}
return true;
}
public int hashCode() {
// TODO come up with hashcode unique to layout taking in
// consideration block at each coordinate, size of block
int hashCode = 0;
for (Block b : blocks) {
hashCode += (17 * (b.width * b.col)) + (7 * (b.length * b.row));
}
return hashCode;
}
public boolean isOK() {
Block[][] trayChecker = new Block[width][length];
Iterator<Block> blockIter = blocks.iterator();
while (blockIter.hasNext()) {
Block b = blockIter.next();
for (int x = b.col; x < x + b.width; x++) {
for (int y = b.row; y < y + b.length; y++) {
if (trayChecker[x][y] != null) {
throw new IllegalStateException(
"Two blocks cannot be in the same location");
}
if (x < 0 || x > width || y < 0 || y > length) {
throw new IllegalStateException(
"Block must be completely on the board");
}
trayChecker[x][y] = b;
}
}
}
return true;
}
// only returns possible valid moves that haven't been seen before
public LinkedList<Integer> generateMoves() {
LinkedList<Integer> movesToTry = new LinkedList<Integer>();
// TODO: generate moves that haven't been seen
int[] moveDir = { -10, 10, -1, 1 };
for (Block b : blocks) {
for (int m : moveDir) {
if (canMove(b, m)) {
int trayMove = createMove(b, m);
move(trayMove, false);
if (!visited.contains(hashCode())) {
movesToTry.add(trayMove);
}
move(trayMove, true); // reverse the move
}
}
}
return movesToTry;
}
public boolean canMove(Block b, int dir) {
int tmp = Math.abs(dir);
int y = tmp % 10;
int x = tmp / 10;
if (dir < 0) {
x = -x;
y = -y;
}
if ((b.col + x < 0 || b.col + b.width + x > this.width)
|| (b.row + y < 0 || b.row + b.length + y > this.length)) {
return false;
}
if (x == 0) {
for (int xBlock = b.col; xBlock < b.col + b.width; xBlock++) {
if (layout[xBlock][b.row + y] != null) {
return false;
}
// } else if(x > 0 && layout[xBlock][b.row + y + b.length -
// 1] != null) {
// return false;
// }
}
}
if (y == 0) {
for (int yBlock = b.row; yBlock < b.row + b.length; yBlock++) {
if (layout[b.col + x][yBlock] != null) {
return false;
}
// } else if(x > 0 && layout[b.col + x + b.width -
// 1][yBlock] != null) {
// return false;
// }
}
}
return true;
}
// only takes valid input
public boolean move(int moveDirections, boolean reverse) {
Block toMove = null;
if (moveDirections == 0) {
return false;
}
// System.out.println(moveDirections + " " + recursionCounter);
int tmp = Math.abs(moveDirections);
int moveY = tmp % 10;
tmp /= 10;
int moveX = tmp % 10;
tmp /= 10;
int blockY = tmp % 1000;
tmp /= 1000;
int blockX = tmp;
System.out.println(blockX + " + " + blockY);
if (reverse) {
if (moveDirections > 0) {
toMove = layout[blockX + moveX][blockY + moveY];
} else {
toMove = layout[blockX - moveX][blockY - moveY];
}
setTrayAfterMove(toMove, true);
if (moveDirections < 0) {
toMove.col += moveX;
toMove.row += moveY;
} else {
toMove.col -= moveX;
toMove.row -= moveY;
}
setTrayAfterMove(toMove, false);
} else {
toMove = layout[blockX][blockY];
setTrayAfterMove(toMove, true);
if (moveDirections < 0) {
toMove.col -= moveX;
toMove.row -= moveY;
} else {
toMove.col += moveX;
toMove.row += moveY;
}
setTrayAfterMove(toMove, false);
}
return true;
// 256x256
// 1x256 23x256
// 100x01 100x001 100x100
// 1x01 1x001 1x100
// 10x01 10x001 10x100
}
private int createMove(Block b, int dir) {
// multiply b.x to get 8 digits
// multiply bh .y to get 5 digits
int move = b.col * 100000;
move += (b.row * 100);
move += Math.abs(dir);
if (dir < 0) {
move *= -1;
}
return move;
}
private void setTrayAfterMove(Block b, boolean isBeforeMove) {
for (int blockX = b.col; blockX < b.col + b.width; blockX++) {
for (int blockY = b.row; blockY < b.row + b.length; blockY++) {
if(isBeforeMove) {
layout[blockX][blockY] = null;
} else {
layout[blockX][blockY] = b;
}
}
}
}
}
public class Block {
private int length;
private int width;
private int row;
private int col;
public Block(int l, int w, int r, int c) {
length = l;
width = w;
row = r;
col = c;
}
public boolean equals(Block b) {
return this.length == b.length && this.width == b.width
&& this.row == b.row && this.col == b.col;
}
}
public static void main(String[] args) {
if (args.length < 2 || args.length > 3) {
throw new IllegalArgumentException(
"Must have at least 2 and no more than 3 arguments");
}
String initialLayout = args[0];
String goalLayout = args[1];
String debug = "";
if (args.length == 3) {
if (args[0].substring(0, 2).equals("-o")) {
debug = args[0].substring(2, args[0].length());
switch (debug) {
// cases for debugging arguments go here
}
} else {
throw new IllegalArgumentException(
"First argument must start with -o");
}
initialLayout = args[1];
goalLayout = args[2];
}
Solver s = new Solver(initialLayout, goalLayout);
s.solve();
}
}
Could someone please take a look at my code? Suggestions on how to improve efficiency are also welcome. Thanks!
Instead of solving your problem, let me give you some advice on how you can root cause this yourself.
Are you developing in and IDE? If you aren't, start now.
Have you ever used a debugger? If not, start now.
Have you ever set a conditional breakpoint? If not, start now.
Set a conditional breakpoint on the variable that is null, with the condition being that the variable is null. Run your program in debug mode and see whats going on.
If the community solves this problem for you, you haven't learned anything about becoming a better programmer. Make it a point to solve this problem yourself - otherwise you are just postponing the inevitable : becoming a mediocre programmer.
Related
I tried this example with only 4 points, it works with:
-1 -1
2 1
4 -2
1 -4
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class CarreSimple {
static int distance(Point point1, Point point2) {
int dist = (int) (Math.pow(point1.getX() - point2.getX(), 2) + Math.pow(point1.getY() - point2.getY(), 2));
dist = (int) Math.sqrt(dist);
return dist;
}
public static void main(String[] args) {
boolean EstCarre = false;
Point[] tb = new Point[10000];
int i = 0;
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("C:/Users/walid/Downloads/points.txt"));
String line;
while ((line = br.readLine()) != null) {
String[] parts = line.split(" ");
int part1 = Integer.parseInt(parts[0]);
int part2 = Integer.parseInt(parts[1]);
Point p = new Point(part1, part2);
tb[i] = p;
System.out.println("X " + tb[i].getX() + " y " + tb[i].getY());
i++;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
int[] distances = new int[3];
distances[0] = distance(tb[0], tb[1]);
distances[1] = distance(tb[0], tb[2]);
distances[2] = distance(tb[0], tb[3]);
System.out.println(distances[0]);
int CoteEgal1 = -1;
int CoteEgal2 = -1;
int CotePasEgal = -1;
if (distances[0] == distances[1]) {
if (distances[0] != distances[2]) {
CoteEgal1 = 0;
CoteEgal2 = 1;
CotePasEgal = 2;
}
} else if (distances[1] == distances[2]) {
if (distances[1] != distances[0]) {
CoteEgal1 = 1;
CoteEgal2 = 2;
CotePasEgal = 0;
}
} else if (distances[0] == distances[2]) {
if (distances[0] != distances[1]) {
CoteEgal1 = 0;
CoteEgal2 = 2;
CotePasEgal = 1;
}
}
if (CoteEgal1 != -1) {
int coinOpposÈ = 0;
switch (CotePasEgal) {
case 0:
coinOpposÈ = distance(tb[2], tb[3]);
break;
case 1:
coinOpposÈ = distance(tb[1], tb[3]);
break;
case 2:
coinOpposÈ = distance(tb[1], tb[2]);
break;
default:
break;
}
if (coinOpposÈ == distances[CotePasEgal]) {
int diagonal = coinOpposÈ;
int adjacent = distances[CoteEgal1];
boolean stillOK = true;
for (int a = 0; a < 4; a++) {
int diagonalCount = 0;
int adjacentCount = 0;
for (int b = 0; b < 4; b++) {
if (a != b) {
int dist = distance(tb[a], tb[b]);
if (dist == diagonal) {
diagonalCount++;
} else if (dist == adjacent) {
adjacentCount++;
}
}
}
// est ce que on a 1 diagonal et 2 adjacents
if (!(diagonalCount == 1 && adjacentCount == 2)) {
stillOK = false;
break;
}
}
if (stillOK) {
EstCarre = true;
}
}
}
if (EstCarre) {
System.out.println("C'est un carre");
} else {
System.out.println("Ce n'est pas un carre");
}
}
}
To make a loop on a file of 10,000 points and test on each combination of 4 I found too much difficult.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Square {
static int distance(Point point1, Point point2) {
int dist = (int) (Math.pow(point1.getX() - point2.getX(), 2) + Math.pow(point1.getY() - point2.getY(), 2));
dist = (int) Math.sqrt(dist);
return dist;
}
public static void main(String[] args) {
boolean EstCarre = false;
Point[] tb = new Point[10000];
int i = 0;
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("C:/Users/walid/Downloads/exercice.txt"));
String line;
while ((line = br.readLine()) != null) {
String[] parts = line.split(" ");
int part1 = Integer.parseInt(parts[0]);
int part2 = Integer.parseInt(parts[1]);
Point p = new Point(part1, part2);
tb[i] = p;
System.out.println("X " + tb[i].getX() + " y " + tb[i].getY());
i++;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
for (int i2 = 0; i2 < 10000; i2++) {
for (int j = 0; j < 10000; j++) {
for (int k = 0; k < 10000; k++) {
for (int l = 0; l < 10000; l++) {
if (i2 != j && i2 != k && i2 != l && j != k && j != l && k != l) {
int[] distances = new int[10000000];
distances[i2] = distance(tb[i2], tb[j]);
distances[j] = distance(tb[i2], tb[k]);
distances[k] = distance(tb[i2], tb[l]);
int CoteEgal1 = -1;
int CoteEgal2 = -1;
int CotePasEgal = -1;
if (distances[i2] == distances[j]) {
if (distances[i2] != distances[k]) {
CoteEgal1 = i2;
CoteEgal2 = j;
CotePasEgal = k;
}
} else if (distances[i2] == distances[k]) {
if (distances[j] != distances[i2]) {
CoteEgal1 = j;
CoteEgal2 = k;
CotePasEgal = i2;
}
} else if (distances[i2] == distances[k]) {
if (distances[i2] != distances[j]) {
CoteEgal1 = i2;
CoteEgal2 = k;
CotePasEgal = j;
}
}
int coinOpposÈ = 0;
if (CoteEgal1 != -1) {
if (CotePasEgal == i2) {
coinOpposÈ = distance(tb[k], tb[l]);
} else if (CotePasEgal == j) {
coinOpposÈ = distance(tb[j], tb[l]);
} else if (CotePasEgal == k) {
coinOpposÈ = distance(tb[j], tb[k]);
}
if (coinOpposÈ == distances[CotePasEgal]) {
int diagonal = coinOpposÈ;
int adjacent = distances[CoteEgal1];
boolean stillOK = true;
for (int a = 0; a < 10000000; a++) {
int diagonalCount = 0;
int adjacentCount = 0;
for (int b = 0; b < 10000000; b++) {
if (a != b) {
int dist = distance(tb[a], tb[b]);
if (dist == diagonal) {
diagonalCount++;
} else if (dist == adjacent) {
adjacentCount++;
}
}
}
// est ce que on a 1 diagonal et 2 adjacents
if (!(diagonalCount == 1 && adjacentCount == 2)) {
stillOK = false;
break;
}
}
if (stillOK) {
EstCarre = true;
}
}
}
}
if (EstCarre) {
System.out.println("square found");
}
}
}
}
}
}
}
code 2: couple is a class that takes two points
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
public class CarreP {
public static void main(String[] args) {
Point[] tb = new Point[10000];
int i = 0;
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("C:/Users/walid/Downloads/exercice.txt"));
String line;
while ((line = br.readLine()) != null) {
String[] parts = line.split(" ");
int part1 = Integer.parseInt(parts[0]);
int part2 = Integer.parseInt(parts[1]);
Point p = new Point(part1, part2);
tb[i] = p;
i++;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
HashMap<Couple, Integer> hmap = new HashMap<Couple, Integer>();
int j = 0;
while (j < tb.length) {
for (int k = 0; k < tb.length; k++) {
int xcarre = (int) Math.pow(tb[j].getX() - tb[k].getX(), 2);
int ycarre = (int) Math.pow(tb[j].getY() - tb[k].getY(), 2);
int distance = (int) Math.sqrt(xcarre + ycarre);
Couple c = new Couple(tb[k], tb[k]);
hmap.put(c, distance);
Set set = hmap.entrySet();
Iterator iterator = set.iterator();
Iterator iterator2 = set.iterator();
int[] distances = new int[hmap.size()];
int s = 0;
while (iterator.hasNext()) {
Map.Entry mentry = (Map.Entry) iterator.next();
distances[s] = (int) mentry.getValue();
System.out.println(distances[s]);
s++;
}
int CoteEgal1 = -1;
int CoteEgal2 = -1;
int CoteInegal = -1;
for (int i1 = 0; i1 < distances.length; i1++) {
for (int i2 = 0; i2 < distances.length; i2++) {
for (int i3 = 0; i3 < distances.length; i3++) {
if (distances[i1] == distances[i2]) {
if (distances[i1] != distances[i3]) {
CoteEgal1 = i1;
CoteEgal2 = i2;
CoteInegal = i3;
}
} else if (distances[i2] == distances[i3]) {
if (distances[i2] != distances[i1]) {
CoteEgal1 = i2;
CoteEgal2 = i3;
CoteInegal = i1;
}
} else if (distances[i1] == distances[i3]) {
if (distances[i1] != distances[i2]) {
CoteEgal1 = i1;
CoteEgal2 = i3;
CoteInegal = i2;
}
}
}
}
}
}
}
}
}
General idea is the following:
Suppose, we have two points: A and B. There are only two ways to build a square with this points: ABCD and ABEF (see picture below).
Now, lets consider AB is a vector. We can rotate it 90 degrees clockwise and we will get point D. If we move it to point B, then we will get point C. Similarly we can rotate AB 90 degrees counterclockwise and get F and E.
Now we need only check if file contains points C and D, or E and F. If so, we have a square. We can do it quickly if we store all of points in hashmap.
Here is a sample implementation:
import java.util.HashSet;
import java.util.Set;
public class Main {
public static Set<Point> pointsSet = new HashSet<>();
public static void main(String[] args) {
//Suppose, that points are already parsed from file
Point[] points = new Point[]{
new Point(-1, -1),
new Point(2, 1),
new Point(4, -2),
new Point(1, -4)
};
//Fill hashset with points. Important: hashCode() and equals() methods are overrided in Point class
for (Point point : points) {
pointsSet.add(point);
}
Point point1;
Point point2;
Point suggested_point3;
Point suggested_point4;
//Consider every pair of points
for (int i = 0; i < points.length - 1; i++) {
point1 = points[i];
for (int j = i + 1; j < points.length; j++) {
point2 = points[j];
//Calculate vector coordinates for pair of points
Vector vector = new Vector(point2.getX() - point1.getX(), point2.getY() - point1.getY());
//Rotate vector clockwise by 90 degrees and calculate coordinates,
//where two more points should be to form a square with current pair of points
Vector clockwise_rotated_vector = rotateVector(vector, 90);
suggested_point3 = moveVectorToPoint(clockwise_rotated_vector, point1);
suggested_point4 = moveVectorToPoint(clockwise_rotated_vector, point2);
if (pointsSet.contains(suggested_point3) && pointsSet.contains(suggested_point4)) {
squareFound(point1, point2, suggested_point3, suggested_point4);
}
//Same for counterclockwise rotated vector
Vector counterclockwise_rotated_vector = rotateVector(vector, -90);
suggested_point3 = moveVectorToPoint(counterclockwise_rotated_vector, point1);
suggested_point4 = moveVectorToPoint(counterclockwise_rotated_vector, point2);
if (pointsSet.contains(suggested_point3) && pointsSet.contains(suggested_point4)) {
squareFound(point1, point2, suggested_point3, suggested_point4);
}
}
}
}
private static void squareFound(Point point1, Point point2, Point point3, Point point4) {
System.out.println(String.format("%s, %s, %s, %s", point1, point2, point3, point4));
}
private static Point moveVectorToPoint(Vector vector, Point point) {
double x = point.getX() + vector.getX();
double y = point.getY() + vector.getY();
return new Point(x, y);
}
private static Vector rotateVector(Vector original_vector, double degrees) {
double radians = Math.toRadians(degrees);
double x = original_vector.getX() * Math.cos(radians) - original_vector.getY() * Math.sin(radians);
double y = original_vector.getX() * Math.sin(radians) + original_vector.getY() * Math.cos(radians);
return new Vector(x, y);
}
}
Class Point:
import java.math.BigDecimal;
import java.util.Locale;
public class Point {
public Point(double x, double y) {
this.x = x;
this.y = y;
//Store decimal values with certain scale, which would later be used for hashCode() calculation
this.decimalX = new BigDecimal(x).setScale(DECIMAL_SCALE, BigDecimal.ROUND_HALF_EVEN);
this.decimalY = new BigDecimal(y).setScale(DECIMAL_SCALE, BigDecimal.ROUND_HALF_EVEN);
}
private static final int DECIMAL_SCALE = 3;
private double x;
public double getX() {
return x;
}
private double y;
public double getY() {
return y;
}
private BigDecimal decimalX;
private BigDecimal decimalY;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
if (!decimalX.equals(point.decimalX)) return false;
return decimalY.equals(point.decimalY);
}
#Override
public int hashCode() {
//calculate for decimal values, so two very close enough points will have equal hash codes
int result = decimalX.hashCode();
result = 31 * result + decimalY.hashCode();
return result;
}
#Override
public String toString() {
return String.format(Locale.ENGLISH, "(%2f,%2f)", x, y);
}
}
Class Vector:
public class Vector {
public Vector(double x, double y) {
this.x = x;
this.y = y;
}
private double x;
public double getX() {
return x;
}
private double y;
public double getY() {
return y;
}
}
UPDATE:
As mentioned in comments, there were few moments to improve, so I updated my code.
Finding all possible combinations of squares from 10000 points would be too much work doing it brute-force.
So, you need a way to find points that can be combined to form a square, i.e. points that share X or Y coordinate.
So, load all points into memory, "indexing" them by building a Map<Integer, Set<Integer>>, mapping X coordinates to collection of Y coordinates. The value is a Set so duplicate points are eliminated, and to improve performance of code below.
Now comes the more brute-force part, but it's greatly reduced, so shouldn't be too bad. Search like this (pseudo-code so you have to do part of this too):
for eRight in xMap:
xRight = eKey.key
continue if eRight.value.size() == 1
for eLeft in xMap:
xLeft = eLeft.key
continue if xLeft >= xRight
continue if eLeft.value.size() == 1
// at this point we now have two distinct X coordinates
// with their corresponding collection of Y coordinates
for yTop : eLeft.value:
continue if ! eRight.value.contains(yTop)
for yBottom : eLeft.value:
continue if yBottom >= yTop // assuming (0,0) is lower-left
continue if ! eRight.value.contains(yBottom)
// at this point we now have two distinct Y coordinates
// that fit both X coordinates, i.e we have a rectangle
new Rectangle(xLeft, yTop, xRight, yBottom)
The above code finds rectangles, not squares. I'll leave it to you to changing it to find squares.
Hint: The yBottom (or yTop) loop is replaced with a calculation.
I have an answer that is similar to Marat Safin's above, except that I think we must be careful about the fact that floating point arithmetic is imprecise and we must be careful that after some steps of calculation, a floating point number like 3.1415926 may become 3.1415927 (just differ by 1E-7). The solution to this is that we must also declare an argument epsilon (tolerance) that allows us to do "approximately equal" of two floating point numbers (such as double).
My answer also has test input which you can use to verify your program.
You can find my answer here in another SO post of Detect square in a List of Points
I am new to JavaFX and I would like to create Lloyds fifteen game(https://en.wikipedia.org/wiki/15_puzzle).
I created controler for the game but I cant see a way to connect it with some drawing pane. I´d like to move elements on click.
edit: I´d need help with choosing right pane to draw elements(should they extend something?), that would be clickable.
Controler:
it creates matrix of elements. it provides basic methods to shuffle the board and to move a single element(those methods check, if zero(=blank field) is next to this element, then the element can move). finally it has method to check, if the puzzle is in final state(all elements are in their final positions)
import java.util.Random;
public class LloydsFifteen {
Element[][] actualState;
int[] zeroPosition;
int countInARow;
int numberOfDrags;
//for Lloyds fifteen, countInARow will be 4
public LloydsFifteen(int countInARow)
{
numberOfDrags = 0;
this.countInARow = countInARow;
actualState = new Element[countInARow][countInARow];
zeroPosition = new int[2];
zeroPosition[0] = countInARow-1;
zeroPosition[1] = countInARow-1;
for(int i = 0; i < countInARow; i++)
{
for(int j = 0; j < countInARow; j++)
{
actualState[i][j] = new Element(i*countInARow + j);
}
}
shuffleLloyd(100000);
for(int i = 0; i < countInARow; i++)
{
for(int j = 0; j < countInARow; j++)
{
System.out.println("prvek na pozici [" + i + ", " + j + "] ma id: " +
actualState[i][j].finalPosition + " a aktualni pozici: " + actualState[i][j].actualPosition );
}
}
System.out.println("a pozice nuly je [" + zeroPosition[0] + ", " + zeroPosition[1] + "]" );
System.out.println("a pocet provedenych tahu je " + numberOfDrags);
}
//shuffles the board
public void shuffleLloyd(int pocetTahu)
{
Random r = new Random();
for(int i = 0; i < pocetTahu; i++)
{
switch(r.nextInt(4))
{
case 0: if(moveRight()) numberOfDrags++; break;
case 1: if(moveLeft()) numberOfDrags++; break;
case 2: if(moveUp()) numberOfDrags++; break;
case 3: if(moveDown()) numberOfDrags++; break;
default: break;
}
//another way to shuffle the board
//moveAnElement(r.nextInt(4), r.nextInt(4));
}
}
//checks, if element can be moved
public void moveAnElement(int x, int y)
{
boolean done = false;
if(zeroPosition[0] == x + 1 && zeroPosition[1] == y)
{
done = moveRight();
}
if(zeroPosition[0] == x - 1 && zeroPosition[1] == y)
{
done = moveLeft();
}
if(zeroPosition[0] == x && zeroPosition[1] == y + 1)
{
done = moveDown();
}
if(zeroPosition[0] == x && zeroPosition[1] == y - 1)
{
done = moveUp();
}
if(done)
{
//System.out.println("Pohyb se vydaril");
numberOfDrags++;
}
}
public boolean moveRight()
{
Element tmp;
boolean done = false;
if(zeroPosition[0] > 0)
{
tmp = actualState[zeroPosition[0]][zeroPosition[1]];
actualState[zeroPosition[0]][zeroPosition[1]] = actualState[zeroPosition[0] - 1][zeroPosition[1]];
actualState[zeroPosition[0] - 1][zeroPosition[1]] = tmp;
zeroPosition[0] = zeroPosition[0] - 1;
done = true;
}
if(checkEndOfGame())
{
System.out.println("Gratuluji k nalezeni reseni");
}
return done;
}
public boolean moveLeft()
{
Element tmp;
boolean done = false;
if(zeroPosition[0] < countInARow - 1)
{
tmp = actualState[zeroPosition[0]][zeroPosition[1]];
actualState[zeroPosition[0]][zeroPosition[1]] = actualState[zeroPosition[0] + 1][zeroPosition[1]];
actualState[zeroPosition[0] + 1][zeroPosition[1]] = tmp;
zeroPosition[0] = zeroPosition[0] + 1;
done = true;
}
if(checkEndOfGame())
{
System.out.println("Gratuluji k nalezeni reseni");
}
return done;
}
public boolean moveDown()
{
Element tmp;
boolean done = false;
if(zeroPosition[1] > 0)
{
tmp = actualState[zeroPosition[0]][zeroPosition[1]];
actualState[zeroPosition[0]][zeroPosition[1]] = actualState[zeroPosition[0]][zeroPosition[1] - 1];
actualState[zeroPosition[0]][zeroPosition[1] - 1] = tmp;
zeroPosition[1] = zeroPosition[1] - 1;
done = true;
}
if(checkEndOfGame())
{
System.out.println("Gratuluji k nalezeni reseni");
}
return done;
}
public boolean moveUp()
{
Element tmp;
boolean done = false;
if(zeroPosition[1] < countInARow - 1)
{
tmp = actualState[zeroPosition[0]][zeroPosition[1]];
actualState[zeroPosition[0]][zeroPosition[1]] = actualState[zeroPosition[0]][zeroPosition[1] + 1];
actualState[zeroPosition[0]][zeroPosition[1] + 1] = tmp;
zeroPosition[1] = zeroPosition[1] + 1;
done = true;
}
if(checkEndOfGame())
{
System.out.println("Gratulations");
}
return done;
}
//checks if all elements are in final positions
private Boolean checkEndOfGame()
{
for(int i = 0; i < countInARow; i++)
{
for(int j = 0; j < countInARow; j++)
{
if(actualState[i][j].finalPosition != (i*countInARow + j))
{
return false;
}
}
}
return true;
}
}
and an Element
public class Element {
int finalPosition;
int actualPosition;
//position is j*numberInARow + i
public Element(int finalPosition)
{
this.finalPosition = finalPosition;
this.actualPosition = finalPosition;
}
}
Thanks for any hint, I have no idea about next step.
I want it to look like this: Lloyds fifteen puzzle http://www.cse.wustl.edu/~kjg/cs123/Labs/raster/15.gif
Elements will be part of image(user chooses an image, i will split it and create those elements)
The next step is to setup the UI, the Application which basically is the thread you will start first. For layout I suggest you use a GridPane of 4x4 and initialize it with the Element matrix.
Take a look at this example of a GridPane for a simple form and of course the JavaDoc.
The next steps then would be to add event handlers so that the elements can be reassigned to a different position (that will call the various move methods).
randomEmpty() returns a random coordinate on the n x n grid that is empty (Method works). randomAdjacent() uses randomEmpty() to select an EMPTY coordinate on the map. Comparisons are then made to see if this coordinate has an VALID adjacent coordinate that is NON-EMPTY. The PROBLEM is that randomAdjacent does not always return the coordinates of space with an adjacent NON-EMPTY space. It will always return valid coordinates but not the latter. I can't spot the problem. Can someone help me identify the problem?
public int[] randomEmpty()
{
Random r = new Random();
int[] random = new int[2];
int row = r.nextInt(array.length);
int column = r.nextInt(array.length);
while(!(isEmpty(row,column)))
{
row = r.nextInt(array.length);
column = r.nextInt(array.length);
}
random[0] = row+1;
random[1] = column+1;
return random;
}
public int[] randomAdjacent()
{
int[] adjacentToX = new int[8];
int[] adjacentToY = new int[8];
int[] adjacentFrom = randomEmpty();
int count;
boolean isTrue = false;
boolean oneAdjacentNotEmpty = false;
while(!(oneAdjacentNotEmpty))
{
count = 0;
if(validIndex(adjacentFrom,1,-1))
{
adjacentToX[count] = adjacentFrom[0]+1;
adjacentToY[count] = adjacentFrom[1]-1;
count++;
}
if(validIndex(adjacentFrom,0,-1))
{
adjacentToX[count] = adjacentFrom[0];
adjacentToY[count] = adjacentFrom[1]-1;
count++;
}
if(validIndex(adjacentFrom,-1,-1))
{
adjacentToX[count] = adjacentFrom[0]-1;
adjacentToY[count] = adjacentFrom[1]-1;
count++;
}
if(validIndex(adjacentFrom,-1,0))
{
adjacentToX[count] = adjacentFrom[0]-1;
adjacentToY[count] = adjacentFrom[1];
count++;
}
if(validIndex(adjacentFrom,-1,1))
{
adjacentToX[count] = adjacentFrom[0]-1;
adjacentToY[count] = adjacentFrom[1]+1;
count++;
}
if(validIndex(adjacentFrom,0,1))
{
adjacentToX[count] = adjacentFrom[0];
adjacentToY[count] = adjacentFrom[1]+1;
count++;
}
if(validIndex(adjacentFrom,1,1))
{
adjacentToX[count] = adjacentFrom[0]+1;
adjacentToY[count] = adjacentFrom[1]+1;
count++;
}
if(validIndex(adjacentFrom,1,0))
{
adjacentToX[count] = adjacentFrom[0]+1;
adjacentToY[count] = adjacentFrom[1];
count++;
}
for(int i = 0; i < count; i++)
{
if(!(isEmpty(adjacentToX[i],adjacentToY[i])))
{
oneAdjacentNotEmpty = true;
isTrue = true;
}
}
if(isTrue)
break;
else
adjacentFrom = randomEmpty();
}
return adjacentFrom;
}
public boolean validIndex(int[] a,int i, int j)
{
try
{
Pebble aPebble = array[a[0]+i][a[1]+j];
return true;
}
catch(ArrayIndexOutOfBoundsException e)
{
return false;
}
}
public void setCell(int xPos, int yPos, Pebble aPebble)
{
array[xPos-1][yPos-1] = aPebble;
}
public Pebble getCell(int xPos, int yPos)
{
return array[xPos-1][yPos-1];
}
JUNIT Test Performed:
#Test
public void testRandomAdjacent() {
final int size = 5;
final Board board2 = new Board(size);
board2.setCell(1, 1, Pebble.O);
board2.setCell(5, 5, Pebble.O);
int[] idx = board2.randomAdjacent();
int x = idx[0];
int y = idx[1];
boolean empty = true;
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y + 1; j++) {
if ((i == x && j == y) || i < 1 || j < 1 || i > size || j > size) {
continue;
}
if (board2.getCell(i, j) != Pebble.EMPTY)
empty = false;
}
}
assertFalse(empty);// NEVER gets SET TO FALSE
assertEquals(Pebble.EMPTY, board2.getCell(x, y));
}
As for the answer: I got carried away optimizing your code for readability. I'd think it's most likely
if (board2.getCell(i, j) != Pebble.EMPTY)
empty = false;
causing the problem as getCell operates in 1-based coordinates, but i, j are in 0-based.
You should think about your logic overall. The way I see it, your code might never terminate as randomEmpty() could keep returning the same field over and over again for an undetermined period of time.
I took the liberty to recode your if-if-if cascade into utility method easier to read:
public boolean hasNonEmptyNeighbor(int[] adjacentFrom) {
for(int i = -1; i <= 1; ++i) {
for(int j = -1; j <= 1; ++j) {
if(validIndex(adjacentFrom, i, j) //Still inside the board
&& // AND
!isEmpty(adjacentFrom[0]+i //not empty
,adjacentFrom[1]+j)) {
return true;
}
}
}
return false;
}
Given my previous comment about random() being not the best of choices if you need to cover the full board, your main check (give me an empty cell with a non-empty neighbor) could be rewritten like this:
public void find() {
List<Point> foundPoints = new ArrayList<Point>();
for(int i = 0; i < Board.height; ++i) { //Assumes you have stored your height
for(int j = 0; j < Board.width; ++j) { //and your width
if(isEmpty(i, j) && hasNonEmptyNeighbor(new int[]{i,j})) {
//Found one.
foundPoints.add(new Point(i, j));
}
}
}
//If you need to return a RANDOM empty field with non-empty neighbor
//you could randomize over length of foundPoints here and select from that list.
}
I am trying to write a program which can solve the 8-Puzzle problem.I am using the A* algorithm to find the solution.
I have reviewed my code many times and also tried making some changes.
Even my friends tried to help me find the bug,but they couldn't. I still don't understand where i went wrong.I used javadocs to see if I did something wrong,even that din't solve my problem. I have created three classes to solve this problem.
import java.util.*;
public class Solver implements Iterable<State>
{
ArrayList<State> queue,solQueue;
public int sol[][] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 0 } };
int temp[][],i;
int moves;
int leastPriority,removeIndex;
State removeTemp;
public Solver(State initial)
{
queue = new ArrayList<State>();
solQueue = new ArrayList<State>();
queue.ensureCapacity(16);
solQueue.ensureCapacity(16);
temp = new int[3][3];
i=1;
leastPriority = 100;
removeTemp=initial;
queue.add(removeTemp);
Iterator<State> qu = queue.iterator();
while(removeTemp.m!=sol)
{
leastPriority = 100;
i=0;
queue.iterator();
for (State s : queue)
{
if((s.mh + s.count) <leastPriority)
{
leastPriority = (s.mh + s.count);
removeIndex = i;
}
if(qu.hasNext())
i++;
}
for(State s : removeTemp.neighbours() )
{
queue.add(s);
}
removeTemp=queue.remove(removeIndex);
solQueue.add(removeTemp);
}
this.moves();
this.solution();
}
public int moves()
{
System.out.print("Solution found out in "+ moves+" moves");
moves = removeTemp.count;
return moves;
}
public Iterable<State> solution()
{
for(State s : solQueue)
{
System.out.println(s.m);
System.out.println("");
}
return solQueue;
}
#SuppressWarnings({ "unchecked", "rawtypes" })
#Override
public Iterator iterator() {
return null;
}
}
And the JVM is throwing an exception.
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0,Size: 0
at java.util.ArrayList.rangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at Solver.<init>(Solver.java:41)
at Main.main(Main.java:13)
What i don't understand is that how can the size of the ArrayList be 1 when i have explicitly state it as 16.
The State Class has the heuristic function which is suppose to make the algorithm efficient.The following is the State Class.
import java.util.ArrayList;
import java.util.Iterator;
public class State implements Iterable<State>
{
public int sol[][] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 0 } };
int m[][], bi, bj, count, priority, si, sj;
int i,j,tempm[][];
int mh = 0;
boolean isInitialState, isRepeatedState;
State previousState, tempState;
ArrayList<State> neighbourStates;
public State(State s, int c, int[][] array)
{
neighbourStates = new ArrayList<State>();
neighbourStates.ensureCapacity(16);
tempState =this;
m = new int[3][3];
m=array;
if (s == null)
{
isInitialState = true;
count = 0;
previousState =null;
}
else
{
previousState = s;
count = c+1;
}
this.findZero();
this.manhattanHeuristic();
}
private void findZero()
{
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
if(m[i][j]==0)
{
bi=i;
bj=j;
}
}
}
private void manhattanHeuristic() {
int n = 1;
mh = 0;
for (int i = 0; i < 3; i++)
Z: for (int j = 0; j < 3; j++) {
if ((i == bi) && (j == bj)) {
continue Z;
}
else if (m[i][j] == n) {
n++;
}
else {
this.getSolutionIndex();
mh = mh + Math.abs(i - si) + Math.abs(j - sj);
}
}
}
void getSolutionIndex() {
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) {
if (m[i][j] == 0) {
si = i;
sj = j;
}
}
}
public Iterable<State> neighbours()
{
tempm = m;
this.up();
if(!(equals(tempm)))
{
tempState = new State(this,count,tempm);
neighbourStates.add(tempState);
}
this.down();
if(!(equals(tempm)))
{
tempState = new State(this,count,tempm);
neighbourStates.add(tempState);
}
this.left();
if(!(equals(tempm)))
{
tempState = new State(this,count,tempm);
neighbourStates.add(tempState);
}
this.right();
if(!(equals(tempm)))
{
tempState = new State(this,count,tempm);
neighbourStates.add(tempState);
}
return neighbourStates;
}
public boolean equals(int s[][])
{
if((isInitialState==false)&&(previousState.m == s))
return true;
else
return false;
}
#Override
public Iterator<State> iterator() {
// TODO Auto-generated method stub
return null;
}
public void up()
{
if ((bi > 1) && (bi < 2) && (bj < 3)&& (bj > 1))
{
i = bi;
i = i + 1;
this.move(i,bj);
}
}
public void down()
{
if ((bi > 2) && (bi < 3) && (bj < 3) && (bj > 1))
{
i = bi;
i = i - 1;
this.move(i,bj);
}
}
public void left()
{
if ((bi > 1) && (bi < 3) && (bj < 2)&& (bj > 1)) {
j = bj;
j = j + 1;
this.move(bi, j);
}
}
public void right()
{
if ((bi > 1) && (bi < 3) && (bj < 3) && (bj > 2)) {
j = bj;
j = j - 1;
this.move(bi, j);
}
}
public void move(int x, int y) {
{
tempm = m;
}
if ((tempm[x + 1][y] == 0) || (tempm[x - 1][y] == 0) || (tempm[x][y + 1] == 0)|| (tempm[x][y - 1] == 0)) {
tempm[bi][bj] = tempm[x][y];
tempm[x][y] = 0;
bi = x;
bj = y;
}
}
}
And the finally the class with the main function.
import java.util.Scanner;
public class Main {
public static void main(String[] args)
{
#SuppressWarnings("resource")
Scanner sc = new Scanner(System.in);
int[][] tiles = new int[3][3];
System.out.println("Enter the elements");
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
tiles[i][j] = sc.nextInt();
State initial = new State(null,0,tiles);
Solver solver = new Solver(initial);
solver.solution();
System.out.println("Minimum number of moves = " + solver.moves());
}
}
What i don't understand is that how can the size of the ArrayList be 1 when i have explicitly state it as 16.
You did not set the size of the ArrayList to 16. You've set the capacity:
queue.ensureCapacity(16);
solQueue.ensureCapacity(16);
This does not make the ArrayList have a size of 16.
An ArrayList has an array to hold its data. When you add more elements to the ArrayList and its internal array is full, it will have to allocate a larger array and copy the content of what it currently holds plus the new element.
The capacity of the ArrayList is the minimum size that the internal array has. You can use ensureCapacity to make sure that the ArrayList doesn't have to resize too often (resizing and copying the content is an expensive operation). So, ensureCapacity is a call you make to make it work effiently.
It does not make the ArrayList have 16 elements; it only makes sure that the ArrayList has room for at least 16 elements.
If you want the ArrayList to have 16 elements, you'll have to add those elements one by one.
Size of the collection and the capacity are 2 different concepts.
capacity represents the maximum size of items a collection can hold without a reallocation.
size represents the current number of items in the collection.
IndexOutOfBoundsException is saying that you are trying to access an item with index that does not exist in the collection.
please try the below code in Solver.java
if(!queue.isEmpty())
removeTemp=queue.remove(removeIndex);
else
break;
I have this path finding code which does the first part of the finding by only going one square
public class PathFinding {
static Vector2 start;
static Vector2 end;
static Cell[][] cells;
static Node currentNode;
static Arena arena;
public static void calcPAth(Vector2 from, Vector2 to,
Cell[][] mapCells, Arena a) {
start = from;
end = to;
cells = mapCells;
arena = a;
List<Node> openList = new ArrayList<Node>();
List<Node> closedList = new ArrayList<Node>();
Gdx.app.log(PArena.LOG, "Lists Created");
currentNode = new Node(null, start);
openList.add(currentNode);
Gdx.app.log(PArena.LOG, "Added start to openList");
// check squares around this and add
int startPX = (int) currentNode.parentV.x / 32;
Gdx.app.log(PArena.LOG, "Start X" + startPX);
int startPY = (int) currentNode.parentV.y / 32;
Gdx.app.log(PArena.LOG, "Start Y" + startPY);
Gdx.app.log("", "");
//
int MIN_X = startPX - 1;
int MIN_Y = startPY - 1;
int MAX_X = startPX + 1;
int MAX_Y = startPY + 1;
int startPosX = (startPX - 1 < MIN_X) ? startPX : startPX - 1;
int startPosY = (startPY - 1 < MIN_Y) ? startPY : startPY - 1;
int endPosX = (startPX + 1 > MAX_X) ? startPX : startPX + 1;
int endPosY = (startPY + 1 > MAX_Y) ? startPY : startPY + 1;
// Check boundaries on start cell
for (int rowNum = startPosX; rowNum <= endPosX; rowNum++) {
for (int colNum = startPosY; colNum <= endPosY; colNum++) {
// All the neighbors will be grid[rowNum][colNum]
if (!cells[rowNum][colNum].getTile().getProperties()
.containsKey("blocked")) {
Node node = new Node(currentNode, new Vector2(
rowNum, colNum));
if (rowNum != startPX && colNum != startPY) {
node.setMovementCost(14);
} else
node.setMovementCost(10);
openList.add(node);
System.out.print(node.getFValue() + "|");
} else
System.out.print("B");
}
System.out.println("");
}
openList.remove(currentNode);
closedList.add(currentNode);
int n = openList.get(0).getFValue();
int index = 0;
for (Node temp : openList) {
if (temp.getFValue() < n) {
n = temp.getFValue();
index = openList.lastIndexOf(temp);
Gdx.app.log("n", "n = " + n);
}
}
currentNode = openList.get(index);
arena.colorSquare(currentNode.getVectorPos());
// need to calc move cost;
//
Gdx.app.log("", "");
openList.clear();
closedList.clear();
}
This is my Node class
public static class Node {
int hVal;
int gVal;
int fVal;
Node parentNode;
Vector2 parentV;
private Node(Node node, Vector2 p) {
setParent(node);
this.parentV = p;
calcHValue();
}
public void setMovementCost(int c) {
this.gVal = c;
calcFVal();
}
private void calcFVal() {
fVal = gVal + hVal;
// Gdx.app.log("Node", "HVal = " + hVal);
// Gdx.app.log("Node", "GVal = " + gVal);
// Gdx.app.log("Node", "FVal = " + fVal);
}
private void calcHValue() {
int x = (int) (parentV.x - end.x);
if (x < 0)
x *= -1;
int y = (int) (parentV.y - end.y);
if (y < 0)
y *= -1;
hVal = (int) (x + y) / 32;
// Gdx.app.log(PArena.LOG, "Heuristic Value" + hVal);
}
private void setParent(Node node) {
this.parentNode = node;
}
public int getFValue() {
return fVal;
}
public Vector2 getVectorPos() {
return parentV;
}
}
My problem is that my debugging outputs like this
15|11|15|
11|11|11|
15|11|15|
So basically it isnt actually calculating the total value. It is just adding the movement cost, not heuristic.
What is th problem? Am i missing a step?
You are missing the Successor list i think. An A* does have a Successorlist and while the openlist isnt empty you do the following stuff:
while (openList.size() != 0) {
successor.clear();
q = openList.remove(); //first element of the prio queue
// generate your neighbornodes of q and add them to the successorlist
//after this you iterate over the successor and check if its your goalnode.
//If so you do return it else you add it to the openlist. (still inside of the while!)
//Dont forget to check if the neighbor is inside of the close list!
//if so you do not need to add it to the successorlist
//Here is how it does look at mine A*. It also contains a check if there is a betterone
// calc
for (Node suc : successor) {
if (suc.x == (int) this.screen.character.mapPos.x
&& suc.y == (int) this.screen.character.mapPos.y)
return suc; //return the goalnode
boolean add = true;
if (betterIn(suc, openList))
add = false;
if (betterIn(suc, closeList))
add = false;
if (add)
openList.add(suc);
}
Last but not least you do delete the q note from the openlist and add it to the close ist.
}
closeList.add(q);
}//end of while
Some more minor improvmements would be that you do add a compareable to the Node..
#Override
public int compareTo(Node o) {
if ((this.g + this.h) < (o.g + o.h))
return -1;
else if ((this.g + this.h) >= (o.g + o.h))
return 1;
else
return 0;
}
also override the equals and the hashCode method for it for example like this:
#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() {
return x + y;
}
After that your openList can be a PriorityQueue<Node> and the first object you are getting from the is always the one with the smallest h.
Dont forget to return our final Node to iterate over the getparent method to get the path.
private boolean betterIn(Node n, Collection<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;
}