views:

163

answers:

4

I'm writing a program which solves a 24-puzzle (5x5 grid) using two heuristic. The first uses how many blocks the incorrect place and the second uses the Manhattan distance between the blocks current place and desired place.

I have different functions in the program which use each heuristic with an A* and a greedy search and compares the results (so 4 different parts in total).

I'm curious whether my program is wrong or whether it's a limitation of the puzzle. The puzzle is generated randomly with pieces being moved around a few times and most of the time (~70%) a solution is found with most searches, but sometimes they fail.

I can understand why greedy would fail, as it's not complete, but seeing as A* is complete this leads me to believe that there's an error in my code.

So could someone please tell me whether this is an error in my thinking or a limitation of the puzzle? Sorry if this is badly worded, I'll rephrase if necessary.

Thanks

EDIT:

So I"m fairly sure it's something I'm doing wrong. Here's a step-by-step list of how I'm doing the searches, is anything wrong here?

  • Create a new list for the fringe, sorted by whichever heuristic is being used
  • Create a set to store visited nodes
  • Add the initial state of the puzzle to the fringe
  • while the fringe isn't empty..
    • pop the first element from the fringe
    • if the node has been visited before, skip it
    • if node is the goal, return it
    • add the node to our visited set
    • expand the node and add all descendants back to the fringe
+2  A: 

If you mean that sliding puzzle: This is solvable if you exchange two pieces from a working solution - so if you don't find a solution this doesn't tell anything about the correctness of your algorithm.

It's just your seed is flawed.

Edit: If you start with the solution and make (random) legal moves, then a correct algorithm would find a solution (as reversing the order is a solution).

Eiko
A: 

I'm going to assume your code is correct, and you implemented all the algorithms and heuristics correctly.

This leaves us with the "generated randomly" part of your puzzle initialization. Are you sure you are generating correct states of the puzzle? If you generate an illegal state, obviously there will be no solution.

Yuval A
The randomness involves 10 different random moves of the blank tile, so I believe this would always generate a solvable puzzle in < 10 moves. I've updated my post with some more theory about how I do things, could you take another look?
Gary
+1  A: 

It is not completely clear who invented it, but Sam Loyd popularized the 14-15 puzzle, during the late 19th Century, which is the 4x4 version of your 5x5.

From the Wikipedia article, a parity argument proved that half of the possible configurations are unsolvable. You are probably running into something similar when your search fails.

John R. Strohm
Thanks for your reply, but as per the comment I left on "Yuval A" - the way I'm shuffling the tiles should always result in a solvable board seeing as I just randomly move the blank square around.
Gary
As Eiko answered - if you start at non-solvable configuration - you will end after N moves in a non-solvable configuration again.
0x69
A: 

While the steps you have listed seem a little incomplete, you have listed enough to ensure that your A* will reach a solution if there is one (albeit not optimal as long as you are just simply skipping nodes).

It sounds like either your puzzle generation is flawed or your algorithm isn't implemented correctly. To easily verify your puzzle generation, store the steps used to generate the puzzle, and run it in reverse and check if the result is a solution state before allowing the puzzle to be sent to the search routines. If you ever generate an invalid puzzle, dump the puzzle, and expected steps and see where the problem is. If the puzzle passes and the algorithm fails, you have at least narrowed down where the problem is.

If it turns out to be your algorithm, post a more detailed explanation of the steps you have actually implemented (not just how A* works, we all know that), like for instance when you run the evaluation function, and where you resort the list that acts as your queue. That will make it easier to determine a problem within your implementation.

NickLarsen