8 and 15 Puzzles

The (N2 − 1)-puzzle is a collection of N2 − 1 movable tiles number 1 through N2 − 1 together with one blank arranged in an N × N square. Figure 1 shows an eight-puzzle.

Figure 1. An eight-puzzle.

The only valid moves are to move a tile which is immediately adjacent to the blank into the location of the blank. An example of such a move is to move tile 6 into the blank as is shown in Figure 2. Given any arrangement of the tiles, there are between two and four valid moves.

Figure 2. A valid move of the eight-puzzle.

The objective is to take a permutation of the tiles and the blank; and, by making a sequence of valid moves, to transform the puzzle into the original shown in Figure 1. Figure 3 shows a permutation with a single move which places 6 into the correct location. Given a permutation, a solution is a sequence of moves which transforms the permutation into the solution.

Figure 3. A move in a permutation of the eight-puzzle.

Please note, only half of all permutations of the tiles and the blank have solutions.

The algorithm presented uses an A* search to find the solution to the (N2 − 1)-puzzle: arranging the numbers in order with a blank in the last location.

The data structure used to efficiently solve the A* algorithm is a modified heap which is able to allow the user to update the priority in O(ln(n)) time: a index to each entry is stored in a hash table and when the priority is updated, the index allows the heap to, if necessary, percolate the object up.

Without the hash table, objects in the heap could not be easily accessed and therefore the run time would be slowed significantly.

The design, shown in Figure 4, is as follows:

  • Each object is placed into the hash table corresponding to its bin, here shown using a chained hash table.
  • The nodes within the chains store not only the object, but also an index into the heap.
  • The heap only stores pointers back to the nodes in the hash table.

Figure 4. An updatable priority queue.

For example, Black hashes to 4 and has the highest priority, therefore it is in the 1st location of the heap and the index 1 is stored in the node. Similarly, Orange hashes to 7 and has priority lower than Brown. Being stored in index location 4, the node in the hash table stores 4.

There are three distances which can be used to measure the distance between the state of a puzzle and the solution:

  • The discrete distance (0 if equal and 1 otherwise),
  • The Hamming distance (the number of tiles out of place), and
  • The Manhattan distance (the sum of the minimum number of steps to move each tile (assuming no other tiles) in its correct location),

For example, Figure 5 shows the solution to the eight-puzzle and a permutation of the tiles.

Figure 5. The solution to the eight-puzzle and a permutation of the tiles.

The discrete distances between the permutation and the solution is 1 (they are different). Using the Hamming distance, the distance is 8—only one tile is in the correct location. This is shown on the left of Figure 6. Using the Manhattan distance, the distance is the sum of the moves shown in Figure 6: 2 + 0 + 4 + 2 + 1 + 1 + 2 + 3 + 1 = 16.

Figure 6. The Hamming and Manhattan distances of the permutation from Figure 5.

The formula for the average Manhattan distance of a random permutation is given by the formula 2/3(N − 1)(N2 + N − 3/2), which, for this case is 14.

The Updatable_heap data structure makes use of a heap as an array using the complete binary tree representation and a chained hash table. The nodes in the hash table are reasonably independent of the problem being solved, requiring only that the class have a member function with the signature int lower_bound() const which can be called to calculate the lower bound on the distance from the object to the solution.

The class also tracks the size and the maximum size of the heap (the maximum number of objects in the priority queue).

To demonstrate the algorithm and the solution, Figure 7 shows one puzzle for which the solution was found using the discrete, Hamming, and Manhattan distances to guide the A* search.

Figure 7. A permutation of the eight-puzzle.

Dijkstra's algorithm found the minimum solution of 24 moves after having considered 139466 possible solutions (visited 139466 vertices) during the search and the maximum size of the heap was 24154.

Using the Hamming distance, the number of puzzles considered dropped to 127643. This small reduction is almost certainly due to the fact that the Hamming distance is only really useful in the last stages of finding the solution. The maximum heap size was still 22899.

Using the Manhattan distance, only 2751 vertices were visited and the maximum heap size was 1501.

Solving fifteen-puzzles is much more difficult: the puzzle in Figure 8 has a solution of 50 moves and required that 84702 vertices (different permutations of the puzzle) be visited and the maximum heap size was 72340.

Figure 8. A permutation of the fifteen-puzzle.