How to avoid StackOverflow Error in Recursive MazeSolver - java

Like many other java students in college, I need to develop a maze program that solves the maze. My solveMaze method that implements recursion returned a stackoverflow runtime error. How do I solve this problem please? Does this have to do with my algorithm? Thanks in advance.
A) I created a solution maze that array that's going to hold the path to the exit.
B) Then, I implemented a method solveMaze() that took a step toward the exit everytime it's called.
Note: The isWall() method checks if the position you're moving to is a wall or not.
public void showPath() {
int[][] sol = new int[m.length][m[0].length];
for (int j = 0; j < sol.length; j++) {
for (int i = 0; i < sol[0].length; i++) {
sol[j][i] = m[j][i];
}
}
if (solveMaze(sol, m.length - 1, 0, exitCords) == false)
System.out.println("Solution doesn't exist");
else {
for (int y = 0; y < sol.length; y++) {
for (int x = 0; x < sol[0].length; x++) {
if (sol[y][x] == exitCords[0] && sol[y][x] == exitCords[1]) {
System.out.print("E ");
} else {
if (sol[y][x] == 1) {
System.out.print(" ");
} else if (sol[y][x] == 3) {
System.out.print("~");
} else {
System.out.print("# ");
}
}
}
System.out.println();
}
}
}
public boolean solveMaze(int[][] sol, int y, int x, int[] exitCords) {
//exitCords[] is a one-dimensional array that holds the x and y coordinate of the exit point on a maze.
if (y == exitCords[1] && x == exitCords[0]) {//Base Case
return true;
}
//North
if (!isWall(x, y - 1) && sol[y - 1][x] != 3) {
sol[y][x] = 3;//3 is assigned to positions you already visited.
y--;
sol[y][x] = 3;
//Implement recursion to call the solveMaze again on this line.
solveMaze(sol, y, x, exitCords);
return true;
}
//South
else if (!isWall(x, y + 1) && sol[y + 1][x] != 3) {
sol[y][x] = 3;
y++;
sol[y][x] = 3;
solveMaze(sol, y, x, exitCords);
return true;
}
//East
else if (!isWall(x + 1, y) && sol[y][x + 1] != 3) {
sol[y][x] = 3;
x++;
sol[y][x] = 3;
solveMaze(sol, y, x, exitCords);
return true;
}
//West
else if (!isWall(x - 1, y) && sol[y][x - 1] != 3) {
sol[y][x] = 3;
x--;
sol[y][x] = 3;
solveMaze(sol, y, x, exitCords);
return true;
}
/*The following line of code are to get out of dead ends and replace every position near a dead end with a wall*/
else if ((isWall(x, y - 1) && isWall(x, y + 1) && isWall(x + 1, y)) || (isWall(x, y - 1) && isWall(x, y + 1) && isWall(x - 1, y))
|| (isWall(x - 1, y) && isWall(x, y + 1) && isWall(x + 1, y)) || (isWall(x - 1, y) && isWall(x, y - 1) && isWall(x + 1, y))) {
if (isWall(x, y - 1) && isWall(x, y + 1) && isWall(x + 1, y)) {
sol[y][x] = 0;
solveMaze(sol, y, x - 1, exitCords);
return true;
}
if (isWall(x, y - 1) && isWall(x, y + 1) && isWall(x - 1, y)) {
sol[y][x] = 0;
solveMaze(sol, y, x + 1, exitCords);
return true;
}
if (isWall(x - 1, y) && isWall(x, y + 1) && isWall(x + 1, y)) {
sol[y][x] = 0;
solveMaze(sol, y - 1, x, exitCords);
return true;
}
if (isWall(x - 1, y) && isWall(x, y - 1) && isWall(x + 1, y)) {
sol[y][x] = 0;
solveMaze(sol, y + 1, x, exitCords);
return true;
}
}
return false;
}

You have different ways to solve the problem:
The first one is to rewrite your code without using recursion (no luck for tail recursion - Java doesn't have optimization for it)
Another way is to increase stack size using -Xss option
Or you can add actual depth check to solveMaze method, e.g.:
public void showPath() {
// ...
if (solveMaze(sol, m.length - 1, 0, exitCords , 0) == false) {
// ...
}
public boolean solveMaze(int[][] sol, int y, int x, int[] exitCords, int depth) {
if (depth > 64) {
return false;
}
// ...
solveMaze(sol, y, x, exitCords, depth + 1);
// ...
}

A stack overflow error means that your recursion has gone deeper than the language allows. For a small maze, this shouldn't happen, unless you are revisiting locations in the maze. As your code does not seem to make any effort to avoid that, you might want to fix that.

Related

How do you search around any index in a 2D array?

So I'm making a program that takes in a 2D array of 5x5, and lists all the characters around any given index of the array. For example, if I input list[1][1], it will give the indexes: [0][0], [0][1], [0][2], [1][0], [1][2], [2][0], [2][1] ,[2][2].
I can print out all the letters around the indexes except for the ones on the edges such as index [0][0]. I can't seem to figure out how to get past that.
private static void checkSurrounding(char[][] list, int x, int y) {
for(int dx = -1; dx <= 1; dx++) {
for(int dy = -1; dy <= 1; dy++) {
if(!(dx == 0 && dy == 0)) {
System.out.print(list[x + dx][y + dy]);
}
}
}
}
Your code is almost correct! You exclude the middle point here:
private static void checkSurrounding(char[][] list, int x, int y) {
for(int dx = -1; dx <= 1; dx++) {
for(int dy = -1; dy <= 1; dy++) {
if(!(dx == 0 && dy == 0)) {
System.out.print(list[x + dx][y + dy]);
}
}
}
}
The only thing you miss is to avoid getting out of bounds. Just make sure that you do not get out of bounds and it should work impeccably:
private static void checkSurrounding(char[][] list, int x, int y) {
for(int dx = -1; dx <= 1; dx++) {
if ((x + dx >= 0) && (x + dx < list.length)) {
for(int dy = -1; dy <= 1; dy++) {
if ((y + dy >= 0) && (y + yd < list[x + dx].length) && (!(dx == 0 && dy == 0))) {
System.out.print(list[x + dx][y + dy]);
}
}
}
}
}

Boolean method to determine consecutive numbers

Write a method named consecutive that accepts three integers as parameters and returns true if they are three consecutive numbers; that is, if the numbers can be arranged into an order such that there is some integer k such that the parameters' values are k, k+1, and k+2. Your method should return false if the integers are not consecutive. Note that order is not significant; your method should return the same result for the same three integers passed in any order.
For example, the calls consecutive(1, 2, 3), consecutive(3, 2, 4), and consecutive(-10, -8, -9) would return true. The calls consecutive(3, 5, 7), consecutive(1, 2, 2), and consecutive(7, 7, 9) would return false.
This is what I have so far and keep getting infinite loop error and skipped tests
public boolean consecutive(int x, int y, int z) {
Scanner kb = new Scanner(System.in);
x = kb.nextInt();
y = kb.nextInt();
z = kb.nextInt();
if (((x < y && x < z) && (y < z && ((y - x) == 1) && ((z - x) == 2))) 
||((z < y && ((z - x) == 1) && ((y - x) == 2)))) 
{
return true;
} else if (((y < x && y < z)&& (x < z && ((x - y) == 1) && ((z - y) == 2))) 
|| ((z < x && ((z - y) == 1) && ((x - y) == 2))))
{
return true;
} else if (((z < x && z < y)&& (y < x && ((y - z) == 1) && ((x - z) == 2))) 
||((x < y && ((x - z) == 1) && ((y - z) == 2))))
{
return true;
} else {
return false;
}
What you have there is serious overkill and pretty much unreadable to anyone who hasn't spent a large proportion of their career in C :-)
You should always strive for readability (and hence maintainability) first, reverting to less readable code only when absolutely necessary. Even if you do revert, you should then document why and what you've done so the next poor soul that has to maintain your code won't be cursing your name.
For this specific case, what you are attempting can be achieved in much simpler code such as the following (pseudo-code):
def areConsecutive(a, b, c):
if a > b: swap a, b
if b > c: swap b, c
if a > b: swap a, b
return (b - a == 1) and (c - b == 1)
The three if statements are simply an unrolled bubble sort to ensure a, b and c are in ascending order, then you simply check to ensure the difference between them is one in both cases.
There's no need to put them into a list or array to sort them since sorting three items is relatively easy (the swap can be done with int t = a; a = b; b = t;).
In terms of Java code (once you've moved the input to outside the function where it belongs), you'd end up with something like:
bool areConsecutive(int a, int b, int c) {
int t;
if (a > b) { t = a; a = b; b = t; }
if (b > c) { t = b; b = c; c = t; }
if (a > b) { t = a; a = b; b = t; }
return (b - a = 1) && (c - b == 1);
}
When you are passing value why using scanner?
Remove those lines and its working. You can use another logic to determine consecutive numbers.
public boolean consecutive(int x, int y, int z) {
if (((x < y && x < z) && (y < z && ((y - x) == 1) && ((z - x) == 2))) ||((z < y && ((z - x) == 1) && ((y - x) == 2)))) {
return true;
} else if (((y < x && y < z)&& (x < z && ((x - y) == 1) && ((z - y) == 2))) ||
((z < x && ((z - y) == 1) && ((x - y) == 2)))){
return true;
} else if (((z < x && z < y)&& (y < x && ((y - z) == 1) && ((x - z) == 2))) ||((x < y && ((x - z) == 1) && ((y - z) == 2)))){
return true;
} else
return false;
}
Just Remove these code:
Scanner kb = new Scanner(System.in);
x = kb.nextInt();
y = kb.nextInt();
z = kb.nextInt();
Or use Like this:
public class test{
public static void main(String[] args) {
Scanner kb = new Scanner(System.in);
x = kb.nextInt();
y = kb.nextInt();
z = kb.nextInt();
System.out.println("Result:" + consecutive(x, y, z));
}
public static boolean consecutive(int x, int y, int z) {
if (((x < y && x < z) && (y < z && ((y - x) == 1) && ((z - x) == 2))) || ((z < y && ((z - x) == 1) && ((y - x) == 2)))) {
return true;
} else if (((y < x && y < z) && (x < z && ((x - y) == 1) && ((z - y) == 2)))
|| ((z < x && ((z - y) == 1) && ((x - y) == 2)))) {
return true;
} else if (((z < x && z < y) && (y < x && ((y - z) == 1) && ((x - z) == 2))) || ((x < y && ((x - z) == 1) && ((y - z) == 2)))) {
return true;
} else {
return false;
}
}
}
Create a list with the numbers, sort it and do de diference between the elements:
public static boolean myConsecutive(int x, int y, int z) {
final List<Integer> list = new ArrayList<>();
list.add(x);
list.add(y);
list.add(z);
Collections.sort(list);
return (list.get(2) - list.get(1) == 1 && list.get(1) - list.get(0) == 1);
}
The consecutive method is passed with three values , then why you are reading from the console.
create an array with the size of 3 elements.
Sort the arrays using Arrays.Sort method
Check the difference between the second and the first number is 1 and difference between Third and the second number is 1.
Code :
public boolean consecutive(int x, int y, int z) {
int [] numbers = new int [3];
numbers[0] = x;
numbers[1] = y;
numbers[2] = z;
Arrays.sort(numbers);
boolean isConsecutive = (numbers[1]==numbers[0]+1)&&(numbers[2]==numbers[1]+1);
return isConsecutive;
}

The values in the if statement is not returning, only returning the else statement (can't run it in the main class)

This subclass won't go through the if statements but only return the else statement so it only returns 0. Also, the main class and subclass are separate java classes.
public class LargestEven {
int largestEven(int x, int y, int z) {
if(x % 2 == 0 && x >= y && x >= z) {
return x; // this part won't return
}
if(y % 2 == 0 && y >= x && y >= z) {
return y; //this part also won't return
}
if(z % 2 ==0 && z >= x && z >= y) {
return z; //this too won't return
}
else {
return 0;
}
}
}
Your code requires that a number is both the largest and even. In your example with values (2, 4, 9), you will not get back 4, because 4 is the largest even between these numbers, but not the largest, i.e. this is not true: y>=z.
You would need to change your checks, so that you only check the valid cases:
public class LargestEven {
int largestEven(int x, int y, int z) {
boolean xIsEven = x%2 == 0;
boolean yIsEven = y%2 == 0;
boolean zIsEven = z%2 == 0;
//checks that x is even and greater from y and z if y and z are
//even respectively
if (xIsEven && (!yIsEven || x>=y) && (!zIsEven || x >= z)) {
return x; // this part won't return
}
//we know for sure that x is not the largest even
//so we skip checking it
if (yIsEven && (!zIsEven || y>=z)) {
return y;
}
//we know that neither x or y are the largest evens
//so return either z if z is even, or 0
return zIsEven ? z : 0;
}
}
It does not appear to be an issue with your function logic... see below, the same function in JS.
function largestEven(x, y, z) {
if (x % 2 == 0 && x >= y && x >= z) {
return x; // this part won't return
}
if (y % 2 == 0 && y >= x && y >= z) {
return y;
}
if (z % 2 == 0 && z >= x && z >= y) {
return z;
} else {
return 0;
}
}
var ele = document.getElementById("result");
ele.innerHTML += "<p>Sending 2, 3, 4 (position z): " + largestEven(2,3,4)+"</p>";
ele.innerHTML += "<p>Sending 3, 4, 2 (position y): " + largestEven(3,4,2)+"</p>";
ele.innerHTML += "<p>Sending 4, 3, 2 (position x): " + largestEven(4,3,2)+"</p>";
ele.innerHTML += "<p>Sending 2, 2, 2 (position x): " + largestEven(2,2,2)+"</p>";
ele.innerHTML += "<p>Sending 3, 3, 3 (no largest): " + largestEven(3,3,3)+"</p>";
<div id="result"></div>

StackOverflowError during recursive calls to find closest empty index in 2D array

I am trying to use a recursive approach to figure out the closest empty element in my 2D array if the input index already has an element. For example, I call my method tryPlant(int X, int Y) to put a symbol in my 2D (32x32) array:
ecoArray[X][Y] == "." (empty) then fill it with "~"
If there is an element besides ".", then recursively find the next empty spot in the same row first, then column.
The code for the method is:
public void tryPlant(int X, int Y){
if (this.ecoArray[X][Y] == ".") {
this.ecoArray[X][Y] = "~";
}else{
if((Y - 1) >= 0) {
tryPlant(X, Y - 1);
}
else if((Y + 1) <= 32){
tryPlant(X, Y + 1);
}else if((X - 1) >= 0 ) {
tryPlant(X - 1, Y);
}else if((X + 1) <= 32){
tryPlant(X + 1, Y);
}
}
}
I am calling the method in another class like this:
Plant p1;
private void initPlants(){
int size = p1.initPop;
for (int i = 0; i < size; i++) {
int randX = randGen();
int randY = randGen();
Plant plant = new Plant(randX, randY, this.ecoArray);
}
}
The randGen() method returns a random integer between 0-31.
Sometimes the random generator gives me indexes such that they do not collide with other objects. An example of this (the | are obstacles):
I want to know why I get a stackoverflow error and what I can do to fix it. If you need any other portions of my code, please ask. I am pretty new to Java.
Edit
public static int left = 0;
public static int right = 0;
public void tryPlant(int X, int Y){
if (this.ecoArray[X][Y] == ".") {
this.ecoArray[X][Y] = "~";
}else{
if((Y - 1) >= 0) {
this.left++;
tryPlant(X, Y - 1);
}
else if((Y + this.left + 1) <= 32){
tryPlant(X, Y + this.left + 1);
this.left = 0;
}else if((X - 1) >= 0 ) {
this.right++;
tryPlant(X - 1, Y);
}else if((X + this.right + 1) <= 32){
tryPlant(X + + this.right + 1, Y);
this.right = 0;
}
}
}
Approximately what happens is: Each time a function is called it is stored in a memory stack. When the function return, what was stored is cleared. But, if you call too many functions, you fullfil the memory and a stackoverflow error is sent.
You get a stackoverflow error because somewhere you have a loop in your algorithm. That cause too many call to tryPlant().
First, when you find a way where you already pass, return. (where char == '~')
Second, where there is an obstacle, return. (where char == '|')
Third, you don't try all directions:
if ((Y - 1) >= 0) {
this.left++;
tryPlant(X, Y - 1);
} else if ((Y + this.left + 1) <= 32){
tryPlant(X, Y + this.left + 1);
this.left = 0;
} else if ((X - 1) >= 0 ) {
this.right++;
tryPlant(X - 1, Y);
} else if ((X + this.right + 1) <= 32){
tryPlant(X + + this.right + 1, Y);
this.right = 0;
}
I tried this and it seems to work:
public class Test {
private char[][] ecoArray;
public Test() {
String[] stringArray = new String[] {
"||.|.||..|..||.|.||||||||..||.||",
"||||....||..|||..||....|.||..|||",
"||||||...|.|||...||.|..||..||||.",
"||...||.|.|||.||||.|||||.|...||.",
"|.|....|.|||||||||..||.|.|.||...",
"..|||.||||...|..||.||..|..||||.|",
"..|.||||||..||.||||..|||.|.|...|",
"|||||.||.|||...||...||..||.|||..",
"||||.|..||||||..|.|||...||.||.|.",
"|||.|||||.|||||.||||.|....||||||",
"||...||||||.|.|||||||||||.|.|.||",
"|.|.||||||||.||||....|.||||.||||",
"||..||.||||.|..||.|||..||.|.||||",
"..||..|..||.|.|||..|||..|||||.|.",
"||||.|.||.||||.|||||..|||.|.....",
"..|.|.|||..|||..||.||||.|||.|..|",
"||||.|..|||||||.|||||.||.|.|....",
"..|...||...|||||.|...|..|...|||.",
"..|||||||..||...||||||..|..|||||",
"||||..||.|.|||||.||||.|||||.||..",
"|||||.||||.|....||||....||.||...",
"||..||.|||||.||||||..||..|....||",
"|.||||.||..|...|.|..|||.|.|||.||",
"...||||.|..|||.|||..|.||...|.|||",
".||||.|..|.|..||..||..||..||||||",
"|||.|||..|||..||||.||||.|.||.|||",
"|||||.||...|.|.|.||...|||..|.|||",
".||||.|.|.|||...|||.|||.....||||",
"|||.|||.|.|...||.|....||||.|.|||",
".||||||.|||||||...||..|||.||||.|",
"||||.|||||.|.||.||||..|.|||.||||",
"|.|.||||....|.||||||||.|||||.|.|"
};
this.ecoArray = new char[32][32];
int index = 0;
for (String s : stringArray) {
this.ecoArray[index] = s.toCharArray();
++index;
}
}
public void tryPlant(int X, int Y){
if (this.ecoArray[X][Y] == '~' || this.ecoArray[X][Y] == '|') {
return;
}
if (this.ecoArray[X][Y] == '.') {
this.ecoArray[X][Y] = '~';
if ((Y - 1) >= 0) {
tryPlant(X, Y - 1);
}
if((Y + 1) < 32){
tryPlant(X, Y + 1);
}
if((X - 1) >= 0 ) {
tryPlant(X - 1, Y);
}
if((X + 1) < 32){
tryPlant(X + 1, Y);
}
}
}
private void displayInConsole() {
for (char[] cl : this.ecoArray) {
System.out.println(cl);
}
}
public static void main(String[] args) {
for (int x = 0; x < 32; x++) {
for (int y = 0; y < 32; y++) {
Test t = new Test();
t.tryPlant(x, y);
System.out.println("After tryPlant('" + x + "','" + y + "'): ");
t.displayInConsole();
}
}
}
}
Is it doing what you need ? I don't really understand where you want to start and where you want to stop...
The result is too big sorry...

Recursion and StackOverflow - what should I use instead?

I have a piece of code:
private void colorize(int color, int x, int y) {
visited[x][y] = true;
if (x + 1 < d)
if (board[x + 1][y] == board[x][y] && visited[x + 1][y] == false)
colorize(color, x + 1, y);
if (x - 1 >= 0)
if (board[x - 1][y] == board[x][y] && visited[x - 1][y] == false)
colorize(color, x - 1, y);
if (y + 1 < d)
if (board[x][y + 1] == board[x][y] && visited[x][y + 1] == false)
colorize(color, x, y + 1);
if (y - 1 >= 0)
if (board[x][y - 1] == board[x][y] && visited[x][y - 1] == false)
colorize(color, x, y - 1);
board[x][y] = color;
}
I call it: colorize(int random, int 0, int 0). This gives me stackoverflow even for a small table (20x20). How can I do this without recursion?
The code as given seems fine.
There is a question as to what d is, but I'm assuming that it is correctly the width of a square grid.
It's possible that you have a problem in whatever code calls this, but the code you have given us should not have a stack overflow.
Building off of Måns Rolandi Danielsson's answer, here is one that doesn't use the explicit stack, but builds one on the heap. My java is extremely rusty, but this should work. If anyone has fixes for this code, feel free to fix it.
Instead of getting a stack overflow, at large table sizes I get a java.lang.OutOfMemoryError: Java heap space error instead. You could probably use a set or some other data structure (rather than a linked list) to optimize memory usage.
import java.util.Queue;
import java.util.LinkedList;
class Pair<L,R> {
private L l;
private R r;
public Pair(L l, R r){
this.l = l;
this.r = r;
}
public L getL(){ return l; }
public R getR(){ return r; }
}
public class HelloW {
static int d = 20;
static boolean[][] visited = new boolean[d][d];
static int[][] board = new int[d][d];
static Queue<Pair<Integer, Integer>> Q = new LinkedList<Pair<Integer, Integer>>();
static void colorize(int color, int orig_x, int orig_y) {
Q.add(new Pair<Integer, Integer>(orig_x, orig_y));
while (Q.isEmpty() == false) {
Pair<Integer,Integer> foo = Q.remove();
int x = foo.getL();
int y = foo.getR();
int old_color = board[x][y];
visited[x][y] = true;
board[x][y] = color;
if (x + 1 < d)
if (board[x + 1][y] == old_color && visited[x + 1][y] == false)
Q.add(new Pair<Integer, Integer>(x+1, y));
if (x - 1 >= 0)
if (board[x - 1][y] == old_color && visited[x - 1][y] == false)
Q.add(new Pair<Integer, Integer>(x-1, y));
if (y + 1 < d)
if (board[x][y + 1] == old_color && visited[x][y + 1] == false)
Q.add(new Pair<Integer, Integer>(x, y+1));
if (y - 1 >= 0)
if (board[x][y - 1] == old_color && visited[x][y - 1] == false)
Q.add(new Pair<Integer, Integer>(x, y-1));
}
}
public static void main (String[] args) {
colorize(1, 0, 0);
}
}
I just copied your code and ran it like you can see below, and it works perfect, ends up with a matrix full of "true"'s and a matrix full of ones:
static int d = 20;
static boolean[][] visited = new boolean[d][d];
static int[][] board = new int[d][d];
static void colorize(int color, int x, int y) {
visited[x][y] = true;
if (x + 1 < d)
if (board[x + 1][y] == board[x][y] && visited[x + 1][y] == false)
colorize(color, x + 1, y);
if (x - 1 >= 0)
if (board[x - 1][y] == board[x][y] && visited[x - 1][y] == false)
colorize(color, x - 1, y);
if (y + 1 < d)
if (board[x][y + 1] == board[x][y] && visited[x][y + 1] == false)
colorize(color, x, y + 1);
if (y - 1 >= 0)
if (board[x][y - 1] == board[x][y] && visited[x][y - 1] == false)
colorize(color, x, y - 1);
board[x][y] = color;
}
public static void main(String[] args)
{
colorize(1, 0, 0);
}
EDIT: the test program runs fine for d = 20 on my computer, however if I increase to d = 100 I get the StackOverflow. It seems the algorithm is more or less fine, but the recursion goes unnecessary deep, there ought to be a more elegant formulation.
Your code works correctly as far as I can tell; the only thing that could affect it is if another thread came and modified one of your data structures during the recursion, or if you have over-simplified it before posting.

Categories