3

I'm using Java to solve the 8-Puzzle problem using DFS.

this is what I came up with:

    public static boolean found = false;

        public void solveDepthFirst(EightPuzzle currentState, int lastMove){

            if(currentState.goal()){
                System.out.println(currentState);
                found = true;//to stop DFS when a solution is found (even if not optimal)
                return;
            }

            for(int i=0;i<numMoves;++i){
                if(found) return;

                EightPuzzle e = currentState.move(i);//0 = up, 1 = down, 2 = left, 3= right

                if(!e.equals(currentState) && i != lastMove
                        && !visitedNodes.contains(e.toString())){
                    solveDepthFirst(e, i);  
                }           
                     if(!visitedNodes.contains(currentState.toString())){
                visitedNodes.add(currentState.toString());
                     }
            }
        }

!e.equals(currentState) checks if move is possible. (if currentState.move(i) is out of bounds move() returns the same state)

i != lastMove makes sure that if in your last move you moved right you don't move left now (since it doesn't make sens)

visitedNodes is a HashSet of visited Nodes.

This is running out of stack space. When I use -xss10m to increase the stack space from 128k to 10m the algorithm works fine. However I'm sure there are a ton of other optimizations that can be made.

any tips would be greatly appreciated.

Jonny
  • 2,787
  • 10
  • 40
  • 62

2 Answers2

1

I think you can speed up your search considerably by marking a state visited before proceeding with a recursive call. Other than that, there aren't too many optimizations for this puzzle: you simply need to try all possible moves.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
1

First of all you can make a stack instead of a recursive call. Add the lastMove to the EightPuzzle class.

This is what you get:

// Queue<EightPuzzle> queue = new PriorityQueue<EightPuzzle>();
Stack<EightPuzzle> stack = new Stack<EightPuzzle>();

public void solveDepthFirst() {

    while (true) {
        EightPuzzle currentState = stack.pop(); // queue.poll();
        if (currentState.goal()) {
            System.out.println(currentState);
            found = true;// to stop DFS when a solution is found (even if
                            // not
                            // optimal)
            return;
        }

        for (int i = 0; i < 4; ++i) {
            if (found)
                return;

            EightPuzzle e = currentState.move(i);// 0 = up, 1 = down, 2 =
                                                    // left,
                                                    // 3= right

            if (!e.equals(currentState) && i != currentState.getLastMove()
                    && !visitedNodes.contains(e)) {
                stack.push(e); // queue.add(e);
            }
            if (!visitedNodes.contains(currentState.toString())) {
                visitedNodes.add(currentState);
            }
        }
    }
}

The performance drops significantly when you use recursive calls instead of an iterative design.

After that you can further optimize (but it will not be a true DFS) by using a PriorityQueue. The heuristic to use can be the Manhattan distance. This way the solution to be searched for the first is the closest to the target. It is more efficient but not strict DFS.

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/PriorityQueue.html

Alin Stoian
  • 1,122
  • 1
  • 12
  • 24