The Eight Puzzle By: Robert Goldstein Prepared for CS661- Artificial Intelligence I Professor Tony Grgas Table of Contents 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. Test System____________________________________________________ Introduction____________________________________________________ Breadth First Search (BFS) ________________________________________ A* Search _____________________________________________________ 3.1 A* Search using the Manhattan Distance Heuristic__________________ 3.2 A* Search using the Tiles Out of Place Heuristic___________________ 3.3 A* Search using Greedy Search_________________________________ Language of Choice for Project_____________________________________ How is the Eight Puzzle represented in the program?_____________________ How was the BFS was implemented?_________________________________ How was the A* Search implemented?________________________________ 7.1 How was the Manhattan Distance Heuristic implemented?____________ 7.2 How was the Tiles Out of Place Heuristic implemented?_____________ 7.3 How was the A* Search using Greedy Search implemented?___________ How Random is Randomize Tiles?___________________________________ Results from Searches_____________________________________________ Known Bugs____________________________________________________ 10.1 Display of Breadth First Search (BFS)____________________________ 10.2 Depth Problem With Respect To Breath First Search (BFS)___________ 10.3 Depth Problem With Respect To A* Searches______________________ 10.4 Applet Usage Problem With Respect To Microsoft Internet Explorer__ 10.5 Applet Usage Problem With Respect To Netscape Navigator________ Page 2 of 14 3 3 4 5 5 6 6 6 7 7 8 11 12 12 12 13 13 13 13 13 14 14 0. Test System I used my home PC in order to do the testing for the project. The specifications of interest for it are: Pentium 150 MHz 64 Megabytes Ram 10.4 Gig HDD 4 Megabytes VRAM Video card 1. Introduction The Eight puzzle is the largest puzzle of its type that can be completely solved. It is simple, and yet obeys a combinatorially large problem space of 9!/2 states. The N*N extension of the 8puzzle is NP-hard. It is a game in which there are eight 1 x 1 tiles arranged on a 3 x 3 square so that there is one 1 x 1 uncovered area on the square. The tiles are labeled 1 through 8, and initially they are arranged in some random order. A tile with a face adjacent to the uncovered area can be transferred to the uncovered area in a single move. The goal of the puzzle is to place them in order, as in +---+---+---+ | 1 | 2 | 3 | +---+---+---+ | 4 | 5 | 6 | +---+---+---+ | 7 | 8 | | +---+---+---+ Figure 1. Goal State of Eight Puzzle Therefore, the following formulation makes up the Eight-Puzzle problem: States: a state description specifies the location of each of the eight tiles in one of the nine squares. For efficiency, it is useful to include the location of the blank. Operators: blank moves left, right, up, or down. Goal test: state matches the goal configuration (as seen above in Figure 1). Path cost: each step costs 1, so the path cost is just the length of the path. For this problem, I will demonstration how to solve the eight puzzle using three methods: (1) Breadth First Search (BFS), (2) A* Search using the Manhattan Distance heuristic, and (3) A* Search using the number of tiles out of place heuristic. Page 3 of 14 2. Breadth First Search (BFS) Breadth-first search (BFS) is one of the simpler search algorithms. In method, the root node of the search tree is expanded first, then all the nodes generated by the root node are expanded next, and then their successors until it reaches a terminating/goal state of the search. In most cases, all the nodes at depth d in the search tree are expanded before the nodes at depth d+1. This method is very systematic because it considers all paths at one level before moving onto future levels of the search tree (looks at all nodes at level 1 before going onto level 2). BFS is a complete algorithm because if there is a solution, it will find it. It is also generates an optimal solution provided the path cost is a non-decreasing function of the depth of the node (not using a path cost function determine where to expand next). This method has some drawbacks to it. This is because a result of the time and memory factor. As the search algorithm navigates down the search tree the amount of nodes it needs to evaluate increments by a factor of bd, where b is the branching factor and d is the depth the search is currently at. Now suppose that the solution for the problem has a path length of d. The maximum number of node which need to be expanded before finding the solution would be equivalent to: # Nodes Expanded 1 b b 2 b 3 ... b d Figure 2. BFS Node Expansion example from d = 0 to d = 2 This is the maximum number, but the solution could be found at any point on level d, therefore in the best case, this number would be smaller. As a result, the time and memory complexity are both a ratio of O(bd). Page 4 of 14 Figure 3. BFS depth requirements on time and memory 3. A* Search The A* Search is a part of a class of algorithms called Best-First Search. This class of searching uses an evaluation function in order to determine the desirability (or lack thereof) of expanding a node. The name of this class of search algorithms is sort of misleading because if the search was able to directly pick the best node first, it would be a straight marsh to the Goal State instead of a search. However, what actually happens is that it chooses the node that appears to be the best node according to the evaluation function. The A* search makes its determination via the summation of two sub-functions in order to produce the evaluation function. There sub-functions provide the total path cost so far, g(n), and an admissible heuristic1, h(n), which is used to minimize the estimated path cost to the goal state. Therefore, this search method is optimal and complete. This means that it will in the end, always find a solution and it will be the best one. 3.1 A* Search using the Manhattan Distance Heuristic Using the Manhattan distance heuristic with the A* search is good algorithm use if you would like to keep the keep the search from retreating in depth too much. This algorithm uses the sum of the distances of the tiles from their goal positions. Because tile cannot move along diagonals, the distance counted will be the sum of the horizontal and vertical distances. This heuristic is admissible, because any move can only move one tile one step closer to the goal. Below is the result of this heuristic when tiles 1 to 8 are in the following Start State: 1 An admissible heuristic never overestimates the cost to reach the Goal State. This optimism of this transfers through to the f function as well. If h is admissible, f(n) never overestimates the actual cost of the best solution through n. Page 5 of 14 +---+---+---+ | 5 | 4 | | +---+---+---+ | 6 | 1 | 8 | +---+---+---+ | 7 | 3 | 2 | +---+---+---+ Manhattan Distance = 2 + 3 + 3 + 2 + 4 + 2 + 0 + 2 = 18 Figure 4. Manhattan Distance Heuristic 3.2 A* Search using the Tiles Out of Place Heuristic The Tiles Out of Place heuristic is a fine algorithm to use with A* search. The heuristic involves summing the number of tiles that are not in their correct location for the Goal State. This heuristic is admissible, because it is clear that any tile that is out of place must be moved at least once. Below is the result of this heuristic when tiles 1 to 8 are in the following Start State: +---+---+---+ | 5 | 4 | | +---+---+---+ | 6 | 1 | 8 | +---+---+---+ | 7 | 3 | 2 | +---+---+---+ Tiles Out of Place = 1 + 1 + 1 + 1 + 1 + 0 + 1 + 1 = 7 Figure 5. Tiles Out of Place Heuristic 3.3 A* Search using Greedy Search The Greedy Search is one of the simplest algorithms to use. This is because there is no additional heuristic to aid you besides the total path cost so far, g(n). As a result, many nodes that should not be expanded are and additional time is needed in order to find the correct solution. 4. Language of Choice for Project The language that I picked for this project was Java. This is because this project is a demonstration of how to solve the Eight Puzzle with different algorithms. JAVA is a very good language for this purpose, especially with its ability to quickly create graphics and its delightful similarity to the common Object Oriented Language (OOP), C++. Consequently, this project will displayable from a Web page or via Java’s Appletviewer, i.e. it is platform-independent. Page 6 of 14 Figure 6. Main Screen of Project as seen through Java’s Appletviewer 5. How is the Eight Puzzle represented in the program? The Eight Puzzle is represented inside of the program as a one-dimensional array of integers, starting from 0 to the size of the dimensions of the board –1. The dimensions are specified within the HTML file EightPuzzle.html via the parameters x and y, whereby the dimensions of the board are equal to x*y. The blank location is specified by another parameter in the previously mentioned HTML file and is labeled BLANK. It has a number from 0 to the size of the dimensions of the board – 1. Each value inside of the array initially corresponds to the array index, i.e. puzzle[0] = 0. The initial representation is Goal State. Each initial value of the array 0, 1, 2… also corresponds to a piece of an image specified in the previously mentioned HTML file as parameter IMAGE. 6. How was the BFS was implemented? Breadth First Search (BFS) was implemented using a queue. The queue stores its values in a linked list, therefore the size is able to increase or decrease with copying all currently stored data into a larger array. A queue is a simple way to accomplish BFS because it needs to go down and across the search tree in order to find a solution. By using a queue, the program appends all valid child nodes onto the queue for later checking. The program is going through every combination and therefore it does not know the order of how it reached the goal, i.e. order of moves made. Below are the procedures used to accomplish BFS. Page 7 of 14 Let cCount = the number of nodes that possibly at current depth Let fCount = the number of nodes that possibly at current depth + 1, i.e. future Let nodes = the number of nodes evaluated Let depth = the expected depth for finding the current depth looking at need to be checked need to be checked depth solution, i.e. the 1. Remove a node state from queue. 2. If cCount = 0, then Increment depth by 1. Set cCount = fCount and fCount = 0. 3. If all tiles are in correct location, add 1 to nodes and Done! Otherwise continue to step 3. 4. Create all valid future nodes for that state and append to queue. Increment fCount by 1 for valid future nodes. 5. Reduce cCount by 1. 6. Increment nodes by 1. 7. Go to step 1. Figure 7. Procedures for Implemented BFS 7. How was the A* Search implemented? The A* Search algorithm, described generally in section 3, uses two heuristics, g(n) and h(n), to determine which path to continue reviewing of the open leaves (nodes) on the Search Tree. For the Eight Puzzle problem, the total path cost so far, g(n), is an increment of 1 tile per move. Each of admissible heuristics will be described in the following subsections. From reading section 3, it can be seen that the A* Search is a bit complex as compared to the BFS implementation described in section 6. This is because you need to keep track of all of the open nodes on the Search Tree while keeping track of the total cost from the heuristics for each open node. For the purposes of this project, hash-tables and vectors were used. The vectors and hash-tables were used to keep track of the different nodes on the search tree that need to be examined throughout the search to see if they are better than the current best path so far. Below are the procedures used to accomplish the A* Search. Page 8 of 14 Let nodes = A vector that contains all open children nodes from the best node sorted by least total cost Let best = The first node in nodes vector. The node under consideration for future expansion. Let bestTotal = the best total of all nodes evaluated. Let open = A hash-table that contains all open nodes on the Search Tree. It is used to make sure that there is no duplicates saved and only keeping the best around. Let closed = A hash-table that contains the nodes that have already been expanded on the Search Tree. It is used insuring that the correct path is not over looked. Let theNode = A temporary variable that holds the node under examination. Let children = A vector that contains children of best, temporarily, until added to the nodes vector. 1. Set best = the first node in nodes vector. 2. If best’s heuristic total != bestTotal, then set bestTotal = best’s heuristic total 3. If best is the Goal State, then Done! Recurse through best back to the Start State and store all values in Vector (path through the Search Tree to reach the Goal State), so it can be shown graphically to user by manipulating the tiles. 4. Clean up past children from last run. 5. Generate best’s children. 6. Produce best’s children’s g(n), 1 + best’s cost. 7. Increment expanded depth by 1. 8. Examine of best’s children individually using the following steps: a. Obtain next child state, and clear out closedNode (temp var), openNode (temp var), theNode. b. If the child state is not in the closed table (store in closedNode), then try to obtain it from the open table (store in openNode). c. If openNode isn’t “null”, then theNode = openNode, otherwise theNode = closedNode. d. Determine whether theNode isn’t “null”. i. If theNode isn’t “null” 1. Check to see if theNode’s costs are > the best’s children’s costs. If they are: a. If closedNode isn’t “null” i. Put theNode into the open table with the child state as the key. ii. Remove the value in the closed table with the child state as the key. iii. Set theNode’s costs = the child’s costs iv. Set theNode’s total = the theNode’s new cost + theNode’s distance. v. Set theNode’s parent to the best node. vi. Add theNode to the children vector. Page 9 of 14 b. If closedNode is “null” i. Set theNode = a new node for the child state, with the parent as the best node, costs as the child’s costs, and the distance as theNode’s distance. ii. Put theNode into the open table with the child state as the key. iii. Add theNode to the children vector. ii. If theNode is “null” 1. Get the estimate for the child state. 2. Create a new node for the child state, with the parent as the best node, costs as the child’s costs, and the distance as the estimate. 3. Put the new node inter the open table with the child state as the key. 4. Increment the evaluated nodes 5. Add the new node to the children vector. e. Loop until all of best’s children have been examined. 9. Remove from the open table the value where the best node’s state is the key. 10. Add to the closed table the best node’s state using the best node as a key. 11. Remove the first element in nodes vector. 12. Add children nodes to nodes vector in order of lowest cost 13. Go to step 1, until there are no more nodes in the nodes vector. 14. Return “null”, signifying a failure, i.e. no solution found. Figure 8. Procedures for Implemented A* Search Page 10 of 14 7.1 How was the Manhattan Distance Heuristic implemented? The Manhattan Distance Heuristic described in section 3.1, has been implemented via a nested for loop where the inner loop performed the calculations to generated the final result of the heuristic. The code fragment for this heuristic is: int “heuristic result” = 0; int “index” = -1; for(int sy = 0; sy < “vertical dimension of puzzle”; sy++) { for(int sx = 0; sx < “horizontal dimension of puzzle”; sx++) { “increment index by 1”; int “tile value” = “sequence to check”[“index”]; if (“tile value” == “blank location in puzzle”) { // don’t include in heuristic result continue; } int “horizontal spaces offby” = “tile value” % “horizontal dimension of puzzle”; int “vertical spaces offby” = “tile value” / “horizontal dimension of puzzle”; “heuristic result” += Math.abs(“vertical spaces offby” - sy) + Math.abs(“horizontal spaces offby” - sx); } } return “heuristic result”; Figure 9. Pseudo code fragment for Manhattan Distance Heuristic Page 11 of 14 7.2 How was the Tiles Out of Place Heuristic implemented? The Tiles Out of Place Heuristic, talked about in section 3.2, is implemented via a for loop that checks each element in the array to determine whether or not the number in each element is in the correct position. Each time the number is incorrect the heuristic result is incremented by one, starting at zero. The code fragment for this heuristic is: for(int offby=0; offby < “dimensions of puzzle”; offby++) { if( “sequence to check”[offby] != offby ) { “increment heuristic result by one”; } } return “heuristic result”; Figure 10. Pseudo code fragment for Tiles Out of Place Heuristic 7.3 How was the A* Search using Greedy Search implemented? As state previously in section 3.3, the Greedy Search only uses the total path cost so far, g(n). Therefore in the Greedy Search implementation, zero is added to g(n) in order to protect the operational procedures of the A* Search algorithm stated above, in section 7. 8. How Random is Randomize Tiles? Randomize Tiles is very random. While testing it out, there were times using the A* searches to solve the Eight Puzzle that the depth reached and was in excess of 23 and on average the solution depth was about 27. Therefore, it is strongly urged until a less random tile option is added to the program, please manually move the tiles around via mouse clicks in the interface to achieve a less random board to solve. Page 12 of 14 9. Results from Searches The following table was produced after running tests on the program using the following eleven moves on the tiles from the Goal State: Right, Up, Right, Up, Right, Down, Left, Up, Right, Right, Up. Moves from Breadth First Search Manhattan Distance Tiles Out of Place Greedy Search Goal State Depth Nodes Depth Nodes Depth Nodes Depth Nodes 1 1 3 1 4 1 4 1 7 3 3 20 3 9 3 9 3 23 5 5 132 5 12 5 12 5 71 7 7 1412 7 17 7 21 7 256 9 9 8580 9 21 9 40 9 763 11 11 10278 11 23 11 76 11 2013 Figure 11. Results from Eight Puzzle Program Search Algorithms 10. Known Bugs 10.1 Display of Breadth First Search (BFS) The problem with the BFS display is that the search doesn’t graphically display each movement of the tiles from the Start State to the Goal State after completing the search, but rather redisplays the Goal State to the user. This is a result of not attaching the parent states to each child state throughout the search before putting the nodes on the queue to be later examined, something I did not forget to do in the A* searches. This bug will be corrected in the next version of the program. 10.2 Depth Problem With Respect To Breath First Search (BFS) While doing experimentation on the BFS algorithm in the program, I noticed that a depth greater 13 was beyond a reasonable amount of time to wait for an answer2. This is because the Eight Puzzle has on average a branching factor of around 2.7 3. Therefore at depth 14, the number of nodes that needs to be evaluated is approximately 1,094,190 compared to depth 13 which is approximately 405,256 nodes. This means that it would need significantly more time to complete the evaluation of these depths up to the point of never. 10.3 Depth Problem With Respect To A* Searches 2 A reasonable amount of time is approximately ¾ of a hour. This was sufficient time for me to get bored playing other games or watching television, waiting for an answer. However, according to the ACM Regional Coding Contest, I recently attended, the reasonable amount of time was significantly lower. It actually was two minutes. 3 1 location with all four valid moves, 4 with three valid moves, and 4 with two valid moves = ((1*4)+(4*3)+(4*2))/ 9 spots = 2.6666 ~ 2.7 Page 13 of 14 The A* searches also have problems with large depths. However, when using the Randomize Tiles function, the main problem that was truly noticed was between depth 30 to depth 50. This problem was the ability to display the moved taken to trace back the path to reach the Goal State. The number that continued to pop up as 18,144 nodes. A conclusion drawn from this is that there is a limitation to amount to information that was able to be stored in the solution vector, which resulted in this problem. Further research into the matter is underway. 10.4 Applet Usage Problem With Respect To Microsoft Internet Explorer The Microsoft Internet Explorer problem is most likely the result of the browser’s hatred for working with non-Microsoft products. The applet opens in IE, however totally functionality does not work. This includes movement of tiles, running Randomize Tiles, and submitting a request to solve the puzzle. Further research into the matter is underway. 10.5 Applet Usage Problem With Respect To Netscape Navigator The problem with Netscape Navigator is the inability to use the Randomize Tiles function. Further research into the matter is underway and therefore suggested that Java’s Appletviewer be used to test and play with the program at the present time to have all functionality working. Page 14 of 14