CS 5541 Lab 3: Search Strategies in Python

Fall 2003


Turned out: Tuesday, 30 September 2003

Due date: Tuesday, 14 October 2003

In this assignment, you are going to write two functions: one for breadth-first search, and one for depth first search. You will write these functions, and any auxiliary functions you need in Python. The problem you are solving is the missionaries and cannibals problem-- with operators available as given in lecture

Your functions

Your search functions should be called in the following way:

problem = [startState, operators, goalTest]

## BFS search
def BFS(problem):
    def BFSQueueFunction(childrenOfExpandedNode, nodeQueue):
        return nodeQueue + childrenOfExpandedNode
    print "Performing BFS search..."
    Search(problem, BFSQueueFunction)
## End-def

## DFS search
def DFS(problem):
    def DFSQueueFunction(childrenOfExpandedNode, nodeQueue):
        return childrenOfExpandedNode + nodeQueue
    print "Performing DFS search..."
    Search(problem, DFSQueueFunction)
## End-if

BFS(problem)
print
DFS(problem)
  
A problem is defined as a Python list containing: the start state of the problem, a list of operators (including their textual names), and a function to test for the goal state (takes a single parameter, a state, and returns 1 for true, or 0 for false).
[startState, operators, goalTest]

Each of your search functions needs to print out the following information:

(1) Whether or not the search was successful (was the goal found?);
(2a) If the goal was found, then the textual names of the operators along the path from the start state to the goal state;
(2b) If the goal was found, then the length of this path in (2a) to reach the goal (path cost);
(3) If the goal was found, then the number of nodes (states) that were expanded in the search to reach the goal (search cost). A node is said to be expanded when it is passed as a parameter to Apply, to apply the operators (see class notes online on Chapter 3).

Your search functions need to avoid expanding the same node twice. E.g., for the m&c problem, only expand the start state node once. To do this, you need to keep a list of all nodes that have previously been expanded.

Do not depend on global variables for implementing your algorithms. You will receive points off for depending on global variables in your algorithms. Of course, you can define the start state, and operator list as globals, but you should use these globally defined objects by passing them as parameters into your functions.

Some Algorithm Details

Your DFS and BFS functions need to call a common function, Search, as above. Since depth-first search and breadth-first search algorithms differ only in terms of how nodes are queued (see pp. 72-77 of the course text), they can be implemented in terms of a common function, Search, which accepts a queuing function as a parameter.

You need to modify the operator representation we studied in class so that each operator (e.g., in the operators list) has a textual name. You will need this in the state representation as follows.

You need to modify the state representation we gave in class so that it keeps track of the sequence of operators (i.e., each textual operator name) that was required to reach that state. (This will make it so that a simple test for equality of states will not work, so beware!).

You need to modify the Apply function so that it does not include empty states in the list of states that it returns.

You need to modify the Move function given in class so that it returns an empty state if cannibals would outnumber missionaries.

Example Output

[nomad10-132:~/cs5541/lab3] chris% python -i lab3.py
Performing BFS search...
Search: Expanding states: XX states from Apply (YY new states): 3 (3); 1 (0); 2 (1); 3 (2); 2 (0); 
2 (1); 2 (1); 2 (1); 2 (1); 2 (1); 2 (1); 3 (2); 2 (1);
Search: Found goal!
Search: Path to goal was: ['Move0m2c', 'Move0m1c', 'Move0m2c', 'Move0m1c', 'Move2m0c',
 'Move1m1c', 'Move2m0c', 'Move0m1c', 'Move0m2c', 'Move1m0c', 'Move1m1c']
Search: Length of path to goal (number of operators): 11
Search: Expanded 13 states to find goal state.

Performing DFS search...
Search: Expanding states: XX states from Apply (YY new states): 3 (3); 1 (0); 2 (1); 2 (1); 3 (1); 3 (1); 
2 (1); 2 (1); 2 (1); 2 (1); 2 (1); 2 (1); 2 (1); 2 (1); 2 (1); 2 (1); 2 (1); 2 (1); 3 (2); 3 (2); 2 (1); 2 (1); 2 (1); 2 (1);
Search: Found goal!
Search: Path to goal was: ['Move0m2c', 'Move0m1c', 'Move0m2c', 'Move0m1c', 'Move2m0c', 
'Move1m1c', 'Move2m0c', 'Move0m1c', 'Move0m2c', 'Move1m0c', 'Move1m1c']
Search: Length of path to goal (number of operators): 11
Search: Expanded 24 states to find goal state.

Documentation and Testing

Run your search functions on the m&c problem.

In addition to solving the problem, your program needs to adhere to standard software engineering concepts. Documentation of your parameters and variables is important, because Python has very little support for type checking. Ensure that you comment segments of code that perform intricate algorithmic manipulation. Provide a comment for each function indicating what its parameters are and their type, and what the function does. As always, if you document your program code just before you write it, you will avoid the excruciating pain of having to do the commenting after the fact. Learn the mantra: Commenting before helps me understand my own program!

Turn In

You need to turn in hard copy listings of your program. Turn in hard copy of your test output for solving the m&c problem for each of your functions. Please email the TA (Prashant Jain) a copy of your program code as well.