Eight Puzzle FAQ
Eight Puzzle FAQ
If you don’t see your question here, post it on Piazza or come to office hours! See the links in the navigation bar for both of those options.
General questions
-
What parts of the Eight Puzzle should we submit for Problem Set 17?
You should submit parts I and II (board) from the Eight Puzzle. Be sure to submit your work for these parts of the assignment in the Problem Set 17 section of Gradescope.
-
Are comments needed for each method/function?
As always, please include docstrings. You should also include comments for portions of your code if they might be hard to understand without the comments.
Part I
-
In the
Boardclass, does thetilesattribute have to be a 2-D list of integers, or can it contain strings?It must be a 2-D list of integers, with 0 representing the blank cell. For example, let’s say that you construct a
Boardobject as follows:>>> b = Board('012345678')
If you then evaluate its
tilesattribute, here is what you should see:>>> b.tiles [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
You should not see the following:
>>> b.tiles [['0', '1', '2'], ['3', '4', '5'], ['6', '7', '8']]
If you do, you should fix your code.
-
What should our
move_blankmethod do if a direction other than'left','right','up', or'down'is passed in for the input?It should print an error message (see the example cases for the format of the message) and return
False, without making any changes to the object. Make sure that you don’t try to return the error message. It should be printed, and then you should returnFalse.
Part II
-
In the
__init__method for theStateclass, how can we determine the number of moves of the predecessor?Keep in mind that the predecessor is also a
Stateobject. Therefore, you can access itsnum_movesattribute using dot notation.For example, if
pis the predecessor, you would dop.num_moves. You should replacepwith the appropriate expression for the predecessor. -
In the
is_goalmethod, how can I access the tiles in theBoardobject associated with theStateobject?Inside any
Statemethod,self.boardwill give us access to theBoardobject associated with theStateobject. And becausetilesis an attribute of theBoardobject, you can use the following expression to access the tiles:self.board.tiles -
In the
generate_successorsmethod, what should we use as the input for the predecessor when we construct the newStateobject?As the hints provided for this method suggest, you should use the variable
selffor the predecessor, since the current state (the one represented by the called objectself) is itself the predecessor of the new state. -
In the
generate_successorsmethod, when I try to add a successor to the list of successors, I get an error that saysState object is not iterable. What am I doing wrong?When you use the
+or+=operator to add aStateobject to the list of successors, you need to perform list concatenation – adding one list to another list. As a result, you need to make sure that you put square brackets[]around theStateobject. -
My
generate_successorsmethod doesn’t produce the expected results. What am I doing wrong?One thing worth checking: Make sure that you aren’t mistakenly calling
move_blank()twice for a given move.Each call to
move_blank()does up to two things: if the move is possible, it modifies the contents of theBoardobject, and it also returns eitherTrueorFalseto indicate whether the move was possible. As a result, you should only call it once per move. However, many people have been mistakenly calling it twice per move: once to check if the move is possible, and once to make the move.For example, this is not correct:
# don't do this! if b.move_blank(direction) == True: b.move_blank(direction) # no! this moves the blank again! s = State(b, ...) ...
Rather, you should do this instead:
# do this instead! if b.move_blank(direction) == True: s = State(b, ...) ...
Part III
-
My
should_addmethod doesn’t seem to work correctly for the case in which the inputstatewould create a cycle. I don’t see any errors in myshould_add. Is it possible that the providedcreates_cyclemethod has a bug?No! However, it is possible that the problem actually goes back to one of your
Boardmethods. Thecreates_cyclemethod compares twoBoardobjects to check for a cycle, so if one of yourBoardmethods is making an improper change to aBoardobject, then it’s possible that twoBoardobjects that should be considered equal are actually considered unequal.Here’s one piece of test code that could help you to diagnose the problem:
>>> b = Board('012345678') >>> b.tiles [[0, 1, 2], [3, 4, 5], [6, 7, 8]] >>> b.move_blank('right') True >>> b.tiles [[1, 0, 2], [3, 4, 5], [6, 7, 8]]
Make sure that the 2-D lists that you get for
b.tilesmatch the ones that you see above. In particular, make sure that all of the elements of the lists are integers, not strings. If your results forb.tilesare not correct, check your__init__andmove_blankmethods.Other possible sources of the problem are: (1) the
__eq__,copy, anddigit_stringmethods in theBoardclass, and (2) the__init__andgenerate_successorsmethods in theStateclass, which might be failing to set up an appropriate connection between aStateobject and its predecessor. Here is some additional test code that may help you to diagnose the problem:>>> b = Board('012345678') >>> b.tiles [[0, 1, 2], [3, 4, 5], [6, 7, 8]] >>> b.digit_string() '012345678' >>> b.tiles [[0, 1, 2], [3, 4, 5], [6, 7, 8]] >>> b2 = b.copy() >>> b2.tiles [[0, 1, 2], [3, 4, 5], [6, 7, 8]] >>> b.tiles [[0, 1, 2], [3, 4, 5], [6, 7, 8]] >>> s = State(b, None, 'init') >>> s.predecessor == None True >>> succ = s.generate_successors() >>> succ [312045678-down-1, 102345678-right-1] >>> succ[0].predecessor 012345678-init-0 >>> succ[1].predecessor 012345678-init-0
Part IV
-
How can we determine which state has been in the list the longest?
Try tracing through some examples on paper of adding states–one at a time–to the list, and think about which of them should be removed if you want the one that has been in the list the longest. We actually did this in lecture, so you could also consult the notes from before the break.
-
My
GreedySearcherclass passes the tests that are given in task 4 of Part IV, but when I try to use Greedy Search in the context of theeight_puzzledriver function in task 6, I’m getting an error that says that a ‘State’ object does not support indexing. What I am doing wrong?The problem may actually be in your
Searcherclass, sinceGreedySearcherinherits fromSearcher. In particular, make sure that theadd_statesmethod inSearchercalls theadd_statemethod to add each new state toself.states. It should not add the individual states on its own (e.g., by performing list concatenation).The reason that this matters is that
GreedySearcheroverrides the version ofadd_state()that it inherits fromSearcher, but it doesn’t override the inherited version ofadd_states. As a result, ifadd_states()doesn’t calladd_state()as it should, theGreedySearcherversion ofadd_state()won’t get called, and thus the list of states won’t include the priorities as required.
Part V
-
How do I read from a file?
In lecture, we presented two different ways to read the contents of a file. You can consult the lecture notes from a couple weeks ago or check out the example code online. For the assignment, we recommend reading in the file one line at a time using a
forloop. -
I’ve completed my
process_filefunction, and I’m performing the initial tests that you recommend using the10_moves.txtfile. I’m able to reproduce the results for BFS, but I don’t get the same results for Greedy. Is that a problem?Probably. When you first implemented the
GreedySearcherclass in Part IV, task 4, if you got the same results as we did for the examples that we provided, then you should get more or less the same results here as well. The only possible exception is the number of searches that you choose to terminate.If your results don’t match ours, one thing to double check is your
num_misplacedmethod in theBoardclass. Here are some additional test cases for that method that should help you to ensure that it’s working correctly in all cases:>>> b = Board('012345678') >>> b.num_misplaced() 0 >>> b.move_blank('right') True >>> b.num_misplaced() 1 >>> b.move_blank('down') True >>> b.num_misplaced() 2 >>> b.move_blank('left') True >>> b.num_misplaced() 3 >>> b.move_blank('up') True >>> b.num_misplaced() 3