Question

Ok, so I have a 3 x 3 jig saw puzzle game that I am writing and I am stuck on the solution method.

public Piece[][] solve(int r, int c) {
    if (isSolved())
        return board;
    board[r][c] = null;
    for (Piece p : pieces) {
        if (tryInsert(p, r, c)) {
            pieces.remove(p);
            break;
        }
    }
    if (getPieceAt(r, c) != null)
        return solve(nextLoc(r, c).x, nextLoc(r, c).y);
    else {
        pieces.add(getPieceAt(prevLoc(r, c).x, prevLoc(r, c).y));
        return solve(prevLoc(r, c).x, prevLoc(r, c).y);
    }
}

I know I haven't provided much info on the puzzle, but my algorithm should work regardless of the specifics. I've tested all helper methods, pieces is a List of all the unused Pieces, tryInsert attempts to insert the piece in all possible orientations, and if the piece can be inserted, it will be. Unfortunately, when I test it, I get StackOverflow Error.

Was it helpful?

Solution 2

I think you need to structure your recursion differently. I'm also not sure adding and removing pieces from different places of the list is safe; much as I'd rather avoid allocation in the recursion it might be safest to create a list copy, or scan the board so far for instances of the same piece to avoid re-use.

public Piece[][] solve(int r, int c, List<Piece> piecesLeft) {
    // Note that this check is equivalent to
    // 'have r and c gone past the last square on the board?'
    // or 'are there no pieces left?'
    if (isSolved())
        return board;

    // Try each remaining piece in this square
    for (Piece p : piecesLeft) {
        // in each rotation
        for(int orientation = 0; orientation < 4; ++orientation) {
            if (tryInsert(p, r, c, orientation)) {
                // It fits: recurse to try the next square
                // Create the new list of pieces left
                List<Piece> piecesLeft2 = new ArrayList<Piece>(piecesLeft);
                piecesLeft2.remove(p);
                // (can stop here and return success if piecesLeft2 is empty)
                // Find the next point
                Point next = nextLoc(r, c);
                // (could also stop here if this is past end of board)

                // Recurse to try next square
                Piece[][] solution = solve(next.x, next.y, piecesLeft2);
                if (solution != null) {
                    // This sequence worked - success!
                    return solution;
                }
            }
        }
    }

    // no solution with this piece
    return null;
}

OTHER TIPS

Your DFS-style solution algorithm never re-adds Piece objects to the pieces variable. This is not sound, and can easily lead to infinite recursion.

Suppose, for example, that you have a simple 2-piece puzzle, a 2x1 grid, where the only valid arrangement of pieces is [2, 1]. This is what your algorithm does:

1) Put piece 1 in slot 1
2) It fits! Remove this piece, pieces now = {2}. Solve on nextLoc()
3) Now try to fit piece 2 in slot 2... doesn't work
4) Solve on prevLoc()
5) Put piece 2 in slot 1
6) It fits! Remove this piece, pieces is now empty. Solve on nextLoc()
7) No pieces to try, so we fail. Solve on prevLoc()
8) No pieces to try, so we fail. Solve on prevLoc()
9) No pieces to try, so we fail. Solve on prevLoc()
Repeat ad infinitum...

As commenters have mentioned, though, this may only be part of the issue. A lot of critical code is missing from your post, and their may be errors there as well.

StackOverflowError with recursive functions means that you're either lacking a valid recursion stop condition or you're trying to solve too big problem and should try an iterated algorithm instead. Puzzle containing 9 pieces isn't too big problem so the first thing must be the case.

The condition for ending recursion is board completion. You're only trying to insert a piece in the for loop, so the problem is probably either that the tryInsert() method doesn't insert the piece or it doesn't get invoked. As you're sure that this method works fine, I'd suggest removing break; from

if (p.equals(prev[r][c])) 
{
    System.out.println("Hello");
    break;
}

because it's the only thing that may prevent the piece from being inserted. I'm still unsure if I understand the prev role though.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top