0

I'm doing an 8 piece puzzle game in java, and the assignment commands that i do a DFS to find the solution, starting at a random state.

i have a Node class, each Node object knows what state it is in using int[][] and what it's parent node is. Also it knows what direction it can move in (left, right, up, down)

     goal state:     start state (random):
     [0 1 2]         [1 8 0]
     [3 4 5]         [3 7 4]
     [6 7 8]         [2 5 6]

starting with a single node, the start node, my program creates a node for all of its possible children. It checks to see what available directions the blank square can move in, it checks that it is not going back to a state already belonging to another node. So in the example case above the start node would expand as follows:

          [1 8 0]
      A)  [3 7 4]
          [2 5 6]
         /       \
    [1 0 8]     [1 8 4]
B)  [3 7 4]     [3 7 0]  C)
    [2 5 6]     [2 5 6]
   /   /       /       \
...  ...  [1 8 4]     [1 8 4]
      D)  [3 0 7]     [3 7 6]  E)
          [2 5 6]     [2 5 0]
         /  /  /       /    \
     ... ... ... [1 8 4]     NO
             F)  [3 7 6]     CHILD
                 [2 0 5]
                /       \
             ...         ...

The way i handle this is that i explore the first node and push all of it's children to a stack, this happens in a recursive method which loops expanding each node until the goal state is reached or the cutoff is reached (a number i provide to avoid looping forever)

stack = [A]
pop()
stack = []
A -> B
push(B)
stack = [B]
A -> C
push(C)
stack = [C|B]
A -> NO MORE CHILDREN
pop()
stack = [B]
C -> D
push(D)
stack = [D|B]
C -> E
push(E)
stack = [E|D|B|]
C -> NO MORE CHILDREN
pop()
stack = [D|B|]
E -> F
push(F)
stack = [F|D|B]
E -> NO MORE CHILDREN
pup()
stack = [D|B]

The code works fine as long as my cutoff is not too high, if it is than i get the error: java.lang.StackOverflowError

What am i doing wrong?

public void expand(Node parent, int cutoff){
  cutoff--;

  int[][] currentState = parent.getState();

  if(Arrays.deepEquals(currentState, goalState)){
    found = true;
    goalNode = parent;
  }


  if(cutoff>0 && !found){
    if(parent.up){
      Node child = createUPnode();
      child.setParent(parent);
      stack.push(child);
      parent.up = false;
      expand(parent, cutoff)
    }
    else if(parent.right){
      Node child = createRIGHTnode();
      child.setParent(parent);
      stack.push(child);
      parent.right = false;
      expand(parent, cutoff)
    }
    else if(parent.down){
      Node child = createDOWNnode();
      child.setParent(parent);
      stack.push(child);
      parent.down = false;
      expand(parent, cutoff)
    }
    else if(parent.left){
      Node child = createLEFTnode();
      child.setParent(parent);
      stack.push(child);
      parent.left = false;
      expand(parent, cutoff)
    }
    else{
      Node nextNode = stack.pop();
      expand(nextNode, cutoff);
    }
  }
  else{
    System.out.println("CUTOFF REACHED");
  }
}
dsolimano
  • 8,870
  • 3
  • 48
  • 63
Alex
  • 89
  • 1
  • 7

2 Answers2

1

The post 'What is a stack overflow error?' contains a lot of information on what can cause a StackOverflowError in Java. The most common cause for this kind of error is a bad recursive call (causing an infinite loop). It seems you have already guarded against this by introducing a cutoff value for your recursive calls of expand().

But even with this delimiter the stack size may be to small to handle the recursive calls. I belief you have two options:

1) Use a 'small enough' value for your cutoff (this actually works, as you already wrote) but this limits your search depth.

2) Increase the stack size of the JVM. You can do this by adding the flag -Xss1024k as a VM argument. For more information on how to increase the stack size, read Java stack overflow error - how to increase the stack size in Eclipse?

Community
  • 1
  • 1
phuibers
  • 1,239
  • 11
  • 11
  • thank you, i increased the stack size and that got rid of the stackoverflow error, however i feel like it is taking to long to solve the puzzle, atleast the other 8 piece puzzles i see online can solve themselves withing 10 seconds, is there something wrong with my algorithm? or is it natural for DFS to take so long? – Alex May 03 '11 at 11:30
  • Perhaps the solutions you see online are using some heuristic steps. For instance, I dont think that you have to consider expanding the node corresponding to the Up action when the current node is reached by performing the Down action. Because you would just be back at the parent node, right? This is a simple trick which already eliminates 25% (rough, maybe wrong estimate) of the expansions necessary to find the solution. And I am sure you can think of other such rules to improve the searching even further! – phuibers May 03 '11 at 11:41
  • There is a third option to eliminate this problem. Make the stack explicit and eliminate the recursion. Instead of recursive calls, use a stack. As an example, read the examples on iterative tree recursion described at Wikipedia (http://en.wikipedia.org/wiki/Tree_traversal#Iterative_Traversal). I'd consider eliminating the stack the best option. It means you can cope with arbitrarily complicated graphs subject only to the memory available rather than the size of the stack. – Jeff Foster May 03 '11 at 12:28
  • yes i have that rule in place already, and still it sometimes takes up to an hour to go though the entire thing, so i know i must be doing something wrong. Math is not my strongest subject but i think that the maximum number of possible states is 9! which is about 370,000 but it sometimes goes beyond that so i think theres a mistake when checking if the state has already been visited. i guess ill just have to put it aside for a bit and come back to it later with rested eyes... Thanks again for all the help... i'll update once i figure it out.. – Alex May 04 '11 at 02:21
0

It's simple to implement DFS or BFS using a Dequeue, without any recursion, just loop, reading from the head of the dequeue and generating new solutions to the tail (BFS). To use DSF add the children to the head.

I think you should use a Breadth-first search to find the shortest possible solution, right?

If you are certain about the depth first search (which makes little sense, i think) you can do away with the recursion and just use the dequeue you have created (not the call stack). The recusive calls are not needed. Just loop: start each loop by popping a node, generate its childs and push them on the dequeue's head, then start over. If the dequeue is empty there is no solution...

In most cases it's best to check if a generated solution already have been generated before adding them to the end of the queue, to reduce memory usage. Using plain Collections, a HashSet is probably quicker than a TreeSet- Remember to implement Node.equals() and Node.hashCode() properly.

I suppose that an ordinary LinkedList would be the most efficient Dequeue in this case - but why not test yourself.

KarlP
  • 5,149
  • 2
  • 28
  • 41
  • The OP stated in the post: "the assignment commands that i do a DFS to find the solution" – Ray May 03 '11 at 17:04
  • Well apparently, I can't stop wasting my time. The order in the example is "A,B,C,D" etc, which is BFS. – KarlP Mar 06 '14 at 21:38