CS 151: Programming Assignment 1
Introduction to Python and Search [60 points]
due Tuesday, January 27, 11:55pm
Please
read and follow the submission instructions on the assignment page.
This is a SOLO
assignment.
Everyone must do their own work on this assignment (but you
may
verbally discuss the assignment with others if you want).
In this course you will complete your programming assignments in
Python. I chose Python over other textbook-supported languages (LISP
and Java) because (1) Python is easy to use and fairly easy to learn,
(2) one of the more complete libraries associated with the textbook is
in Python, and (3) Python is a highly useful language to know.
The purpose of this assignment is to help you get comfortable
programming in Python. This assignment will NOT teach you
everything
you need to know about Python; thus, another purpose of this assignment
is to help familiarize you with the Python
documentation. Keep this documentation handy
throughout the semester.
Part 1: Getting Started
Python is installed on all of the lab machines in Beckman B102 and
B105, but I recommend that you also install it on your own computer.
You can obtain Python at www.python.org/download/.
After installing Python you can immediately run it by
choosing "Python
(command line)" from the Python menu from the programs part of your
start menu. You should see Python start in a command window.
Alternatively you can start python by opening a command
window and
typing "python" at the prompt. Of course you need to add
Python to
your path if you are not running from within Python's directory.
Python is an interpreted language, which means you can type commands
directly at the prompt. Try the following, pressing enter
after each
one. Notice that you do NOT need semi-colons at the end of
each line,
nor do you need to declare variables before you use them (Python has implicit typing,
like Rex).
>>
10**4
>> a =
'ai is cool'
>> len(a)
>> a.split()
>> myL = [1, 2, 3, 4]
>> myL[1:3]
>> myL[1:]
Can you understand what each of the above lines of code does?
You'll
get more practice with lists and strings later, but you may also want
to check out the documentation.
Of course, you will usually want to save the programs you write.
Like
in almost any other language, you can write programs in a file and then
load them into the Python interpreter. Python comes with its
own
editor, called IDLE. In my opinion IDLE is pretty good, but
you may
use any editor you like. jEdit, Crimson and Emacs
are other reasonable options.
Open your editor of choice and type in (or copy) the following code
that defines a simple list search function:
def listSearch( L, x
):
""" Searches through a list L for the
element x"""
for item in L:
if item
== x:
return
True # We found it, so return True
return
False
# Item not found
Save your file as "search.py".
Notice a few things about the syntax of this function:
- Python uses colons and indentation to indicate blocks.
Indentation is
critical in Python.
The colon at the end of a line indicates the start of a block
of code,
all of which must be indented to the same degree. For
example, the
"return False" line is outside the for-loop because it is indented at
the level of the function body, not the for-loop body.
- Variables
are not declared in Python. The L is understood to be a list
and the x
an element in the list, but it's up to the user to enforce this.
Return values are also not specified. If a function
includes a return
statement, that's what it returns. If it does not, then it
returns
nothing.
- The for-loop in Python is a little different
from what you might be used to. It specifies that the
variable i
should take on each value in the list L. The "range" function
is very
useful in getting for-loops in Python to behave like "normal"
for-loops. (See the documentation on for-loops).
- The line in quotes at the top of the function is a
special comment called a "docstring". It is displayed when
the user
asks for documentation about this function by typing help(listSearch)
at the Python prompt. (Try this below). The parts
of the line
starting with the symbol # are regular comments.
To run
this function, you
first need to load the code into the interpreter. From IDLE,
simply
press F5 to load the function. You should see a Python prompt
(but it
should look like nothing really happened). If you are not
using IDLE,
in a command prompt navigate to the directory containing the file you
just created and run Python (by typing "python"). Then type:
>> import search ; reload(search) ; from search import *
This line is overkill for loading your file, but it will definitely
load and/or reload everything you have written in the file search.py so
I suggest copying and pasting this line for any file you are trying to
load into Python in the future (changing the filename as appropriate,
of course).
Now that the program is loaded you can run any functions
defined in the file (in this case just one). Try it out, e.g.:
>>
listSearch([2, 5, 1, 6, 10], 1])
True
Part 2: Programming in Python
You're now set up and ready to go. Complete each of the
following
problems. Be sure to include docstrings for every function
you write.
- Objects and Games [20 points]. Complete this problem in a file called
pa1pr1.py
In this problem you will develop an object for managing a game of Tic
Tac Toe. While we covered the basics of creating objects in
Python in
class, you will probably want to refer to Python's
documentation on creating classes. Create a class
called TTTBoard that defines the following functions:
You may represent the board however you like. You may define
other
functions as well. You may wish also to define a function
that allows
two players to play the game to make sure your above functions are
working correctly.
- Missionaries and Cannibals [40 points] (Based on AIMA Ex 3.9) Complete this problem is a file called pa1pr2.py.
In this problem you will formulate and solve the famous "Missionaries
and Cannibals" problem. (You'll remembe a similar homework from
CS60, but this time we won't use Prolog, so the search is explicit).
The problem goes like this:
There are 3 missionaries and 3 cannibals on one side of a river.
They have 1 boat, that can hold 1 or 2 people. The goal is
to get everyone across the river without ever leaving more cannibals
than missionaries alone on one side at a time (if there are 0
missionaries, that's OK).
This problem is famous in AI because it was used in the first paper
that presented problem formulation from an analytical viewpoint (AIMA,
90).
a. Formulate this problem precisely as a search problem. First write a class named MissionariesAndCannibals that defines the state space. This class must include (among any other functions you wish to define):
- Data members for everything necessary to represent the state (you need to decide how to do this). Include a comment at the top of your file describing your state space.
- __init__(self): creates an object representing the initial state
- goalTest(self): returns True if the state represents the goal, and False otherwise
- successors(self): returns a list of (action, cost, state) tuples representing all of the legal states that are reachable using valid actions from the current state (self). action is a string representing the action taken (e.g. '2M and 0C from 1 to 2'), cost is the cost of taking this ONE action, and state is a state object representing the new state reached.
- __repr__(self): method that returns a human readable string representation of the state.
b. Now write a class named SearchNode
that keeps track of the information needed to search for the solution.
This class should be flexible, and should not be tied to a
specific search algorithm. It should include the following:
- the following data members:
- state: the node's state (a MissionariesAndCannibals object)
- parent: the node's parent node (a SearchNode object)
- action: a string representing the action that was taken to reach this node
- pathCost: an int representing the total path cost to reach the node
- depth: an int representing the depth of the node
- __init__(self, state, parent=None, action=None, pathCost=0, depth=0):
A function that initializes a new node. Each of the parameters
corresponds to one thing the node must keep track of (note you can give
them default values like I have).
- expand(self): A function that returns a list of nodes reachable from the current node.
- pathFromStart(self): A function that retuns a list containing all of the (action, state) pairs to reach the current node from the root node.
c. Finally, outside of your two classes, write a function named SearchForSolution()
that starts at the initial state for the Missionaries and Cannibals
problem, and searches until it reaches the goal state. Your
function should return the SearchNode
object that contains the first goal state that you reach in your
search. You may implement any search algorithm you like. In a comment above your function, state which algorithm you chose and why.
Hint: If you are using DFS, you'll need to keep track of states you've already seen to avoid going into an infinite loop.
When you are done, submit your solutions to both problems though
the Sakai site.