I am working on my maze game and I've almost got the generating of the maze. The only problem I have is that when I try to run my init method, it gives me a stack overflow error. I think this has to do with the corStack getting too large, but I can't find the cause of this problem. Here's the code:
The maze generating:
public void carveMaze(Stack<Integer> corStack, int currentX, int currentY)
{
ArrayList<Integer> corList = getNeighbours(currentX, currentY);
Random randomGen = new Random();
while (checkForUnvisited()) {enter code here
if (corList.size() > 0) {
boolean goodNumber = false;
int newY = 0;
int newX = 0;
int index = 0;
index = randomGen.nextInt(corList.size());
if (index % 2 != 0) {
newY = corList.get(index);
goodNumber = true;
}
goodNumber = false;
while (!goodNumber) {
index = randomGen.nextInt(corList.size());
if (index % 2 == 0) {
newX = corList.get(index);
goodNumber = true;
}
}
corStack.push(currentY);
corStack.push(currentX);
if (newX > currentX) {
maze[newY][newX - 1].setStatus(Cell.WEG);
maze[newY][newX - 1].setVisited(true);
maze[newY][newX].setVisited(true);
} else if (newX < currentX) {
maze[newY][newX + 1].setStatus(Cell.WEG);
maze[newY][newX + 1].setVisited(true);
maze[newY][newX].setVisited(true);
} else if (newY > currentY) {
maze[newY - 1][newX].setStatus(Cell.WEG);
maze[newY - 1][newX].setVisited(true);
maze[newY][newX].setVisited(true);
} else if (newY < currentY) {
maze[newY + 1][newX].setStatus(Cell.WEG);
maze[newY + 1][newX].setVisited(true);
maze[newY][newX].setVisited(true);
}
maze[currentY][currentX].setVisited(true);
currentX = newX;
currentY = newY;
carveMaze(corStack, currentX, currentY);
} else {
if (!corStack.isEmpty()) {
currentX = (int) corStack.pop();
currentY = (int) corStack.pop();
carveMaze(corStack, currentX, currentY);
}
}
}
}
The cell Class:
package javaapplication23;
public class Cell {
public static final int SPELER = 2;
public static final int WEG = 0;
public static final int MUUR = 1;
public static final int BAZOOKA = 3;
private int status;
private boolean visited;
Cell(int status, boolean visited)
{
this.status = status;
this.visited = visited;
}
public int getStatus()
{
return status;
}
public boolean getVisited()
{
return visited;
}
public void setStatus(int status)
{
this.status = status;
}
public void setVisited(boolean visited)
{
this.visited = visited;
}
}
I double checked everything but I can't find the cause of the problem. The problem starts when I put the else{if(!corstack.isempty)} section after the if(corList.size() > 0) statement so I know it's in there somewhere.
stacktrace:
Exception in thread "main" java.lang.StackOverflowError
at java.util.ArrayList.grow(ArrayList.java:239)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:220)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:212)
at java.util.ArrayList.add(ArrayList.java:443)
at javaapplication23.MazeManager.getNeighbours(MazeManager.java:154)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:55)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:105)
at javaapplication23.MazeManager.carveMaze(MazeManager.java:110)
You are calling carveMaze() within carveMaze() and this causes the StackOverflowError. Everytime the method is called, its variables take up space in the stack, which are garbage collected when the method completes. In your case, before the method completes, you are calling it again and therefore your stack gets filled-up more before it had time to empty-up. Your stack gets full ("overflown") thus the Error. If you want to call carveMaze() continuously, you'd be better off with a loop of some sort.
Before you go to any trouble changing the structure of your code, you need to be sure there is no faulty logic within your algorithm implementation. This is partly just stating the obvious, but it's worth mentioning:
Try your code with minimally sized mazes and see if they complete without an overflow. If they consistently work, then your logic might be sound. If they don't, then there's faulty logic in finding an exit condition for your recursive loop (and that will guarantee an overflow every time).
Try to pinpoint (using your debugger) why these minimal mazes are not finding a termination condition (ie. the case where the recursive calls will stop happening). This can be difficult to do, but is made easier if you are working with minimal reproducible examples - ie. very small mazes.
Sometimes it can also help to put some conditional blocks into your source solely to allow you to set a breakpoint that can be triggered only when a condition is met (ie. allowing you to break when a list is under a certain size for example).
Once you get it working for some mazes, Unit Testing each part of your code is a great way to ensure that special circumstances ("corner cases") aren't causing your code to derail on seemingly arbitrary occasions.
Once small examples work successfully for many inputs, you can start creating larger mazes. If the stack overflow is only happening on very large mazes then its possibly just an issue with the size of the stack.
At that point, if this appears to definitely be the problem, try increasing your java stack size:
java -Xss4m Test
for more info look here: How to increase the Java stack size?
If increasing the stack size solves the problem then all may be good: you can either work around your issues by increasing the stack size every time you run your code, or change the code to remove the recursion, but at least you'll know the logic does find a termination condition eventually given enough memory resources to work with.
The general approach to removing recursion is to use a loop and your own Stack data structure rather than the virtual machine's stack. Further info here:
Way to go from recursion to iteration
I think the most important point is that debugging code is easier to do with a debugger than just from reading it, which makes you, the writer of the code, the person in the best position to fix it.
Besides, debugging your code is usually more fun than writing questions asking other people to help.
Hope these ideas help you solve your problem.
Looks like your program is calling carveMaze() recursively without any base conditions and thus you are getting StackOverFlow Issue.Can you identify a case when this function should stop calling itself.Please find an example here
I found it :) the fault was with the getting of random new neighbour. I inserted the coordinates like x, y, x, y, x, y etc. in the neighbour list and then chose random coordinates. instead of getting the x and following y, i chose a random x with random y :0
thanks all!
Related
I tried this a lot, and debugged it a few times, everything seems to be working and largest prime does indeed become the largest prime even if it takes rather long.
I can't get the printed value from System.out.println. I could find it through the debugger but the value is too high to find fast just holding down step over.
It compiles as well so I am stumped about what's the issue here. I would be very happy to know what I did wrong.
Edit: The reason why I wrote this code in the first place is because in the site project euler it asked for the largest prime value that when divided with the value of primer gave a whole number.
Is there a way at least that would allow me to make it faster with the same value? this seems rather impractical.
package unit5;
public class Primefinder { public static void main(String[] args)
{
double primer = 600851475143d;
double largestprime = 0;
Boolean ifprime = false;
for(double x = 2d; x < primer; x++)
{
for(double z = 2d; z<x; z++)
{
if( (x%z == 0) && (z != x) )
{
ifprime = false;
break;
}
else {
ifprime = true;
}
}
if((ifprime != false) && (x > largestprime))
{
largestprime = x;
}
ifprime = false;
}
System.out.print(largestprime);
}
}
for other questions you might ask everywhere, please tell us that what is the purpose of your code. this way it is easier to get the fault.
the code you have written above runs completely but the numbers you have used are too big so you need to wait a lot, so that compiler be able to reach to this line:
System.out.print(largestprime);
use lower numbers (at least for test) or wait properly.
Your 'primer' Value is very big.
So, loop is taking very much time to reach at '600851475143' value.
Wait Sometime and it with show largest prime number
My flood fill method:
public void fillNeighbours(int x, int y) {
for(int i = -1; i < 2; i++) {
for(int j = -1; j < 2; j++) {
try {
visible[x+i][y+j] = true;
if(num[x+i][y+j] == 0) {
fillNeighbours(x+i, y+j);
}
} catch (ArrayIndexOutOfBoundsException ignored) {}
}
}
}
That catch (ArrayIndexOutOfBoundsException ignored) {} is there for avoiding x and/or y position from going outside of the array. Array size is 30 by 30. I'm making minesweeper like game. So you may know why I need this method and how it should work. If u don't know what minesweeper is then here is quick video about that game: Introduction to minesweeper
The code revisits fields which are set to visible already.
Try something like
if(!visible[x+i][y+j]){
visible[x+i][y+j] = true;
if(num[x+i][y+j] == 0) {
fillNeighbours(x+i, y+j);
}
}
It looks like you're recursively calling fillNeighbours without any break out clause (base case) so the calls fill the stack.
From
Wikistack
The tree laws of recursion are
A recursive algorithm must have a base case.
A recursive algorithm
must change its state and move toward the base case.
A recursive
algorithm must call itself, recursively.
Once fillNeighbours finds a cell and calls itself, the next loop will always call another when i and j are equal to zero. So it will never exit, and crash once the stack is full.
Aside from that, it will produce a very deep tree as it is not keeping track of which cells have been recursed, and will call fillNeighbours on the same cell multiple times.
I try to write a MinMax program in Java for connect-four game, but this program should also be applicable to other games. But, I encountered a problem, which I cannot pass for few days. The values for nodes are not set properly. I am sharing my piece of code which is responsible for generating a tree.
Maybe you will notice where I made a mistake.
If anyone could help me with this, I will be very happy.
public Node generateTree(Board board, int depth) {
Node rootNode = new Node(board);
generateSubtree(rootNode, depth);
minMax(rootNode, depth);
return rootNode;
}
private void generateSubtree(Node subRootNode, int depth) {
Board board = subRootNode.getBoard();
if (depth == 0) {
subRootNode.setValue(board.evaluateBoard());
return;
}
for (Move move : board.generateMoves()) {
Board tempBoard = board.makeMove(move);
Node tempNode = new Node(tempBoard);
subRootNode.addChild(tempNode);
generateSubtree(tempNode, depth - 1);
}
}
public void minMax(Node rootNode, int depth) {
maxMove(rootNode, depth);
}
public int maxMove(Node node, int depth) {
if (depth == 0) {
return node.getValue();
}
int bestValue = Integer.MIN_VALUE;
for (Node childNode : node.getChildren()) {
int tempValue = minMove(childNode, depth - 1);
childNode.setValue(tempValue);
if (tempValue > bestValue) {
bestValue = tempValue;
}
}
return bestValue;
}
public int minMove(Node node, int depth) {
if (depth == 0) {
return node.getValue();
}
int bestValue = Integer.MAX_VALUE;
for (Node childNode : node.getChildren()) {
int tempValue = maxMove(childNode, depth - 1);
childNode.setValue(tempValue);
if (tempValue < bestValue) {
bestValue = tempValue;
}
}
return bestValue;
}
Board class is the representation of the board state.
Move class hold the move to perform (integer [0-8] for tic-tac-toe, [0-6] for Connect Four).
Node class holds the Move and value how good given move is. Also, holds all its children.
In the code I use this method like this:
Node newNode = minmax.generateTree(board, depth, board.getPlayer());
Move newMove = new TicTacToeMove(board.getPlayer(), newNode.getBestMove().getMove(), depth);
board = board.makeMove(newMove);
And when it's obvious that given move is a losing move (or winning), I do not receive this move.
Alright, you did make a couple of mistakes. About 3-4, depending on how you count ;) Took me a bit of debugging to figure it all out, but I finally got an answer for you :D
Mistake #1: All your parents always get twins (that poor mother)
This is only the case with the code you uploaded, not the code in your question, so maybe we count it as half a mistake?
Since your trees aren't that big yet and it won't destroy your algorithm, this was the least important one anyway. Still, it's something to watch out for.
In your uploaded code, you do this in your generateSubtree method:
Node tempNode = new Node(tempBoard, move, subRootNode);
subRootNode.addChild(tempNode);
As that constructor already adds the child to the subRootNode, the second line always adds it a second time.
Mistake #2: That darn depth
If you haven't reached your desired depth yet, but the game is already decided, you completely ignore that. So in your provided example that won't work, if - for example - you look at making move 7 instead of 3 (which would be the 'right' move) and then the opponent does move 3, you don't count it as -10 points because you haven't reached your depth yet. It still won't get any children, so even in your minmax, it will never realize it's a screwed up way to go.
Which is why every move is 'possible' in this scenario and you just get the first one returned.
In the previous moves, there was luckily always a way to reach a losing move with your opponents third move (aka move #5), which is why those were called correctly.
Alright, so how do we fix it?
private void generateSubtree(Node subRootNode, int depth, int player) {
Board board = subRootNode.getBoard();
List<Move> moveList = board.generateMoves();
if (depth == 0 || moveList.isEmpty()) {
subRootNode.setValue(board.evaluateBoard(player));
return;
}
for (Move move : moveList) {
Board tempBoard = board.makeMove(move);
Node tempNode = new Node(tempBoard, move, subRootNode);
generateSubtree(tempNode, depth - 1, player);
}
}
Just get the move list beforehand and then look if it's empty (your generateMoves() method of the Board class (thank god you provided that by the way ;)) already checks if the game is over, so if it is, there won't be any moves generated. Perfect time to check the score).
Mistake #3: That darn depth again
Didn't we just go over this?
Sadly, your Min Max algorithm itself has the same problem. It will only even look at your values if you have reached the desired depth. You need to change that.
However, this is a bit more complicated, since you don't have a nice little method that already checks if the game is finished for you.
You could check to see if your value was set, but here's the problem: It might be set to 0 and you need to take that into account as well (so you can't just do if (node.getValue() != 0)).
I just set the initial value of each node to -1 instead and did a check against -1. It's not... you know... pretty. But it works.
public class Node {
private Board board;
private Move move;
private Node parent;
private List<Node> children = new ArrayList<Node>();;
private boolean isRootNode = false;
private int value = -1;
...
And this in the maxMove:
public int maxMove(Node node, int depth) {
if (depth == 0 || node.getValue() != -1) {
return node.getValue();
}
int bestValue = Integer.MIN_VALUE;
for (Node childNode : node.getChildren()) {
int tempValue = minMove(childNode, depth - 1);
childNode.setValue(tempValue);
if (tempValue > bestValue) {
bestValue = tempValue;
}
}
return bestValue;
}
It works the same for minMove of course.
Mistake #4: The player is screwing with you
Once I changed all that, it took me a moment with the debugger to realize why it still wouldn't work.
This last mistake was not in the code you provided in the question btw. Shame on you! ;)
Turns out it was this wonderful piece of code in your TicTacToeBoard class:
#Override
public int getPlayer() {
// TODO Auto-generated method stub
return 0;
}
And since you called
MinMax minmax = new MinMax();
Node newNode = minmax.generateTree(board, (Integer) spinner.getValue(), board.getPlayer());
in your makeMove method of TicTacToeMainWindow, you would always start out with the wrong player.
As you can probably guess yourself, you just need to change it to:
public int getPlayer() {
return this.player;
}
And it should do the trick.
Also:
Just a couple of things I'd like to remark at this point:
Clean up your imports! Your TicTacToe actually still imports your ConnectFour classes! And for no reason.
Your board is rotated and mirrored in your board array. WHY? You know how annoying that is to debug? I mean, I guess you probably do :D Also, if you're having problems with your code and you need to debug it's extremely helpful to overwrite your boards toString() method, because that will give you a very nice and easy way to look at your board in the debugger. You can even use it to rotate it again, so you see don't have to look at it lying on the side ;)
While we're at the subject of the board... this is just me but... I always tried clicking on the painted surface first and then had to remember: Oh yeah, there were buttons :D I mean... why not just put the images on the buttons or implement a MouseListener so you can actually just click on the painted surface?
When providing code and/or example images, please take out your test outputs. I'm talking about the Player 1 won!s of course ;)
Please learn what a complete, verifiable and minimal example is for the next time you ask a question on StackOverflow. The one in your question wasn't complete or verifiable and the one you provided on github was... well... not complete (the images were missing), but complete enough. It was also verifiable, but it was NOT minimal. You will get answers a LOT sooner if you follow the guidelines.
i want to add to my code an exception in the bottom part
if(legalNeighbor(map,i1,j1,i1,j2)==false&&i1!=i2&&j1!=j2){
return 0;
what i want to do is: if the "if" above is true, instead of doing "return 0" i will throw an exception that will check the code from this point
if (locationNeighbor(map,i1+1,j1,i1,j1)==2&&map2[i1+1][j1]==-1){ //down
map2[i1+1][j1]=counter;
return distance(map,i1+1,j1,i2,j2,map2,counter+1);
}
if (locationNeighbor(map,i1,j1+1,i1,j1)==3&&map2[i1][j1+1]==-1){
and then again if the "if" happens i will check the code from this area
if (locationNeighbor(map,i1,j1+1,i1,j1)==3&&map2[i1][j1+1]==-1){ //right
map2[i1][j1+1]=counter;
return distance(map,i1,j1+1,i2,j2,map2,counter+1);
}
and then last time i check
if (locationNeighbor(map,i1,j1-1,i1,j1)==4&&map2[i1][j1-1]==-1){ //left
map2[i1][j1-1]=counter;
return distance(map,i1,j1-1,i2,j2,map2,counter+1);
}
this is my code
public static int distance(int[][] map, int i1, int j1, int i2, int j2, int[][]map2, int counter) {
if(legalNeighbor(map,i1,j1,i2,j2)==true){ // if its 1 step before the end
map2[i2][j2]=counter; // put the last number
}
if (locationNeighbor(map,i1-1,j1,i1,j1)==1&&map2[i1-1][j1]==-1){ //up
map2[i1-1][j1]=counter;
return distance(map,i1-1,j1,i2,j2,map2,counter+1);
}
if (locationNeighbor(map,i1+1,j1,i1,j1)==2&&map2[i1+1][j1]==-1){ //down
map2[i1+1][j1]=counter;
return distance(map,i1+1,j1,i2,j2,map2,counter+1);
}
if (locationNeighbor(map,i1,j1+1,i1,j1)==3&&map2[i1][j1+1]==-1){ //right
map2[i1][j1+1]=counter;
return distance(map,i1,j1+1,i2,j2,map2,counter+1);
}
if (locationNeighbor(map,i1,j1-1,i1,j1)==4&&map2[i1][j1-1]==-1){ //left
map2[i1][j1-1]=counter;
return distance(map,i1,j1-1,i2,j2,map2,counter+1);
}
print(map2);
if(legalNeighbor(map,i1,j1,i1,j2)==false&&i1!=i2&&j1!=j2){
return 0;
}
else{
int x=map2[i2][j2];
return x;
}
}
is this even possible with exception?
Anything is possible, but it's probably not a good idea. Exceptions should be truly exceptional, not something that is likely to happen.
Not only that, but they are expensive in terms of performance. You should not be using exceptions to control flow logic.
Why isn't a true/false return from a method named isLegalNeighbor not sufficient?
You really have two methods here: One to calculate distance and another to determine a legal neighbor. Split them up. A method should do one thing well, and its name should make clear what that is.
One more bit: I find your code very hard to read. I would start thinking more about style and how to write more readable code if I were you.
Forget about the exception, it's probably not what you want here. First explain what your are trying to achieve. This is completely unobvious from your code.
It's strange that the first if-statement sets a value in map2 but does not return. This means the value might be overwritten by the code below. Is that intended?
I also think there's an error in the line:
if(legalNeighbor(map,i1,j1,i1,j2)==false&&i1!=i2&&j1!=j2){
The 4th argument i1 should be i2, right?
To make your code more readable you should introduce instance fields and methods with meaningful names. If your tutor is forcing you to use static methods and meaningless names like i1 etc you should fire him/her. :-D
Here's an idea of what I mean:
// I chose weird names because I really don't know what's in these arrays
// You should make it clear from the name what it represents
private int[][] fooBar;
private int[][] bazBop;
public int distance(int fromX, int fromY, int toX, int toY, int counter) {
if (legalNeighbor(fromX, fromY, toX, toY) == true) { // if its 1 step before the end
bazBop[toX][toY] = counter; // put the last number
}
int newX = fromX - 1;
int newY = fromY;
if (locationNeighbor(newX, newY, fromX, fromY) == 1 && bazBop[newX][newY] == -1) { //up
bazBop[newX][newY] = counter;
return distance(newX, newY, toX, toY, counter + 1);
}
// ...
}
I have looked everywhere for answers for fixing my code but after long hours spent trying to debug it I find myself hopelessly stuck. The problem is that my minimax function will not return the correct values for the best possible move, I even attempted to fix it by storing the best first moves (when depth = 0), but if the solution is not obvious, then the algorithm fails horribly. I also tried modifying the return values from the base cases in order to prioritize earlier wins, but this didn't solve the problem.
Currently I am testing the function on a TicTacToe board and the helper classes (Eg getMoves() or getWinner are working properly), I know my style is not the most efficient but I needed the code to be fairly explicit.
By adding a bunch of print statements I realized that under some circumstances my bestFinalMoves ArrayList was not modified, so this may be related to the issue. Another related problem is that unless the algorithm finds a direct win (in the next move), then instead of choosing a move that may lead to a future win or tie by blocking a square that leads to an immediate block, it just yields the space for the minimizing player to win.
For example on the board:
aBoard= new int[][] {
{0,1,0}, // 1 is MAX (AI), -1 is MIN (Human)
{-1,0,0},
{-1,0,0}
};
Yields the incorrect result of 2,0, where it is obvious that it should be 0,0, so that it blocks the win for the minimizing player, and the bestFinalMoves ArrayList is empty.
private result miniMaxEnd2(Board tmpGame, int depth){
String winner = tmpGame.whoWon();
ArrayList<Move> myMoves = tmpGame.getMoves();
if (winner == 'computer'){ //Base Cases
return new result(1000);
}else if (winner == 'human'){
return new result(-1000);
}
else if (winner == 'tie'){
return new result(0);
}
if (tmpGame.ComputerTurn) {//MAX
bestScore = -99999;
for (Move m : tmpGame.getMoves()){
Board newGame = new Board(tmpGame,!tmpGame.ComputerTurn, m);
result aScore = miniMaxEnd2(newGame, depth+1);
if (aScore.score > bestScore) {
bestScore = aScore.score;
bestMove = m;
if (depth == 0) {
bestFinalMoves.add(m);
}
}
}
return new result(bestScore, bestMove);
} else {//MIN
bestScore = 99999;
for (Move m : tmpGame.getMoves()) {
Board newGame = new Board(tmpGame,!tmpGame.ComputerTurn, m);
result aScore = miniMaxEnd2(newGame, depth + 1);
if (aScore.score < bestScore) {
bestScore = aScore.score;
bestMove = m;
}
}
return new result(bestScore,bestMove);
}
}
I know this was a long post, but I really appreciate your help. The full code can be accessed at https://github.com/serch037/UTC_Connect
The bestScore and bestMove variables must be declared as local variables inside the miniMaxEnd2 method for this logic to work properly.
Those variables' values are being replaced by the recursive call.