Shortest path maze solving algorithm - java

I have this method that solves the first possible path in a 2D maze matrix (works fine)
public boolean findFirstPath() {
boolean found = false;
path = new ArrayList<Coordinate>(); // restarts the path in case the algorithm has been solved previously
Coordinate coor = new Coordinate();
coor.i = startI; coor.j = startJ; coor.direction = 0; // instance of the first Coordinate object
path.add(coor); // the first coordinate is added to the path
while (!found && path.size() > 0) {
path.get(path.size()-1).direction++; // accesses the last element and increments the direction by 1
if (path.get(path.size()-1).direction <= 4) {
Coordinate coor_next = setNextCell(path.get(path.size()-1)); // we assign the last element of the path
if (isValidSpot(coor_next)) {
path.add(coor_next); // add to the path the valid cell
if (coor_next.i == endI && coor_next.j == endJ) { // if it reaches the end
found = true;
}
}
} else { // if it has checked all 4 directions go back
path.remove(path.size()-1); // deletes the last position in backtracking
}
}
return found;
}
I need to make modifications to find the shortest possible path and I can't find an optimal solution, I've tried something like this:
public boolean findShortestPath() {
boolean found = false;
int steps = 0;
path = new ArrayList<Coordinate>();
ArrayList<Coordinate> shortest = new ArrayList<Coordinate>(); // arrayList where the shortest path will be stored
Coordinate coor = new Coordinate();
coor.i = startI; coor.j = startJ; coor.direction = 0;
path.add(coor);
while (!found && path.size() > 0) {
path.get(path.size()-1).direction++;
if (path.get(path.size()-1).direction <= 4) {
Coordinate coor_next = setNextCell(path.get(path.size()-1));
if (isValidSpot(coor_next)) {
steps++;
if (steps < path.size()) {
shortest.add(path.get(path.size()-1));
} else {
path.add(coor_next);
}
if (coor_next.i == endI && coor_next.j == endJ) {
found = true;
}
}
} else {
path.remove(path.size()-1);
}
}
return found;
}
but it does not even enter the if (steps < path.size()) comprobation, any suggestions? Thanks.

Related

Node problems with stack implementation of depth first search

If anyone could help me find out what I did wrong, it would be really appreciated. I have taken a few shots at this and have gotten closer with each version of my code. I started by having a linked list within the SearchPath class itself but now I moved it to the Node class. For some reason the code works and finds the end, but it does not properly remove some stuff from the stack. Feel free to give it a try. When the code finishes elements from the stack are supposed to be removed and it does but some extra coordinates end up in there. Not sure if it is because of the way I handled the processing of the already checked nodes or if it the recursion itself. This could also just be a horrible implementation of the problem that needs to be solved and maybe I need to think of another way to solve it, but I know the answer is right there. Possibly also take a look at how I included the linked list and stack as static variables within the node class. Should they have maybe been added elsewhere or could that have been causing some of the issues. Thanks a lot. Also the way it works is by inputting a grid of the same dimensions. Finish is indicated with F and start is indicated with S. The walls are X and the path is O to find the way throught path.
/* This program will implement a stack to try and traverse
a grid from a text file that contains a path. It will try
and find a path to the end and when it does it succeeds.
If it does not find the end that means that there was
not path that exists to it. Reads a file as input.
*/
import java.util.Stack;
import java.util.Scanner;
import java.io.File;
import java.io.FileReader;
import java.lang.Exception;
import java.util.LinkedList;
// This class will represent information about a part in the board and will be used by the
// algorithim to determine what to do next.
class Node {
private static Stack<Node> pathLocation = new Stack<>(); // Create a stack that will hold nodes for the algorithm
// to traverse the area.
private static LinkedList<int[]> checkedLocations = new LinkedList<int[]>();
private static int totalMoves = 0;
private int rowNumber, columnNumber;
private boolean checked;
public Node() { // Constructor for a default node.
}
// Gets the current node in the stack.
public Node getLocaiton() {
return pathLocation.peek();
}
// Removes a node from the stack.
public void removeLocation() {
pathLocation.pop();
}
// Adds a new node to the stack.
public void newLocation(Node nextPath) {
pathLocation.push(nextPath);
}
public Stack<Node> getStack() {
return pathLocation;
}
public int getRowLocation() {
return rowNumber;
}
public int getColumnLocation() {
return columnNumber;
}
public void setRowLocation(int inputRow) {
rowNumber = inputRow;
}
public void setColumnLocation(int inputColumn) {
columnNumber = inputColumn;
}
public void setChecked(boolean input) {
checked = input;
}
public boolean getChecked() {
return checked;
}
public int getTotalMoves() {
return totalMoves;
}
public void setTotalMoves(int input) {
totalMoves = input;
}
public boolean getChecked(int inputColumn, int inputRow) {
for (int[] i : checkedLocations) {
if (inputColumn == i[0] && inputRow == i[1]) {
return true;
}
}
return false;
}
public boolean addChecked(int inputColumn, int inputRow) {
this.setColumnLocation(inputColumn);
this.setRowLocation(inputRow);
for (int[] i : checkedLocations) {
if (this.getColumnLocation() == i[0] && this.getRowLocation() == i[1]) {
--totalMoves;
this.checked = true;
return this.checked;
}
}
int[] temp = { this.getColumnLocation(), this.getRowLocation() };
checkedLocations.push(temp);
this.checked = false;
++totalMoves;
return this.checked;
}
}
class Board {
private int size; // This will determine the size of the board which is obtained from the
// BoardFile class.
private char[][] boardArea; // The current location on the board. This current program supports double
// array. Could later be upgraded to support more dimensions.
private int boardColumn;
private int boardRow;
Board(BoardFile inputBoard) throws Exception { // This constructor could throw and exception.
Scanner boardRead = inputBoard.processedBoard(); // Obtained the scanner from the boardFile class.
String temp; // Strings that will be processed and saved by the scanner.
while (boardRead.hasNext()) { // Processes infromation from the board as long as there is information to read.
temp = boardRead.nextLine(); // Sets a temp string equal to what was next in the file.
int tempSize; // Remembers the size of the temp string.
size = temp.length(); // Sets the total size of game area.
boardColumn = size;
boardArea = new char[size][size];
int j = 0;
boolean startFound = false, endFound = false; // Makes sure there is a finish and a start.
while (temp.length() == size) {
for (int i = 0; i < temp.length(); ++i) { // This loop will construct the game board and
// and makes sure everything is correct.
if (temp.charAt(i) == 'X' || temp.charAt(i) == 'O' || temp.charAt(i) == 'F' || temp.charAt(i) == 'S') {
boardArea[i][j] = temp.charAt(i);
// The following if statements check to make sure there is only one instance of
// finish and start.
if (temp.charAt(i) == 'F' && !endFound) {
endFound = true;
}
else if (temp.charAt(i) == 'F' && endFound) {
throw new Exception("The finish was already found. Please check and try again.");
}
else if (temp.charAt(i) == 'S' && !startFound) {
startFound = true;
}
else if (temp.charAt(i) == 'S' && startFound) {
throw new Exception("The start was already found. Please check and try again.");
}
}
else {
// throws an exception in case one of the characters was not excpected.
throw new Exception("There was a incorrect character within the board. Please check and try again.");
}
}
if (boardRead.hasNext()) {
tempSize = temp.length(); // Assign the previous line size.
temp = boardRead.nextLine(); // Assign the next line.
if (tempSize != temp.length()) { // Check to make sure both lines are the same size.
throw new Exception("The size of the board is not correct. Please check and try again.");
}
}
else {
break;
}
++j; // Increment the row.
if (j > size - 1) { // Makes sure the area in the map is correct.
throw new Exception("The area in the map is not the same. Please check and try again.");
}
boardRow = j; // Sets the row number.
}
if (j != size - 1) {
throw new Exception("The area in the map is not the same. Please check and try again.");
}
if (!endFound) {
throw new Exception("The end was not found. Please check and try again.");
}
if (!startFound) {
throw new Exception("The start was not found. Please check and try again.");
}
}
}
public char[][] getBoardArea() { // Returns the array.
return boardArea;
}
public char getBoardLocation(int inputColumn, int inputRow) { // Gets a character a certain location
return boardArea[inputColumn][inputRow]; // within the array.
}
public int getBoardColumn() { // Gets the column total.
return boardColumn - 1;
}
public int getBoardRow() { // Gets the row total.
return boardRow;
}
}
// The class used to read a file.
class BoardFile {
private File file;
private FileReader createdBoard;
private Scanner readBoard;
BoardFile(String fileName) throws Exception { // Creates a file reader object.
file = new File(fileName);
if (!file.exists()) { // Makes sure the file is found.
throw new Exception("The file could not be found.");
}
createdBoard = new FileReader(file); // Creates the file reader.
readBoard = new Scanner(createdBoard); // Creates a new scanner object to read the board.
}
public Scanner processedBoard() { // Gets the scanner object from the read board.
return this.readBoard;
}
}
// This class is the actual searching and will be used to traverse the map.
public class PathSearch {
private Node node = new Node(); // Creates the node object to traverse the map area.
private Board board; // Creates the map.
private Integer[] saveLocation = new Integer[2]; // Create an array that saves a certain location.
PathSearch() throws Exception { // Accepts nothing but asks for a string that searchs for a file.
Scanner input = new Scanner(System.in);
String userFile;
System.out.println("Please enter the name of the file.");
userFile = input.nextLine();
input.close();
board = new Board(new BoardFile(userFile)); // This creates the board from a file that was input.
for (int i = 0; i < board.getBoardColumn(); ++i) { // Searches the board for the starting point.
for (int j = 0; j < board.getBoardRow(); ++j) {
if (board.getBoardLocation(i, j) == 'S') {
node.newLocation(new Node()); // Creates a new element in the stack.
saveLocation[0] = i; // Saves the column in which the node was found.
saveLocation[1] = j; // Saves the row in which the node was found.
System.out.println("Start found at " + (saveLocation[0]) + " " + (saveLocation[1]));
break; // Exits the loop since the information that was needed was processed.
}
}
}
if (saveLocation == null) {
throw new Exception("No starting point was found. Please check and try again.");
}
checkPath(saveLocation[0], saveLocation[1]);
}
// This method accepts two seperate inputs because it needs to remember if it
// checked the position it is currently at.
public Stack<Node> checkPath(int inputColumn, int inputRow) throws Exception {
node.addChecked(inputColumn, inputRow); // Get the current node and check if has been used already.
if (node.getChecked() && !node.getStack().empty()) { // Sees if it was checked and if the stack is not empty.
node.removeLocation(); // Removes the current node from the stack.
checkPath(node.getLocaiton()); // Uses the previous node to keep checking,
}
else {
Node tempNode = new Node(); // Creates a new temp node.
tempNode.setColumnLocation(inputColumn);
tempNode.setRowLocation(inputRow);
node.newLocation(tempNode); // Adds the temp node into the stack.
checkPath(node.getLocaiton()); // Starts checking at the new location.
}
return null; // Path was not found.
}
// This is a overloaded version of checkPath that accepts the current node
// and begins looking at the current node.
public Stack<Node> checkPath(Node node) throws Exception {
int inputColumn = node.getLocaiton().getColumnLocation();
int inputRow = node.getLocaiton().getRowLocation();
if (board.getBoardLocation(inputColumn, inputRow) == 'F') {
System.out.println("The end was found at " + node.getLocaiton().getColumnLocation()
+ " " + node.getLocaiton().getRowLocation());
System.out.println("Here is the path.");
while (!node.getStack().empty()) { // While the stack has information.
System.out.println(node.getLocaiton().getColumnLocation() + " " + node.getLocaiton().getRowLocation());
node.removeLocation(); // Remove the node to process the next.
}
System.out.println("The total moves were " + node.getTotalMoves());
return node.getStack();
}
// All statements to determine where to move next within the board.
if (inputColumn < board.getBoardColumn() && (board.getBoardLocation(inputColumn + 1, inputRow) == 'O'
|| board.getBoardLocation(inputColumn + 1, inputRow) == 'F')
&& !node.getChecked(inputColumn + 1, inputRow)) {
checkPath(inputColumn + 1, inputRow);
}
if (inputColumn > 0 && (board.getBoardLocation(inputColumn - 1, inputRow) == 'O'
|| board.getBoardLocation(inputColumn - 1, inputRow) == 'F')
&& !node.getChecked(inputColumn - 1, inputRow)) {
checkPath(inputColumn - 1, inputRow);
}
if (inputRow < board.getBoardRow() && (board.getBoardLocation(inputColumn, inputRow + 1) == 'O'
|| board.getBoardLocation(inputColumn, inputRow + 1) == 'F')
&& !node.getChecked(inputColumn, inputRow + 1)) {
checkPath(inputColumn, inputRow + 1);
}
if (inputRow > 0 && (board.getBoardLocation(inputColumn, inputRow - 1) == 'O'
|| board.getBoardLocation(inputColumn, inputRow - 1) == 'F')
&& !node.getChecked(inputColumn, inputRow - 1)) {
checkPath(inputColumn, inputRow - 1);
}
return null; // No path was found at that location.
}
}

Recursively finding a path on a 2d array [Java]

I want to write a programm in java that finds a path on a 2d Array with recursion.
The 2d Array named "gitter" consists of Objects of the type 'field'.
Every field will be initialized with a random number in the range of 100-999. If a field was initialized with a number, in which one of the digits is a prime, it is a "trap". The fields can be imagined as squares, so i can move only in 4 directions:
public class Field {
int number;
boolean visited;
Field() {
Random rn = new Random();
this.number = rn.nextInt((999+1) - 100) + 100;
this.visited = false;
}
boolean isTrap() {
String str = String.valueOf(number);
if(str.contains("2") | str.contains("3") | str.contains("5") | str.contains("7")) {
return true;
} return false;
}
}
The start-field and the end-field of the path shall have a manhattan-distance of more than 2. The problem so far is the recursion. The more i think about it, the if cases in it are just getting longer and longer. I added also a boolean variable "visited", for not visiting the same field twice, but no success. Is the while-loop necessary for the recursion? If not (what i guess), what is the easiest way to make the recursion stop, after i found a path? i tried it with and without a while-loop, but couldn't find any solution.
public class Gitter {
Field[][] gitter = new Field[10][10];
List<Field> path = new ArrayList<Field>();
public Field[] getStartAndGoal() {
boolean notFound = true;
Field[] startGoal = new Field[2];
while(notFound) {
Random x0 = new Random();
Random y0 = new Random();
Random x1 = new Random();
Random y1 = new Random();
int row0 = x0.nextInt((9)+1);
int line0 = y0.nextInt((9)+1);
int row1 = x1.nextInt((9)+1);
int line1 = y1.nextInt((9)+1);
int distance = Math.abs(row1-row0) + Math.abs(line1-line0);
if(distance>2){
if(gitter[row0][line0].isTrap() == false && gitter[row1][line1].isTrap() ==false) {
notFound = false;
Field start = gitter[row0][line0];
Field goal = gitter[row1][line1];
startGoal[0] = start;
startGoal[1] = goal;
}
}
}
return startGoal;
}
public boolean findPath(Field start, Field goal) {
boolean solved = false;
for(int i = 0; i < 10; i++) {
for(int j = 0; j < 10; j++) {
if(gitter[i][j].equals(start)) {
gitter[i][j].visited=true;
while(solved==false){
if((i+1)<10 && gitter[i+1][j].isTrap()==false && gitter[i+1][j].visited == false && findPath(gitter[i+1][j], goal)){
gitter[i+1][j].visited = true;
path.add(gitter[i+1][j]);
return true;
}
if((i-1)>0 && gitter[i-1][j].isTrap()==false && gitter[i-1][j].visited == false && findPath(gitter[i-1][j], goal)){
gitter[i-1][j].visited = true;
path.add(gitter[i-1][j]);
return true;
}
if((j+1)<10 && gitter[i][j+1].isTrap()==false && gitter[i][j+1].visited == false && findPath(gitter[i][j+1], goal)){
gitter[i][j+1].visited = true;
path.add(gitter[i][j+1]);
return true;
}
if((j-1)>10 && gitter[i][j-1].isTrap()==false && gitter[i][j-1].visited == false && findPath(gitter[i][j-1], goal)){
gitter[i][j-1].visited = true;
path.add(gitter[i][j-1]);
return true;
}
for(i=0; i<path.size(); i++) {
if(path.get(i).equals(goal)){
solved = true;
break;
}
}
}
}
}
} return false;
}
Does anybody got a hint for me?
Looking at the problem, there are many variables that aren't necessarily too important to consider yet would allow me to create a simple solution quicker. If you use concepts from my solution, just take notes that there are a variety of restraints completely ignored which can easily be implemented.
Recursion
Typically recursion is used to replace the iterative behaviour of loops. With this problem you want to search every possible path if there exists a path that can take you from point A to point B, or up until you have determined there exists a path.
The more i think about it, the if cases in it are just getting longer and longer
Easy. Create a helper method that does the calculation for you so that you avoid repetitive use of the same code.
Is the while-loop necessary for the recursion?
I'm not entirely sure what you're doing with your loops, but it would definitely be possible (and much more elegant) to do away with loops altogether for this solution.
what is the easiest way to make the recursion stop, after i found a path?
Once you have found a solution, return true. The previous recursive step will receive a true value and then understand that somewhere along the line you have reached the end so it too should return true. This is where the recursive aspect can produce such an elegant solution.
Solution
public static void main (String args[]) {
int n = 10;
int[][] map = new int[n][n]; // Generate random data however you want
boolean[][] visited = new boolean[n][n]; // Defaults to false
int[2] start = new int[]{0,0}; // Top left corner
int[2] end = new int[]{n-1, n-1}; // Bottom right corner
boolean path = pathExists(map, visited, start, end);
}
public static boolean pathExists(int[][] map, boolean[][] visited, int[] current, int[] end) {
int x = current[0];
int y = current[1];
// If not in bounds, not a valid route
if (!inBounds(map, visited, current)) return false;
// If this is the end, return true!
if (current[0] == end[0] && current[1] == end[1]) return true;
// Attempt each cardinal direction. If you find a return, return true
if (pathExists(map, markPresent(visited, current), new int[]{x-1,y}, end)) return true;
if (pathExists(map, markPresent(visited, current), new int[]{x+1,y}, end)) return true;
if (pathExists(map, markPresent(visited, current), new int[]{x,y+1}, end)) return true;
if (pathExists(map, markPresent(visited, current), new int[]{x,y-1}, end)) return true;
// There is no solution down this path
return false;
}
public static boolean[][] markPresent(boolean[][] visited, int[] current) {
// Make a deep copy - Is needed to prevent interferance
boolean[][] copy = new boolean[visited.length][visited[0].length];
for (int i = 0; i < copy.length; i++)
copy[i] = Arrays.copyOf(visited[i], visited[i].length);
// Mark where you currently are
copy[current[0]][current[1]] = true;
return copy;
}
public static boolean inBounds(int[][] map, boolean[][] visited, int[] position) {
int x = position[0];
int y = position[1];
// Make sure it is within the bounds of the map
if (x < 0 || y < 0 || x >= map.length || y >= map.length) return false;
// Check if the current block is a barrier
if (isBarrier(map, position)) return false;
// If you have visited this path before, don't do it again
if (visited[x][y]) return false;
// Otherwise, check the path!
return true;
}
public static boolean isBarrier(int[][] map, int[] position) {
// Return your definition of a barrier (I used modulo 10 for testing
}
I've done minimal testing, so if you can see any glearing issues feel free to leave a comment.
From what I can gather, you are wanting to detect whether a path exists between to points. If you are wanting to detect the shortest distance (using the Manhatten metric), instance of returning boolean values you could return integer value. If you want to find the path itself, you can return an array of points and recursively append all points to another array. Here's a modified version that finds the shortest path:
public static ArrayList<int[]> findDistance(int[][] map, boolean[][] visited, int[] current, int[] end) {
int x = current[0];
int y = current[1];
ArrayList<int[]> ret = new ArrayList<>();
ret.add(current);
if (!inBounds(map, visited, current)) return new ArrayList<>(); // Return empty array
if (current[0] == end[0] && current[1] == end[1]) return ret; // Return current location
ArrayList<ArrayList<int[]>> paths = new ArrayList<>();
paths.add(findDistance(map, markPresent(visited, current), new int[]{x-1,y}, end));
paths.add(findDistance(map, markPresent(visited, current), new int[]{x+1,y}, end));
paths.add(findDistance(map, markPresent(visited, current), new int[]{x,y+1}, end));
paths.add(findDistance(map, markPresent(visited, current), new int[]{x,y-1}, end));
// Find the shortest path that leads somewhere
paths.removeIf((ArrayList<int[]> data) -> data.size() == 0);
paths.sort(Comparator.comparing(ArrayList::size));
// If the size of the paths is 0, no path was found
if (paths.size() == 0) {
return new ArrayList<>();
} else {
//Append the found path to the current location and return the list
ret.addAll(paths.get(0));
return ret;
}
}
EDIT
I had a terrible feeling I had forgotten something obvious - I had. You will need to clone the visited array so that you don't interfere with each recursive step. I've added a method to do that.
Adding some extra things to make things easier:
start and goal are attributes of Gitter class
findPath has been divided in tho methods, one public and another private
Code
public class Gitter {
Field[][] gitter = new Field[10][10];
List<Field> path = new ArrayList<Field>();
private Field start = null;
private Field goal = null;
// i've omited initializing too
public boolean findPath(Field start, Field goal) {
this.start = start;
this.goal = goal;
// Instead of having one recursive method, I divided it in two, one public and another private
for(int i = 0; i < 10; i++) {
for(int j = 0; j < 10; j++) {
if(gitter[i][j].equals(start)) {
return findPath(i,j);
}
}
} return false;
}
/*
* Check if available path exist from Field [i][j]
*/
private boolean findPath(int i, int j){
boolean solved = false;
// This check makes if-else chain cleaner
if(i < 0 || i >= 10 || j < 0 || j >= 10){
return false;
}
// Don't check already visited cells
if(!gitter[i][j].visited){
gitter[i][j].visited=true;
// If its possible to have a trap in goal Field, this check must be first
if(gitter[i][j].equals(goal)){
path.add(gitter[i][j]);
return true;
}
// If start Field mustn't be a trap, first condition should be removed
if(!gitter[i][j].equals(start) && gitter[i][j].isTrap()){
return false;
}
// Down
if(findPath(i+1,j)){
solved = true;
}
// Up
else if(findPath(i-1,j)){
solved = true;
}
// Right
else if(findPath(i,j+1)){
solved = true;
}
// Left
else if(findPath(i,j-1)){
solved = true;
}
// If any direction check found a path, this cell is part of that path
if(solved){
path.add(gitter[i][j]);
}
return solved;
}
return false;
}
}
I've tried with small numbers (from 1 to 9) and it should work
This one can not be a perfect nor efficient solution, but I've tried to make code easier to read and understand. I think with this kind of recursive problems that's a point to be highly considered.

What's causing my stackoverflowerror in my maze solver?

I have to solve a maze using recursion and everything was going fine until I ran the program and ran into a stackoverflowerror. I've read a couple other questions on the site and they all say it's because of infinite recursion however none of their issues seem to be the exact same as mine.
my netID_maze.java file
import java.util.Random;
public class netID_Maze
{
int exitRow,
entranceRow;
char[][] map = null;
// Method Name : Maze (Constructor)
// Parameters : None
// Partners : None
// Description : No Parameter Constructor for the maze
public netID_Maze()
{
// omitted code generating a random maze
setEntranceRow(rnger.nextInt(row - 2) + 1);
setExitRow(rnger.nextInt(row - 2) + 1);
map[getEntranceRow()][0] = '.';
map[getExitRow()][column - 1] = '.';
} // end netID_Maze (without parameters)
// Method Name : Maze (Constructor)
// Parameters : exitTemp (int), entranceTemp(int), mapTemp (char[][])
// Partners : None
// Description : Parameter Constructor for the maze
public netID_Maze(char[][] mapTemp, int exitTemp, int entranceTemp)
{
map = mapTemp;
exitRow = exitTemp;
entranceRow = entranceTemp;
} // end netID_Maze (with parameters)
// Method Name : getCell
// Parameters : row (int), column (int), character in cell (character)
// Partners : None
// Returns : The character in the cell that's being called (character)
// Description : Returns the character of the cell that's being called
public char getCell(int r, int c)
{
return map[r][c];
} // end getCell()
// Method Name : setCell
// Parameters : row (int), column (int), character in cell (character)
// Partners : None
// Returns : None
// Description : Changes the character of the cell that's being called
public void setCell(int r, int c, char val)
{
this.map[r][c] = val;
} // end setCell()
public int getEntranceRow ()
{
return entranceRow;
}
public int getExitRow()
{
return exitRow;
}
public void setEntranceRow(int entranceTemp)
{
entranceRow = entranceTemp;
}
public void setExitRow(int exitTemp)
{
exitRow = exitTemp;
}
public int getRows()
{
return map.length;
}
public int getColumns()
{
return map[1].length;
}
public boolean isExit(int r, int c)
{
boolean isExit = false;
if (getExitRow() == r && map[1].length - 1 == c)
{
isExit = true;
}
return isExit;
}
public boolean isEntrance(int r, int c)
{
boolean isEntrance = false;
if (getEntranceRow() == r && 0 == c)
{
isEntrance = true;
}
return isEntrance;
}
public boolean isOpen(int r, int c)
{
boolean isOpen = true;
if (r < 0 || c < 0 || r >= getRows() || c >= getColumns())
{
isOpen = false;
}
else if (map[r][c] == '.')
{
isOpen = false;
}
return isOpen;
}
}
and my netID_MazeSolver.java file
public class netID_MazeSolver {
int steps = 0;
netID_Maze maze = new netID_Maze();
public netID_MazeSolver(netID_Maze mazeTemp)
{
setSteps(0);
maze = mazeTemp;
}
public boolean solveMaze(int r, int c)
{
//Finding whether Current Cell is outside the maze
if (r < 0 || c < 0 || r >= maze.getRows() || c >= maze.getColumns())
{
return false;
}
//Finding whether the current cell is the exit
if (maze.isExit(r,c) == true)
{
return true;
}
//Finding whether current cell is NOT open
if (maze.isOpen(r,c) == false)
{
return false;
}
//Setting current cell as part of the solution path
//Finding out whether solve maze(cell below current) == true
if (solveMaze(r - 1,c) == true)
{
return true;
}
//Finding out whether solve maze(cell to the right of current) == true
if (solveMaze(r,c + 1) == true)
{
return true;
}
//Finding out whether solve maze(cell to the left of current) == true
if (solveMaze(r,c - 1) == true)
{
return true;
}
//Finding out whether solve maze(cell above current) == true
if (solveMaze(r + 1,c) == true)
{
return true;
}
//setting current cell to NOT part of the solution path
return false;
}
public void setSteps(int stepsTemp)
{
steps = stepsTemp;
}
public int getSteps()
{
return steps;
}
}
The actual error just keeps repeating:
at netID_MazeSolver.solveMaze(netID_MazeSolver.java:53)
at netID_MazeSolver.solveMaze(netID_MazeSolver.java:71)
The basic mistake you made, was that you don't set any flags for visited cells. Due to this, your algorithm can visit the same cell again and again. If your generated maze contains any cycles, you're pretty likely to end up in an endlessloop causing a stackoverflow. And btw, you don't need, to write if(maze.isOpen(r , c) == true). if(maze.isOpen(r , c)) gives the same result with less code.

This method that determines the winner of a Mancala does not work

Mancala is a fascinating game that I programmed in Java. On the image below we see a Mancala gameboard. For my problem you need to know that a A1-A6,B1-B6 are called pits and the big pits are called kalahs.
(source: pressmantoy.com)
The pits A1-A6 and right kalah belongs to player A and pits B1-B6 and left kalah to player B.
The game ends when all six pits on one side of the gameboard are empty or when there are more than 24 pits in one player's kalah. This is what I tried to program in a boolean method (which returns true if there is a winner so I can use other method to tell who it is):
public boolean isThereAWinner() {
ArrayList <SuperPit> pitsOfOwner = owner.getmyPits();
ArrayList <SuperPit> pitsOfOpponent = owner.getopponent().getmyPits();
for (int i = 0; i < pitsOfOwner.size(); i++) {
if (pitsOfOwner.get(i).isValidPlayOption() == true)
return false;
}
for (int i = 0; i < pitsOfOpponent.size(); i++) {
if (pitsOfOpponent.get(i).isValidPlayOption() == true)
return false;
}
if (owner.getKalah().getSeed() > 24) return true;
return false;
}
Where:
protected int seed;
public int getSeed() {
return seed;
}
public boolean isValidPlayOption() {
if (getSeed() > 0) return true;
else return false;
}
The oppositepit() and nextPit() methods work. The myPits ArrayLists contain the pits that belong to the two respective players.
I thought that this method should work since if one player no longer has seeds in his pit the game should stop. The method isThereAWinner() is run every time a player makes a move.
Unfortunately, the method always returns false. I have no idea why and hope someone here can provide me with some insight.
It's always returning false because of :
for (int i = 0; i < pitsOfOwner.size(); i++) {
if (pitsOfOwner.get(i).isValidPlayOption() == true)
return false;
}
The moment any pit has seeds, it returns false, even if the other board is completely empty.
How about:
int sum1 = 0;
for (int i = 0; i < pitsOfOwner.size(); i++) {
sum1 += pitsOfOwner.get(i).getSeed();
}
if (sum1 == 0) return true; // all pits are empty
If one player has one valid play option, you already return a value. You need to continue checking.
You don't return true if a player doesn't have a move.
What about checking the opponent's kalah?
== true is redundant.
Code:
public boolean isThereAWinner() {
ArrayList <SuperPit> pitsOfOwner = owner.getmyPits();
ArrayList <SuperPit> pitsOfOpponent = owner.getopponent().getmyPits();
boolean hasLost = true;
for (int i = 0; i < pitsOfOwner.size() && hasLost; i++) {
if (pitsOfOwner.get(i).isValidPlayOption())
hasLost = false;
}
if (hasLost) return true;
hasLost = true;
for (int i = 0; i < pitsOfOpponent.size() && hasLost; i++) {
if (pitsOfOpponent.get(i).isValidPlayOption())
hasLost = false;
}
if (hasLost) return true;
if (owner.getKalah().getSeed() > 24) return true;
if (owner.getopponent().getKalah().getSeed() > 24) return true;
return false;
}
The && hasLost is just an optimization to stop the loop once you find a move.
With less redundancy:
private boolean hasLost(Player player)
{
boolean hasLost = true;
for (int i = 0; i < player.getmyPits().size() && hasLost; i++) {
if (player.getmyPits().get(i).isValidPlayOption())
hasLost = false;
}
return (hasLost || player.getopponent().getKalah().getSeed() > 24);
}
public boolean isThereAWinner() {
if (hasLost(owner)) return true;
if (hasLost(owner.getopponent())) return true;
return false;
}

Finding if Path2D self-intersects

I need to find if Path2D intersects itself. For now, I do it by simply extracting an array of lines from path, and finding if any of these intersect. But it has O(n^2) complexity, and so it is very slow. Is there a faster way to do it?
You can do this faster using the sweep-line algorithm: http://en.wikipedia.org/wiki/Sweep_line_algorithm
Pseudocode:
Each line has a start point and an end point. Say that `start_x` <= `end_x` for all the lines.
Create an empty bucket of lines.
Sort all the points by their x coordinates, and then iterate through the sorted list.
If the current point is a start point, test its line against all the lines in the bucket, and then add its line to the
bucket.
if the current point is an end point, remove its line from the bucket.
The worst case is still O(N^2), but the average case is O(NlogN)
Here is my Java implementation of this algorithm:
import java.awt.Point;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
import java.util.*;
/**
* Path2D helper functions.
* <p/>
* #author Gili Tzabari
*/
public class Path2Ds
{
/**
* Indicates if a Path2D intersects itself.
* <p/>
* #return true if a Path2D intersects itself
*/
public static boolean isSelfIntersecting(PathIterator path)
{
SortedSet<Line2D> lines = getLines(path);
if (lines.size() <= 1)
return false;
Set<Line2D> candidates = new HashSet<Line2D>();
for (Line2D line: lines)
{
if (Double.compare(line.getP1().distance(line.getP2()), 0) <= 0)
{
// Lines of length 0 do not cause self-intersection
continue;
}
for (Iterator<Line2D> i = candidates.iterator(); i.hasNext();)
{
Line2D candidate = i.next();
// Logic borrowed from Line2D.intersectsLine()
int lineRelativeToCandidate1 = Line2D.relativeCCW(line.getX1(), line.getY1(), line.
getX2(),
line.getY2(), candidate.getX1(), candidate.getY1());
int lineRelativeToCandidate2 = Line2D.relativeCCW(line.getX1(), line.getY1(), line.
getX2(),
line.getY2(), candidate.getX2(), candidate.getY2());
int candidateRelativeToLine1 = Line2D.relativeCCW(candidate.getX1(),
candidate.getY1(),
candidate.getX2(), candidate.getY2(), line.getX1(), line.getY1());
int candidateRelativeToLine2 = Line2D.relativeCCW(candidate.getX1(),
candidate.getY1(),
candidate.getX2(), candidate.getY2(), line.getX2(), line.getY2());
boolean intersection = (lineRelativeToCandidate1 * lineRelativeToCandidate2 <= 0)
&& (candidateRelativeToLine1 * candidateRelativeToLine2 <= 0);
if (intersection)
{
// Lines may share a point, so long as they extend in different directions
if (lineRelativeToCandidate1 == 0 && lineRelativeToCandidate2 != 0)
{
// candidate.P1 shares a point with line
if (candidateRelativeToLine1 == 0 && candidateRelativeToLine2 != 0)
{
// line.P1 == candidate.P1
continue;
}
if (candidateRelativeToLine1 != 0 && candidateRelativeToLine2 == 0)
{
// line.P2 == candidate.P1
continue;
}
// else candidate.P1 intersects line
}
else if (lineRelativeToCandidate1 != 0 && lineRelativeToCandidate2 == 0)
{
// candidate.P2 shares a point with line
if (candidateRelativeToLine1 == 0 && candidateRelativeToLine2 != 0)
{
// line.P1 == candidate.P2
continue;
}
if (candidateRelativeToLine1 != 0 && candidateRelativeToLine2 == 0)
{
// line.P2 == candidate.P2
continue;
}
// else candidate.P2 intersects line
}
else
{
// line and candidate overlap
}
return true;
}
if (candidate.getX2() < line.getX1())
i.remove();
}
candidates.add(line);
}
return false;
}
/**
* Returns all lines in a path. The lines are constructed such that the starting point is found
* on the left (or same x-coordinate) of the ending point.
* <p/>
* #param path the path
* #return the lines, sorted in ascending order of the x-coordinate of the starting point and
* ending point, respectively
*/
private static SortedSet<Line2D> getLines(PathIterator path)
{
double[] coords = new double[6];
SortedSet<Line2D> result = new TreeSet<Line2D>(new Comparator<Line2D>()
{
#Override
public int compare(Line2D o1, Line2D o2)
{
int result = Double.compare(o1.getX1(), o2.getX1());
if (result == 0)
{
// Ensure we are consistent with equals()
return Double.compare(o1.getX2(), o2.getX2());
}
return result;
}
});
if (path.isDone())
return result;
int type = path.currentSegment(coords);
assert (type == PathIterator.SEG_MOVETO): type;
Point.Double startPoint = new Point.Double(coords[0], coords[1]);
Point.Double openPoint = startPoint;
path.next();
while (!path.isDone())
{
type = path.currentSegment(coords);
assert (type != PathIterator.SEG_CUBICTO && type != PathIterator.SEG_QUADTO): type;
switch (type)
{
case PathIterator.SEG_MOVETO:
{
openPoint = startPoint;
break;
}
case PathIterator.SEG_CLOSE:
{
coords[0] = openPoint.x;
coords[1] = openPoint.y;
break;
}
}
Point.Double endPoint = new Point.Double(coords[0], coords[1]);
if (Double.compare(startPoint.getX(), endPoint.getX()) < 0)
result.add(new Line2D.Double(startPoint, endPoint));
else
result.add(new Line2D.Double(endPoint, startPoint));
path.next();
startPoint = endPoint;
}
return result;
}
}

Categories