I'm programming a game using a recursive algorithm but I got "StackOverFlow error" when compiling it. I'm aware that that's probably due to not having a good end clause and it's re-cursing forever. I'm just trying to get some help on how to fix it.
I'm also aware that the code I'll be posting is a bit too much but I want to provide as much info as I can. Any doubts, just ask.
It starts with the following call:
Integer matrix[][] = new Integer[5][5];
Deque<String> path = new ArrayDeque<>();
ArrayList<Deque<String>> paths = new ArrayList<>();
CoordenateList cList = new CoordenateList();
solver2(matrix, 0, 0, 1, path, paths, cList);
The "solver2" method and the auxiliar methods are the following:
public static void solver2(Integer[][] matrix, int x, int y, int num,
Deque<String> path, ArrayList<Deque<String>> paths, CoordenateList cList) {
if (cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)) == null) {
Coordenate c = new Coordenate((Integer.toString(y) + "," + Integer.toString(x))); //substitir nos IF's
cList.addCoordenate(c);
}
if (y - 3 < 0 || (cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[0] == true || matrix[y - 3][x] != null) {
//NORTE
if (x + 3 > matrix.length - 1 || (cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[1] == true || matrix[y][x + 3] != null) {
//ESTE
if (y + 3 > matrix.length - 1 || (cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[2] == true || matrix[y + 3][x] != null) {
//SUL
if (x - 3 < 0 || (cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[3] == true || matrix[y][x - 3] != null) {
//OESTE
if (y - 2 < 0 || x + 2 > matrix.length - 1 || (cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[4] == true || matrix[y - 2][x + 2] != null) {
//NE
if (y + 2 > matrix.length - 1 || x + 2 > matrix.length - 1 || (cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[5] == true || matrix[y + 2][x + 2] != null) {
//SE
if (y + 2 > matrix.length - 1 || x - 2 < 0 || (cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[6] == true || matrix[y + 2][x - 2] != null) {
//SO
if (y - 2 < 0 || x - 2 < 0 || (cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[7] == true || matrix[y - 2][x - 2] != null) {
//NO
if (num == matrix.length * matrix.length) {
Deque<String> aux = new ArrayDeque<>();
aux.addAll(path);
paths.add(aux);
path.pop();
cleanRecord(num, matrix, cList); //desnecessário
num--;
} else {
//LOSER
path.pop();
num--;
y = getCoordenatePrevious(num, matrix)[1];
x = getCoordenatePrevious(num, matrix)[0];
num++;
cleanRecord(num, matrix, cList);
num--;
}
} else {
matrix[y - 2][x - 2] = num + 1;
(cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[7] = true;
path.push(Integer.toString(y + 2) + "," + Integer.toString(x - 2));
solver2(matrix, x - 2, y + 2, num + 1, path, paths, cList);
}
} else {
matrix[y + 2][x - 2] = num + 1;
(cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[6] = true;
path.push(Integer.toString(y + 2) + "," + Integer.toString(x - 2));
solver2(matrix, x - 2, y + 2, num + 1, path, paths, cList);
}
} else {
matrix[y + 2][x + 2] = num + 1;
(cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[5] = true;
path.push(Integer.toString(y + 2) + "," + Integer.toString(x + 2));
solver2(matrix, x + 2, y + 2, num + 1, path, paths, cList);
}
} else {
matrix[y - 2][x + 2] = num + 1;
(cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[4] = true;
path.push(Integer.toString(y - 2) + "," + Integer.toString(x + 2));
solver2(matrix, x + 2, y - 2, num + 1, path, paths, cList);
}
} else {
matrix[y][x - 3] = num + 1;
(cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[3] = true;
path.push(Integer.toString(y) + "," + Integer.toString(x - 3));
solver2(matrix, x - 3, y, num + 1, path, paths, cList);
}
} else {
matrix[y + 3][x] = num + 1;
(cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[2] = true;
path.push(Integer.toString(y + 3) + "," + Integer.toString(x));
solver2(matrix, x, y + 3, num + 1, path, paths, cList);
}
} else {
matrix[y][x + 3] = num + 1;
(cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[1] = true;
path.push(Integer.toString(y) + "," + Integer.toString(x + 3));
solver2(matrix, x + 3, y, num + 1, path, paths, cList);
}
} else {
matrix[y - 3][x] = num + 1;
(cList.getCoordenateByKey(Integer.toString(y) + "," + Integer.toString(x)).getVisited())[0] = true;
path.push(Integer.toString(y - 3) + "," + Integer.toString(x));
solver2(matrix, x, y - 3, num + 1, path, paths, cList);
}
int cont = 0;
if (checkIfNumExists(num, matrix)) {
boolean[] b = cList.getCoordenateByKey(Integer.toString(getCoordenatePrevious(num, matrix)[1]) + "," + Integer.toString(getCoordenatePrevious(num, matrix)[0])).getVisited();
if (y - 3 >= 0 && matrix[y - 3][x] == null && b[0] == false) {
//NORTE
cont++;
solver2(matrix, x, y, num, path, paths, cList);
} else if (x + 3 < matrix.length && matrix[y][x + 3] == null && b[1] == false) {
//ESTE
cont++;
solver2(matrix, x, y, num, path, paths, cList);
} else if (y + 3 < matrix.length && matrix[y + 3][x] == null && b[2] == false) {
//SUL
cont++;
solver2(matrix, x, y, num, path, paths, cList);
} else if (x - 3 >= 0 && matrix[y][x - 3] == null && b[0] == false && b[3] == false) {
//OESTE
cont++;
solver2(matrix, x, y, num, path, paths, cList);
} else if (y - 2 >= 0 && x + 2 < matrix.length && matrix[y - 2][x + 2] == null && b[4] == false) {
//NE
cont++;
solver2(matrix, x, y, num, path, paths, cList);
} else if (y + 2 < matrix.length && x + 2 < matrix.length && matrix[y + 2][x + 2] == null && b[5] == false) {
//SE
cont++;
solver2(matrix, x, y, num, path, paths, cList);
} else if (y + 2 < matrix.length && x - 2 >= 0 && matrix[y + 2][x - 2] == null && b[6] == false) {
//SO
cont++;
solver2(matrix, x, y, num, path, paths, cList);
} else if (y - 2 >= 0 && x - 2 >= 0 && matrix[y - 2][x - 2] == null && b[7] == false) {
//NO
cont++;
solver2(matrix, x, y, num, path, paths, cList);
}
}
if (cont == 0 && checkIfNumExists(num, matrix)) {
if (num != 1) {
num--;
path.pop();
cleanRecord(num + 1, matrix, cList);
cleanMatrixAbove(num, matrix, cList);
}
}
}
/**
* returns the coordenates of the num
*
* #param num
* #param matrix
* #return
*/
public static int[] getCoordenatePrevious(int num, Integer[][] matrix) {
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix.length; j++) {
if (matrix[i][j] != null && matrix[i][j] == num) {
int[] array = new int[2];
array[0] = j; //x
array[1] = i; //y
return array;
}
}
}
return null;
}
/**
* cleans the visited record of the num
*
* #param num
* #param matrix
* #param cList
*/
public static void cleanRecord(int num, Integer[][] matrix, CoordenateList cList) {
if (checkIfNumExists(num, matrix)) {
Coordenate c = cList.getCoordenateByKey(Integer.toString(getCoordenatePrevious(num, matrix)[1]) + "," + Integer.toString(getCoordenatePrevious(num, matrix)[0]));
c.cleanVisited();
}
}
/**
* sets null every number in the matrix that are bigger than num
*
* #param num
* #param matrix
* #param cList
*/
public static void cleanMatrixAbove(int num, Integer[][] matrix, CoordenateList cList) {
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix.length; j++) {
if (matrix[i][j] != null && matrix[i][j] > num) {
matrix[i][j] = null;
}
}
}
}
/**
* checks if the num exists in the matrix
*
* #param num
* #param matrix
* #return
*/
public static boolean checkIfNumExists(int num, Integer[][] matrix) {
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix.length; j++) {
if (matrix[i][j] != null && matrix[i][j] == num) {
return true;
}
}
}
return false;
}
The other classes (Coordenate, CoordenateList) are the following:
public class Coordenate {
String keyID;
boolean[] visited;
public Coordenate(String keyID) {
this.keyID = keyID;
this.visited = new boolean[8];
}
public String getKeyID() {
return this.keyID;
}
public int getXFromKeyID() {
String[] aux = this.keyID.split(",");
int xaux = Integer.parseInt(aux[0]);
return xaux;
}
public int getYFromKeyID() {
String[] aux = this.keyID.split(",");
int yaux = Integer.parseInt(aux[1]);
return yaux;
}
public boolean[] getVisited() {
return visited;
}
public void cleanVisited() {
visited = new boolean[8];
}
public boolean allVisited() {
for (int i = 0; i < 8; i++) {
if (visited[i] = false) {
return false;
}
}
return true;
}
#Override
public String toString() {
return "Coordenate{" + "keyID=" + keyID + ", visited=" + visited + '}';
}
#Override
public int hashCode() {
int hash = 3;
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Coordenate other = (Coordenate) obj;
if (!Objects.equals(this.keyID, other.keyID)) {
return false;
}
return true;
}
}
public class CoordenateList {
public ArrayList<Coordenate> coordenateList;
public CoordenateList() {
coordenateList = new ArrayList<>();
}
public ArrayList<Coordenate> getCoordenateList() {
return coordenateList;
}
public Coordenate getCoordenateByKey(String keyID) {
for (Coordenate c : coordenateList) {
if (c.getKeyID().equals(keyID)) {
return c;
}
}
return null;
}
public boolean addCoordenate(Coordenate c) {
if (!coordenateList.contains(c)) {
return coordenateList.add(c);
}
return false;
}
public boolean removeCoordenate(Coordenate c){
if (coordenateList.contains(c)) {
return coordenateList.remove(c);
}
return false;
}
}
Once again, I'm sorry for the huge code, but I really want to give as much info as I possibly can. Any questions or doubts, feel free to ask.
If you were getting a StackOverFlow error when compiling your code, then you would have found a bug in your compiler. But you are definitely not receiving a StackOverFlow error when compiling your code, you are receiving it when running your code.
(Recursion is an advanced computer science topic, but if you are still struggling with concepts like when you are compiling your program and when you are running it, then recursion might be a bit too advanced for you right now. Just a thought.)
In your solver2() function you have a monstrous nested if() statement which will always recurse except for a certain unlikely set of circumstances.
Then, you have another sequence of if()-else statements which will also always recurse unless none of the tested conditions is true.
I do not think anyone will invest all the time necessary to read and make sense out of this monstrosity that you have there, so the only recommendation that can be made is to add more conditions in your recursive function which will make it far more likely to refrain from recursing at some point, so that your recursive function will not keep recursing forever. (That is, until it runs out of stack.)
Related
I want the output to be something like "6 + 6 + 6 + 6 = 24" but seeing as how the System.out.print(x + " + ") is inside the recursive loop, it ends up outputting: "6 + 6 + 6 + 6 + = 24"
In the main program I have a separate line of code to output the result:
System.out.print("= " + result);
public static int recursiveMultiply(int x, int y){
//Make y positive
if(y < 0)
return recursiveMultiply(-x, -y);
//base case
if(y == 0)
return 0;
//recursive case
System.out.print(x + " + ");
return x + recursiveMultiply(x, y-1);
As the first 2 conditions, a if(y>=1) is useless
public static int recursiveMultiply(int x, int y) {
if (y < 0) {
return recursiveMultiply(-x, -y);
}
if (y == 0) {
return 0;
}
System.out.print(x);
if (y != 1) {
System.out.print(" + ");
}
return x + recursiveMultiply(x, y - 1);
}
public static void main(String[] args) {
System.out.println("="+ recursiveMultiply(-6,-4));
}
private static int recursiveMultiply(int i, int j) {
if (j< 0) {
return recursiveMultiply(i, Math.abs(-j));
}
if (j == 0) {
return 0;
}
System.out.print(i);
if (j!= 1) {
System.out.print(" + ");
}
return i + recursiveMultiply(i, j - 1);
}
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.
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...
I have a program that I am writing in Java and have to do 2 things, find the longest common sub-sequence and align the common characters. The LCS works just fine but the align part just loops away or do nothing.
I try to do this algorithm which I found on Wikipedia
function printDiff(C[0..m,0..n], X[1..m], Y[1..n], i, j)
if i > 0 and j > 0 and X[i] = Y[j]
printDiff(C, X, Y, i-1, j-1)
print " " + X[i]
else if j > 0 and (i = 0 or C[i,j-1] ≥ C[i-1,j])
printDiff(C, X, Y, i, j-1)
print "+ " + Y[j]
else if i > 0 and (j = 0 or C[i,j-1] < C[i-1,j])
printDiff(C, X, Y, i-1, j)
print "- " + X[i]
else
print ""
Here is the code I wrote (I removed the LCS part)
static char[] input1 = "ABCDE".toCharArray();
static char[] input2 = "ACDC".toCharArray();
static int M = input1.length;
static int N = input2.length;
static int[][] opt = new int[M + 1][N + 1];
public static void printDiff(int opt[][], char input1[], char input2[]) {
int i = 0, j = 0;
while (i < input1.length && j < input2.length) {
if (i > 0 && j > 0 && input1[i] == input2[j]) {
System.out.print(" " + input1[i]);
i++;
j++;
} else if (j > 0 && (i == 0 || opt[i][j - 1] >= opt[i - 1][j])) {
System.out.print("+ " + input2[j]);
j++;
} else if (i > 0 && (j == 0 || opt[i][j - 1] < opt[i - 1][j])) {
System.out.print("- " + input1[i]);
i++;
} else {
System.out.print("");
}
}
}
I rewrote your code to use the Wikipedia algorithm. In other words, I used recursion rather than a where clause. I had to change one of the if conditions because Java is zero index based and the Wikipedia algorithm is one index based.
I had to add the LCS function back in so that I could calculate the int[][]opt.
I added parenthesis to the if statements to make sure that the operations were done in the order I wanted them done.
I also fixed the output. The Wikipedia algorithm had "+ " and "- " as output. That appears to be a typo. The output should be " +" and " -", respectively.
Here's my version of the code.
public class PrintDiff {
char[] input1 = "ABCDE".toCharArray();
char[] input2 = "ACDC".toCharArray();
int M = input1.length;
int N = input2.length;
public void run() {
int[][] opt = lcsLength(input1, input2);
printDiff(opt, input1, input2, M - 1, N - 1);
}
public int[][] lcsLength(char[] input1, char[] input2) {
int[][] opt = new int[M][N];
for (int i = 1; i < input1.length; i++) {
for (int j = 1; j < input2.length; j++) {
if (input1[i] == input2[j]) {
opt[i][j] = opt[i - 1][j - 1] + 1;
} else {
opt[i][j] = Math.max(opt[i][j - 1], opt[i - 1][j]);
}
}
}
return opt;
}
public void printDiff(int opt[][], char input1[], char input2[], int i,
int j) {
if ((i >= 0) && (j >= 0) && (input1[i] == input2[j])) {
printDiff(opt, input1, input2, i - 1, j - 1);
System.out.print(" " + input1[i]);
} else if ((j > 0) && ((i == 0) || (opt[i][j - 1] >= opt[i - 1][j]))) {
printDiff(opt, input1, input2, i, j - 1);
System.out.print(" +" + input2[j]);
} else if ((i > 0) && ((j == 0) || (opt[i][j - 1] < opt[i - 1][j]))) {
printDiff(opt, input1, input2, i - 1, j);
System.out.print(" -" + input1[i]);
} else {
System.out.print("");
}
}
public static void main(String[] args) {
new PrintDiff().run();
}
}
And here's my output.
A -B C D -E +C
Here is a version which returns the diffs of all the longest common subsequences (basically backtracks using the cached table - similar to the approach taken to get to all longest common subsequences in All Longest Common Subsequences) (or, you may refer to my blog#: http://codingworkout.blogspot.com/2014/07/longest-common-subsequence.html)
for ex, for GAC and AGCAT, it returns => { { "[G][A]C", "[G]A[C]", "G[A][C]" }, {"A[G]C[A]T", "A[G][C]AT", "[A]G[C]AT"} where GA, GC and AC are longest common subsequences...
string[][] GetDiffs(string A, string B, int aIndex, int bIndex,
int[][] DP_LCS_AllPrefixes_Cache)
{
if((aIndex == 0) && (bIndex ==0))
{
return null;
}
if (DP_LCS_AllPrefixes_Cache[aIndex][bIndex] == 0)
{
var r = new string[2][];
r[0] = new string[] { A.Substring(0, aIndex) };
r[1] = new string[] { B.Substring(0, bIndex) };
return r;
}
if (A[aIndex - 1] == B[bIndex - 1])
{
var r = this.GetDiffs(A, B, aIndex - 1, bIndex - 1,
DP_LCS_AllPrefixes_Cache);
string ch = string.Format("[{0}]", A[aIndex - 1]);
if (r == null)
{
r = new string[2][];
r[0] = new string[] { ch };
r[1] = new string[] { ch };
}
else
{
r[0] = r[0].Select(s => s + ch).ToArray();
r[1] = r[1].Select(s => s + ch).ToArray();
}
return r;
}
int lcs_up_direction = DP_LCS_AllPrefixes_Cache[aIndex - 1][bIndex];
int lcs_left_direction = DP_LCS_AllPrefixes_Cache[aIndex][bIndex - 1];
string[][] lcs_up = null, lcs_left = null;
if (lcs_up_direction == lcs_left_direction)
{
lcs_up = this.GetDiffs(A, B, aIndex - 1, bIndex,
DP_LCS_AllPrefixes_Cache);
lcs_left = this.GetDiffs(A, B, aIndex, bIndex - 1,
DP_LCS_AllPrefixes_Cache);
}
else if (lcs_up_direction > lcs_left_direction)
{
lcs_up = this.GetDiffs(A, B, aIndex - 1, bIndex,
DP_LCS_AllPrefixes_Cache);
}
else
{
lcs_left = this.GetDiffs(A, B, aIndex, bIndex - 1, DP_LCS_AllPrefixes_Cache);
}
char a = A[aIndex - 1], b = B[bIndex - 1];
string[][] rl = new string[2][];
rl[0] = new string[0];
rl[1] = new string[0];
if(lcs_up != null)
{
//we moved upward, that is we accepted that they differ with 'A' at aIndex-1 (a)
rl[0] = lcs_up[0].Select(s => s + a.ToString()).ToArray();
rl[1] = lcs_up[1];
}
if (lcs_left != null)
{
//we moved left, that is we accepted that they differ with 'B' at bIndex-1 (b)
rl[0] = rl[0].Union(lcs_left[0]).ToArray(); ;
rl[1] = rl[1].Union(lcs_left[1].Select(s => s + b.ToString())).ToArray();
}
return rl.ToArray();
}
where the caller is
string[][] GetDiffs(string A, string B, int[][] DP_LCS_AllPrefixes_Cache)
{
var r = this.GetDiffs(A, B, A.Length, B.Length,
DP_LCS_AllPrefixes_Cache);
return r;
}
And the DP method which captures LCS lengths to backtrack
public int[][] LCS_OfAllPrefixes_Length(string A, string B)
{
A.ThrowIfNullOrWhiteSpace("a");
B.ThrowIfNullOrWhiteSpace("b");
int[][] DP_LCS_AllPrefixes_Cache = new int[A.Length+1][];
for(int i = 0;i<DP_LCS_AllPrefixes_Cache.Length; i++)
{
DP_LCS_AllPrefixes_Cache[i] = new int[B.Length + 1];
}
for (int rowIndexOfCache = 1; rowIndexOfCache <= A.Length; rowIndexOfCache++)
{
for (int columnIndexOfCache = 1; columnIndexOfCache <= B.Length; columnIndexOfCache++)
{
//LCS(Ai, Bj) = 0 if i <=0, or j <= 0
// LCS(Ai, Bj) + 1 if Ai == Bj
// Max(LCS(Ai-1, Bj), LCS(Ai, Bj-1))
if(A[rowIndexOfCache-1] == B[columnIndexOfCache-1])
{
DP_LCS_AllPrefixes_Cache[rowIndexOfCache][columnIndexOfCache] = DP_LCS_AllPrefixes_Cache[rowIndexOfCache - 1][columnIndexOfCache - 1] + 1;
}
else
{
DP_LCS_AllPrefixes_Cache[rowIndexOfCache][columnIndexOfCache] = Utilities.Max(DP_LCS_AllPrefixes_Cache[rowIndexOfCache - 1][columnIndexOfCache],
DP_LCS_AllPrefixes_Cache[rowIndexOfCache][columnIndexOfCache - 1]);
}
}
}
return DP_LCS_AllPrefixes_Cache;
}
TestMethod
[TestMethod]
public void LCS_Tests()
{
string A = "GAC", B = "AGCAT";
var DP_LCS_AllPrefixes_Cache = this.LCS_OfAllPrefixes_Length(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Cache[A.Length][B.Length] == 2);
var lcs_sequences = this.GetLongestCommonSubsequences(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(lcs_sequences);
var diffs = this.GetDiffs(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(diffs);
Assert.IsTrue(diffs.Length == 2);
Assert.IsTrue(diffs[0].Length == lcs_sequences.Length);
Assert.IsTrue(diffs[1].Length == lcs_sequences.Length);
Assert.IsTrue(lcs_sequences.Any(s => "AC".Equals(s)));
Assert.IsTrue(lcs_sequences.Any(s => "GC".Equals(s)));
Assert.IsTrue(lcs_sequences.Any(s => "GA".Equals(s)));
var DP_LCS_AllPrefixes_Subsequences_Cache = this.LCS_OfAllPrefixes_Subsequences(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Length == 2);
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Subsequences
.Any(s => "AC".Equals(s)));
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Subsequences
.Any(s => "GC".Equals(s)));
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Subsequences
.Any(s => "GA".Equals(s)));
A = "ABCDGH"; B = "AEDFHR";
DP_LCS_AllPrefixes_Cache = this.LCS_OfAllPrefixes_Length(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Cache[A.Length][B.Length] == 3);
lcs_sequences = this.GetLongestCommonSubsequences(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(lcs_sequences);
diffs = this.GetDiffs(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(diffs);
Assert.IsTrue(diffs.Length == 2);
Assert.IsTrue(diffs[0].Length == lcs_sequences.Length);
Assert.IsTrue(diffs[1].Length == lcs_sequences.Length);
Assert.IsTrue(lcs_sequences.Any(s => "ADH".Equals(s)));
DP_LCS_AllPrefixes_Subsequences_Cache = this.LCS_OfAllPrefixes_Subsequences(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Length == 3);
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Subsequences
.Any(s => "ADH".Equals(s)));
A = "AGGTAB"; B = "GXTXAYB";
DP_LCS_AllPrefixes_Cache = this.LCS_OfAllPrefixes_Length(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Cache[A.Length][B.Length] == 4);
lcs_sequences = this.GetLongestCommonSubsequences(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(lcs_sequences);
diffs = this.GetDiffs(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(diffs);
Assert.IsTrue(diffs.Length == 2);
Assert.IsTrue(diffs[0].Length == 2);
Assert.IsTrue(diffs[1].Length == lcs_sequences.Length);
Assert.IsTrue(lcs_sequences.Any(s => "GTAB".Equals(s)));
DP_LCS_AllPrefixes_Subsequences_Cache = this.LCS_OfAllPrefixes_Subsequences(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Length == 4);
Assert.IsTrue(DP_LCS_AllPrefixes_Subsequences_Cache[A.Length][B.Length].Subsequences
.Any(s => "GTAB".Equals(s)));
A = "ABCDEF"; B = "UVWXYZ";
DP_LCS_AllPrefixes_Cache = this.LCS_OfAllPrefixes_Length(A, B);
Assert.IsTrue(DP_LCS_AllPrefixes_Cache[A.Length][B.Length] == 0);
lcs_sequences = this.GetLongestCommonSubsequences(A, B, DP_LCS_AllPrefixes_Cache);
diffs = this.GetDiffs(A, B, DP_LCS_AllPrefixes_Cache);
Assert.IsNotNull(diffs);
Assert.IsTrue(diffs.Length == 2);
Assert.IsTrue(diffs[0].Length == 1);
Assert.IsTrue(diffs[1].Length == 1);
}
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I've coded an entire program to play Connect Four, but the algorithm for checking who has won (after each turn) isn't working and I don't know why. I keep getting a weird message when I compile it (exception ArrayIndexOutOfBoundsException). Any help is appreciated.
Here is the code:
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class connectFourDemo extends Applet
implements MouseListener, MouseMotionListener {
private static final long serialVersionUID = 1L;
int[][] myGrid = new int[7][6];
// If piece is 0, white. If 1, red. If 2, black.
int xCoord, yCoord; // X and Y co-ordinates for mouse navigation.
int width, height;
int playerTurn = 1; // Player's turn. Default is 1 since red goes first.
int mx, my; // The mouse coordinates.
boolean isButtonPressed = false;
public void init() {
width = getSize().width;
height = getSize().height;
setBackground(Color.yellow);
mx = width / 2;
my = height / 2;
addMouseListener(this);
addMouseMotionListener(this);
}
private int getXValue(int xValue) {
if (xValue < width / 7) return 0;
else if (xValue < width / 7 * 2) return 1;
else if (xValue < width / 7 * 3) return 2;
else if (xValue < width / 7 * 4) return 3;
else if (xValue < width / 7 * 5) return 4;
else if (xValue < width / 7 * 6) return 5;
else return 6;
}
private int getYValue(int yValue) {
if (yValue < width / 6) return 0;
else if (yValue < width / 6 * 2) return 1;
else if (yValue < width / 6 * 3) return 2;
else if (yValue < width / 6 * 4) return 3;
else if (yValue < width / 6 * 5) return 4;
else return 5;
}
public void verticalCheck(int x, int y) {
if (myGrid[x][y] == 1) {
int counter = 1;
for (int i = 5; i >= 0; i--) {
if (myGrid[xCoord][i] == 1) {
System.out.println("Counter one in vertical check is " + counter + ".");
if (myGrid[xCoord][i - 1] == 1 && (i - 1 >= 0)) counter++;
}
}
if (counter == 4) {
System.out.println("Player 1 has won Connect Four vertically!");
}
}
else if (myGrid[x][y] == 2) {
int counter = 1;
for (int i = 5; i >= 0; i--) {
if (myGrid[xCoord][i] == 2) {
System.out.println("Counter two in vertical check is " + counter + ".");
if (myGrid[xCoord][i - 1] == 2 && (i - 1 >= 0)) counter++;
}
}
if (counter == 4) {
System.out.println("Player 2 has won Connect Four vertically!");
}
}
}
public void horizontalCheck(int x, int y) {
if (myGrid[x][y] == 1) {
int counter = 1;
for (int i = 0; i <= 6; i++) {
if (myGrid[i][y] == 1) {
System.out.println("Counter one in horizontal check is " + counter + ".");
if (myGrid[i + 1][y] == 1 && (i + 1 <= 6)) counter++;
}
}
if (counter == 4) {
System.out.println("Player 1 has won Connect Four horizontally!");
}
}
else if (myGrid[x][y] == 2) {
int counter = 1;
for (int i = 0; i <= 6; i++) {
if (myGrid[i][y] == 2) {
System.out.println("Counter two in horizontal check is " + counter + ".");
if (myGrid[i + 1][y] == 2 && (i + 1 <= 6)) counter++;
}
}
if (counter == 4) {
System.out.println("Player 2 has won Connect Four horizontally!");
}
}
}
public void diagonalCheckRight(int x, int y) {
if (myGrid[x][y] == 1) {
int counter = 1;
for (int i = 0; i <= 6; i++) {
for (int j = 5; j >= 0; j--) {
if (myGrid[i][j] == 1) {
System.out.println("Counter one in diagonal check right is " + counter + ".");
if (myGrid[i + 1][j + 1] == 1 && (i + 1 <= 6) && (j + 1 <= 5)) counter++;
else if (myGrid[i - 1][j + 1] == 1 && (i - 1 >= 0) && (j + 1 <= 5)) counter++;
}
}
}
if (counter == 4) {
System.out.println("Player 1 has won Connect Four diagonally!");
}
}
else if (myGrid[x][y] == 2) {
int counter = 1;
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 5; j++) {
if (myGrid[i][j] == 2) {
System.out.println("Counter two in diagonal check right is " + counter + ".");
if (myGrid[i + 1][j + 1] == 1 && (i + 1 <= 6) && (j + 1 <= 5)) counter++;
else if (myGrid[i - 1][j + 1] == 1 && (i - 1 >= 0) && (j + 1 <= 5)) counter++;
}
}
}
if (counter == 4) {
System.out.println("Player 2 has won Connect Four diagonally!");
}
}
}
public void diagonalCheckLeft(int x, int y) {
if (myGrid[x][y] == 1) {
int counter = 1;
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 5; j++) {
if (myGrid[i][j] == 1) {
System.out.println("Counter one in diagonal check left is " + counter + ".");
if (myGrid[i - 1][j - 1] == 1 && (i + 1 <= 6) && (j - 1 >= 0)) counter++;
}
}
}
if (counter == 4) {
System.out.println("Player 1 has won Connect Four diagonally!");
}
}
else if (myGrid[x][y] == 2) {
int counter = 1;
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 5; j++) {
if (myGrid[i][j] == 2) {
System.out.println("Counter one in diagonal check left is " + counter + ".");
if (myGrid[i - 1][j - 1] == 2 && (i <= 6) && (j >= 0)) counter++;
}
}
}
if (counter == 4) {
System.out.println("Player 2 has won Connect Four diagonally!");
}
}
}
public void mouseEntered( MouseEvent e ) {
// Called when the pointer enters the applet's rectangular area.
}
public void mouseExited( MouseEvent e ) {
// Called when the pointer leaves the applet's rectangular area.
}
public void mouseClicked( MouseEvent e ) {
// Called after a press and release of a mouse button with no motion in between.
mx = e.getX();
my = e.getY();
xCoord = getXValue(mx);
yCoord = getYValue(my);
if (myGrid[xCoord][yCoord] == 0 && playerTurn == 1) { // Drop from top, fall to bottom and vice versa.
for (int y = 5; y >= yCoord; y--) {
if (myGrid[xCoord][y] == 0) {
myGrid[xCoord][y] = 1;
y = yCoord - 1;
}
}
verticalCheck(xCoord, yCoord);
horizontalCheck(xCoord, yCoord);
diagonalCheckRight(xCoord, yCoord);
diagonalCheckLeft(xCoord, yCoord);
playerTurn = 2;
}
else if (myGrid[xCoord][yCoord] == 0 && playerTurn == 2) {
for (int y = 5; y >= yCoord; y--) {
if (myGrid[xCoord][y] == 0) {
myGrid[xCoord][y] = 2;
y = yCoord - 1;
}
}
verticalCheck(xCoord, yCoord);
horizontalCheck(xCoord, yCoord);
diagonalCheckRight(xCoord, yCoord);
diagonalCheckLeft(xCoord, yCoord);
playerTurn = 1;
}
}
public void mousePressed(MouseEvent e) { // Called after a button is pressed down.
repaint();
// "Consume" the event so it won't be processed in the
// default manner by the source which generated it.
e.consume();
}
public void mouseReleased(MouseEvent e) { // Called after a button is released.
repaint();
e.consume();
}
public void mouseMoved(MouseEvent e) { // Called during motion when no buttons are down.
mx = e.getX();
my = e.getY();
mx = mx / 50; // Divides applet width by the width of each oval (50).
my = my / 50; // Divides applet height by the height of each oval (50).
showStatus("Mouse in column " + (mx + 1) + ", row " + (my + 1) + ".");
}
public void mouseDragged(MouseEvent e) { // Called during motion with buttons down.
}
public void paint(Graphics g) {
for (int y = 0; y < 6; y++) {
for (int x = 0; x < 7; x++) {
if (myGrid[x][y] == 0) {
g.setColor(Color.white);
}
if (myGrid[x][y] == 1) {
g.setColor(Color.red);
}
if (myGrid[x][y] == 2) {
g.setColor(Color.black);
}
g.fillOval((width / 7) * x + 2, (height / 6) * y + 1, (width / 7) - 4, (height / 6) - 4);
}
}
}
}
Ran the code, your problem is in "diagonalCheckRight", namely this section:
for (int i = 0; i <= 6; i++) {
for (int j = 5; j >= 0; j--) {
if (myGrid[i][j] == 1) {
System.out.println("Counter one in diagonal check right is " + counter + ".");
if (myGrid[i + 1][j + 1] == 1 && (i + 1 <= 6) && (j + 1 <= 5)) counter++;
else if (myGrid[i - 1][j + 1] == 1 && (i - 1 >= 0) && (j + 1 <= 5)) counter++;
}
}
}
Your j index starts at 5, in the if you do myGrid[i+1][j+1] so that means on the first iteration you're accessing myGrid[1][6], however you defined myGrid to be of size [7][6] and so you're out of bounds because the valid indices are: [0..6][0..5].
Also next time look at the error message, my console was showing:
java.lang.ArrayIndexOutOfBoundsException: 6
at Main.diagonalCheckRight(Main.java:134)
I renamed the class to Main, and that 134 is exactly the line number where I found the error.