Assignment 2: Iterative-Deepening Search

  • A2.1: New A2grader.tar and modified explanation of one of the depth_limited_search results.

Type your name here.

Overview

Implement the iterative-deepening search algorithm as discussed in our lecture notes and as shown in figures 3.17 and 3.18 in our text book. Apply it to the 8-puzzle and a second puzzle of your choice.

Required Code

In this jupyter notebook, implement the following functions:

  • iterative_deepening_search(start_state, goal_state, actions_f, take_action_f, max_depth)
  • depth_limited_search(start_state, goal_state, actions_f, take_action_f, depth_limit)

depth_limited_search is called by iterative_deepening_search with depth_limits of $0, 1, \ldots, $ max_depth. Both must return either the solution path as a list of states, or the strings 'cutoff' or 'failure'. 'failure' signifies that all states were searched and the goal was not found.

Each receives the arguments

  • the starting state,
  • the goal state,
  • a function actions_f that is given a state and returns a list of valid actions from that state,
  • a function take_action_f that is given a state and an action and returns the new state that results from applying the action to the state,
  • either a depth_limit for depth_limited_search, or max_depth for iterative_deepening_search.

Use your solution to solve the 8-puzzle. Implement the state of the puzzle as a list of integers. 0 represents the empty position.

Required functions for the 8-puzzle are the following.

  • find_blank_8p(state): return the row and column index for the location of the blank (the 0 value).
  • actions_f_8p(state): returns a list of up to four valid actions that can be applied in state. Return them in the order left, right, up, down, though only if each one is a valid action.
  • take_action_f_8p(state, action): return the state that results from applying action in state.
  • print_state_8p(state): prints the state as a 3 x 3 table, as shown in lecture notes, or a bit fancier with, for example, '-' and '|' characters to separate tiles. This function is useful to call when debugging your search algorithms.
  • print_path_8p(start_state, goal_state, path): print a solution path in a readable form by calling print_state_8p.

Also, implement a second search problem of your choice. Apply your `iterative_deepening_search` function to it.

Here are some example results.

In [34]:
start_state = [1, 0, 3, 4, 2, 5, 6, 7, 8]
In [35]:
print_state_8p(start_state)
 1 - 3
 4 2 5
 6 7 8
In [36]:
find_blank_8p(start_state)
Out[36]:
(0, 1)
In [37]:
actions_f_8p(start_state)
Out[37]:
['left', 'right', 'down']
In [38]:
take_action_f_8p(start_state, 'down')
Out[38]:
[1, 2, 3, 4, 0, 5, 6, 7, 8]
In [39]:
print_state_8p(take_action_f_8p(start_state, 'down'))
 1 2 3
 4 - 5
 6 7 8
In [40]:
goal_state = take_action_f_8p(start_state, 'down')
In [41]:
new_state = take_action_f_8p(start_state, 'down')
In [42]:
new_state == goal_state
Out[42]:
True
In [43]:
start_state
Out[43]:
[1, 0, 3, 4, 2, 5, 6, 7, 8]
In [47]:
path = depth_limited_search(start_state, goal_state, actions_f_8p, take_action_f_8p, 1)
path
Out[47]:
[[1, 2, 3, 4, 0, 5, 6, 7, 8]]

Notice that depth_limited_search result is missing the start state. This is inserted by iterative_deepening_search.

In [48]:
path = depth_limited_search(start_state, goal_state, actions_f_8p, take_action_f_8p, 2)
path
Out[48]:
[[1, 2, 3, 4, 0, 5, 6, 7, 8]]
In [49]:
path = depth_limited_search(start_state, goal_state, actions_f_8p, take_action_f_8p, 3)
path
Out[49]:
[[0, 1, 3, 4, 2, 5, 6, 7, 8],
 [1, 0, 3, 4, 2, 5, 6, 7, 8],
 [1, 2, 3, 4, 0, 5, 6, 7, 8]]

Here depth_limited_search returns more than the solution path. This is due to how we implement depth_limited_search. This only happens when we don't find the shortest path. Of course, when called from iterative_deepening_search we do find the shortest path.

In [50]:
path = iterative_deepening_search(start_state, goal_state, actions_f_8p, take_action_f_8p, 4)
path
Out[50]:
[[1, 0, 3, 4, 2, 5, 6, 7, 8], [1, 2, 3, 4, 0, 5, 6, 7, 8]]

Also notice that the successor states are lists, not tuples. This is okay, because the search functions for this assignment do not make use of python dictionaries.

In [14]:
start_state = [4, 7, 2, 1, 6, 5, 0, 3, 8]
path = iterative_deepening_search(start_state, goal_state, actions_f_8p, take_action_f_8p, 3)
path
Out[14]:
'cutoff'
In [15]:
start_state = [4, 7, 2, 1, 6, 5, 0, 3, 8]
path = iterative_deepening_search(start_state, goal_state, actions_f_8p, take_action_f_8p, 5)
path
Out[15]:
'cutoff'

Humm...maybe we can't reach the goal state from this state. We need a way to randomly generate a valid start state.

In [16]:
import random
In [17]:
random.choice(['left', 'right', 'down', 'up'])
Out[17]:
'down'
In [20]:
def random_start_state(goal_state, actions_f, take_action_f, n_steps):
    state = goal_state
    for i in range(n_steps):
        state = take_action_f(state, random.choice(list(actions_f(state))))  # list required because actions_f is generator
    return state
In [21]:
goal_state = [1, 2, 3, 4, 0, 5, 6, 7, 8]
random_start_state(goal_state, actions_f_8p, take_action_f_8p, 10)
Out[21]:
[1, 2, 3, 4, 7, 5, 0, 6, 8]
In [22]:
start_state = random_start_state(goal_state, actions_f_8p, take_action_f_8p, 50)
start_state
Out[22]:
[1, 3, 8, 4, 2, 7, 6, 5, 0]
In [23]:
path = iterative_deepening_search(start_state, goal_state, actions_f_8p, take_action_f_8p, 20)
path
Out[23]:
[[1, 3, 8, 4, 2, 7, 6, 5, 0],
 [1, 3, 8, 4, 2, 0, 6, 5, 7],
 [1, 3, 0, 4, 2, 8, 6, 5, 7],
 [1, 0, 3, 4, 2, 8, 6, 5, 7],
 [1, 2, 3, 4, 0, 8, 6, 5, 7],
 [1, 2, 3, 4, 5, 8, 6, 0, 7],
 [1, 2, 3, 4, 5, 8, 6, 7, 0],
 [1, 2, 3, 4, 5, 0, 6, 7, 8],
 [1, 2, 3, 4, 0, 5, 6, 7, 8]]

Let's print out the state sequence in a readable form.

In [24]:
for p in path:
    print_state_8p(p)
    print()
 1 3 8
 4 2 7
 6 5 -

 1 3 8
 4 2 -
 6 5 7

 1 3 -
 4 2 8
 6 5 7

 1 - 3
 4 2 8
 6 5 7

 1 2 3
 4 - 8
 6 5 7

 1 2 3
 4 5 8
 6 - 7

 1 2 3
 4 5 8
 6 7 -

 1 2 3
 4 5 -
 6 7 8

 1 2 3
 4 - 5
 6 7 8

Here is one way to format the search problem and solution in a readable form.

In [25]:
print_path_8p(start_state, goal_state, path)
Path from
 1 3 8
 4 2 7
 6 5 -
  to
 1 2 3
 4 - 5
 6 7 8
  is 9 nodes long:
 1 3 8
 4 2 7
 6 5 -

  1 3 8
  4 2 -
  6 5 7

   1 3 -
   4 2 8
   6 5 7

    1 - 3
    4 2 8
    6 5 7

     1 2 3
     4 - 8
     6 5 7

      1 2 3
      4 5 8
      6 - 7

       1 2 3
       4 5 8
       6 7 -

        1 2 3
        4 5 -
        6 7 8

         1 2 3
         4 - 5
         6 7 8

Grading and Check in

Download A2grader.tar and extract A2grader.py from it, before running next code cell.

In [27]:
%run -i A2grader.py
======================= Code Execution =======================

Extracting python code from notebook named 'Anderson-A2.ipynb' and storing in notebookcode.py
Removing all statements that are not function or class defs or import statements.

Searching this graph:
 {'a': ['b', 'z', 'd'], 'b': ['a'], 'e': ['z'], 'd': ['y'], 'y': ['z']}

Looking for path from a to y with max depth of 1.
 5/ 5 points. Your search correctly returned cutoff

Looking for path from a to z with max depth of 5.
10/10 points. Your search correctly returned ['a', 'z']

Testing find_blank_8p([1, 2, 3, 4, 5, 6, 7, 0, 8])
 5/ 5 points. Your find_blank_8p correctly returned 2 1

Testing actions_f_8p([1, 2, 3, 4, 5, 6, 7, 0, 8])
10/10 points. Your actions_f_8p correctly returned ['left', 'right', 'up']

Testing take_action_f_8p([1, 2, 3, 4, 5, 6, 7, 0, 8], up)
10/10 points. Your take_actions_f_8p correctly returned [1, 2, 3, 4, 0, 6, 7, 5, 8]

Testing iterative_deepening_search([1, 2, 3, 4, 5, 6, 7, 0, 8],
                                   [0, 2, 3, 1, 4,  6, 7, 5, 8],
                                    actions_f_8p, take_action_f_8p, 5)
20/20 points. Your search correctly returned
               [1, 2, 3, 4, 5, 6, 7, 0, 8]
               [1, 2, 3, 4, 0, 6, 7, 5, 8]
               [1, 2, 3, 0, 4, 6, 7, 5, 8]
               [0, 2, 3, 1, 4, 6, 7, 5, 8]

Testing iterative_deepening_search([5, 2, 8, 0, 1, 4, 3, 7, 6],
                                   [0, 2, 3, 1, 4,  6, 7, 5, 8],
                                   actions_f_8p, take_action_f_8p, 10)
10/10 points. Your search correctly returned cutoff.

======================================================================
notebooks Execution Grade is 70 / 70
======================================================================

__ / 10 points. At least four sentences describing the solutions found for the 8 puzzle.

__ / 20 points. At least six sentences describing the second search problem, your implementation 
               of state, and the solutions found.

======================================================================
notebooks Additional Grade is __ / 30
======================================================================

======================================================================
notebooks FINAL GRADE is  _  / 100
======================================================================

Extra Credit: Earn one point of extra credit for using your search functions to solve the variation
              of the grid problem in Assignment 1.

notebooks EXTRA CREDIT is 0 / 1

Check in your notebook for Assignment 2 on our Canvas site.

Extra Credit

For extra credit, apply your solution to the grid example in Assignment 1 with the addition of at least one horizontal and at least one vertical barrier, all at least three positions long. Demonstrate the solutions found in four different pairs of start and goal states.