CS101 Lecture Notes
CS101 Lecture Notes
hubo.turn_left()
This robot world consists of 10 vertical avenues Our robot will turn to face northwards:
and 10 horizontal streets. The robot can be placed
at any intersection of an avenue with a street. So
let’s add a robot, by typing in the following line:
hubo = Robot()
Email: [email protected]
1
You can now control your robot by entering com-
mands one by one—the robot executes each com- from cs1robots import *
mand as soon as you have entered it. create_world()
hubo = Robot()
hubo.set_trace("blue")
2 First program
hubo.set_pause(2)
But controlling a robot by typing in commands on ...
a keyboard is not programming. “Programming” a
robot means to design a whole sequence of instruc- The number in parentheses is the length of the
tions (an algorithm) for the robot, which is then break in seconds.
executed automatically by the robot to solve some
task.
Let’s try this: Type the following text in code 3 Bugs!
section: What happens when you mistype a command:
from cs1robots import *
hubo.Move()
create_world()
hubo = Robot()
hubo.move() (Yes, Python distinguishes lower-case and upper-
hubo.turn_left() case letters.) Make sure you try it to see exactly
hubo.move() what happens. In the future, if you see the same
hubo.turn_left() kind of complaint, it might help you discover what’s
hubo.move() wrong with your program. In other words, you will
hubo.turn_left() find how to recognize one kind of bug.
hubo.move() The following program demonstrates a different
hubo.turn_left() bug:
2
There are a few ways to write comments in a pro-
gram. If a line starts with a hash symbol #, then
this line is a comment and will be ignored by the
Python interpreter:
# My first program
def turn_right():
hubo.turn_left()
hubo.turn_left()
hubo.turn_left()
hubo.move()
So how can we make the robot turn right instead turn_right()
of left? The only possible way to do this is by exe- hubo.move()
cuting three left-turns. The following program let’s turn_right()
our robot walk around a square in clock-wise direc- hubo.move()
tion: turn_right()
hubo.move()
from cs1robots import *
create_world() A function definition starts with the Python key-
hubo = Robot() word def, the name of the function, a pair of paren-
hubo.turn_left() theses, and a colon. The body of the function are
hubo.move() the instructions that are executed when the func-
# make a right turn tion is called. Each line in the body has to be in-
hubo.turn_left() dented by the exact same amount (it doesn’t matter
hubo.turn_left() how much—I always use two spaces).
hubo.turn_left() A keyword like def is a special word that can be
hubo.move() used in Python only to define a new function (it is
# make a right turn for instance not allowed to define a function named
hubo.turn_left() “def”).
hubo.turn_left()
hubo.turn_left() Task: (StepBack) Write a function step_back
hubo.move() that makes hubo go backward one step, so that
# make a right turn
hubo.move()
hubo.turn_left()
step_back()
hubo.turn_left()
hubo.turn_left()
hubo.move() will leave the robot at exactly the same position
and orientation it started with:
3
Note that we had to tell Python to create a robot
that already carries one beeper. (Try what happens
without this.)
6 Beepers
Beepers are small objects (drawn fairly big on the Avoid repetition by defining useful functions.
screen) that make a small beeping sound when they Maybe you would rather like to build a more
are turned on. A robot can only hear a beeper if interesting world. You can do that using the
he is standing right on top of them, on the same edit_world() function:
corner. He can pick them up and carry them in his
pockets, or he can put them down. from cs1robots import *
You can instruct the robot to pick up a beeper create_world(avenues=20, streets=5)
through the pick_beeper() command, or to put edit_world()
some down, through the drop_beeper() command. save_world("myworld.wld")
If you ask a robot to pick up beepers where there
are none, or to put down beepers if he doesn’t carry The edit_world function places your world in an
any, you will get an error message and your program editable state. You can click on the world to add
will terminate. and remove walls. If you click with the left mouse
The following small program will drop a beeper button on an intersection, a beeper is placed there.
at the intersection of avenue 3 and street 1: You can remove beepers with the right mouse but-
from cs1robots import * ton. Press the Return key when you are done edit-
create_world() ing. Your program will then continue and save your
hubo = Robot(beepers=1) world.
hubo.set_trace("blue")
hubo.move() 8 Top-down design
hubo.move()
hubo.drop_beeper() Let’s solve the following problem: Hubo delivers
hubo.move() newspapers in his local neighborhood. Have him
hubo.move() climb the stairs to the front door of a house, drop
the newspaper (represented by a beeper) on the top
4
step, and return to his starting point as illustrated Task: (Harvest1) It’s harvest time! Have the
below. The world file is newspaper.wld : robot pick up all the carrots (represented by beep-
ers) in this garden. The world file is harvest1.wld.
Use top-down design.
5
• The series of instructions to follow in that case
is indented, just like we had in the case of func-
tion definitions.
Let’s look at a simple example. Suppose we want
the robot to take 9 steps, picking up any beepers
that are there along the way. (We suppose that
there can be at most one beeper at a given spot.)
For example, the starting position might be like the
following:
10 If only the robot could decide. . . and we want the final position to be:
How would we solve the harvesting problem on a
world like this? (The world file is harvest3.wld.)
def move_and_pick():
hubo.move()
if hubo.on_beeper():
It would be nice if we could just run the progrem hubo.pick_beeper()
we wrote in task Harvest2. But it doesn’t work,
because calling pick_beeper() when there is no for i in range(9):
beeper causes an error. move_and_pick()
If only hubo could check if there is a beeper, and
pick it up only when a beeper is really there. . .
The Python keyword if does exactly this: Task: (Harvest4) Modify your program for the
Harvest2 task so that it works for the world har-
if hubo.on_beeper(): vest3.wld. Note that the new program should also
hubo.pick_beeper() automatically work for the original harvest1.wld
world. Try it!
Let’s look at the meaning of the above code: To be able to harvest in the fall, you have to
• The instruction if implies that some condi- put seeds in the ground in the spring. Can we
tion, whose value is true or false, is going to write a program that will put seed potatoes in the
follow; ground, but skip any spot where there already is
• hubo.on_beeper() is a condition (or test) a potato? In other words, starting with the world
which is true if hubo is on top of a beeper and harvest3.wld, we should end up with the world of
false otherwise; harvest1.wld !
• The colon (:) precedes the series of instructions To do this, hubo needs to drop a beeper if there
that the robot must follow if the condition is is no beeper at the current location. The following
true; code will do this:
6
if not hubo.on_beeper(): from cs1robots import *
hubo.drop_beeper() create_world(avenues=5, streets=5)
11 What else?
In addition to being able to find out if he is standing
next to one or more beepers, a robot can see if there
is a wall in front of him, blocking his way. He can
also turn his head to his left or his right and see if
there is a wall there. You can ask him to have a
look with the following tests:
We can make this more interesting by doing a
hubo.front_is_clear() “dance” if we can move forward, and dropping a
hubo.left_is_clear() beeper if we have to turn. The following program
hubo.right_is_clear() does just that, going only partly around the world:
Let’s use the first one to have the robot explore his from cs1robots import *
world. We will have him follow the boundary of create_world(avenues=5, streets=5)
his world by asking him to move forward, if there
is no wall in front of him, and turn left otherwise. hubo = Robot(beepers=100)
The following simple program is the basis of what hubo.set_trace("blue")
is needed:
def dance():
if hubo.front_is_clear(): for i in range(4):
hubo.move() hubo.turn_left()
else:
hubo.turn_left() def move_or_turn():
if hubo.front_is_clear():
We learnt a new Python keyword here: else. It dance()
introduces an alternative sequence of instructions. hubo.move()
In this example, if there is no wall in front of hubo, else:
then hubo.front_is_clear() is true, and the se- hubo.turn_left()
quence after the if line is executed. If there is hubo.drop_beeper()
a wall, then hubo.front_is_clear() is false, and
the sequence after the else line is executed. Do for i in range(18):
not forget the colon after else, and watch out for move_or_turn()
the correct indentation!
Let’s repeat this simple conditional instruction Notice how the instructions dance() and
many times to go around the world: hubo.move() are aligned after the if statement
7
and indented from it, indicating that they belong
in the same block of instructions. The instructions def move_or_turn():
hubo.turn_left() and hubo.drop_beeper() if hubo.front_is_clear():
are similarly aligned, indented from the else dance()
statement to which they belong. The result of hubo.move()
running the program is indicated below. else:
hubo.turn_left()
hubo.drop_beeper()
def move_or_turn():
if hubo.front_is_clear():
dance()
hubo.move()
else:
hubo.turn_left() So, as you can see, much information is given
hubo.drop_beeper() through blank spaces (that is, the indentation of the
instructions within blocks). Through practice, you
will learn to use this to your advantage and realise
Now, the definition of move_or_turn() includes a
that Python allows you to write very readable code
choice resulting in either a dance and a move for-
by indenting instructions.
ward, or a left turn, followed every time by the in-
Our robot has become quite good at jumping
struction hubo.drop_beeper(). The result of run-
hurdles. He now enters races of different lengths:
ning this program is indicated below:
short sprints and long races. He knows that he has
reached the finish line when he is next to a beeper.
Below, you will find three such race courses; the
world files are hurdles1.wld, hurdles2.wld, and hur-
dles3.wld :
8
Task: (Hurdles3) Assume that there are no races The keyword pass which we introduced here is
longer than 20 units. Define a function that looks Python’s way of saying “do nothing.” It is neces-
somewhat like the following: sary, because it is not possible to write an empty se-
quence of instructions (an empty block ) in Python.
def move_jump_or_finish(): If we have more than three choices, all we need
# test for end of race to do is add other elif statements
if not hubo.on_beeper():
# is there a hurdle? def move_jump_or_finish():
if hubo.front_is_clear(): # test for end of race
hubo.move() if hubo.on_beeper():
else: pass # race is over - do nothing
jump_one_hurdle() # is there a hurdle?
elif hubo.front_is_clear():
hubo.move()
with an appropriate jump_one_hurdle() function, elif hubo.right_is_clear(): # never
so that, other than definitions, the only instruction pass
we need is: else:
jump_one_hurdle()
for i in range(20):
move_jump_or_finish()
Since we follow the bottom wall,
hubo.right_is_clear() is always false, so
Note that, in the above definition, the code is get- the pass instruction is always ignored. Note that
ting more and more indented as we introduce addi- if we had used hubo.left_is_clear() instead, we
tional tests. would have gotten stuck forever as soon as we had
reached the first hurdle. Try it for yourself!
12 If, else, if, else. . .
13 For a while. . .
The previous hurdles task required you to write
an if/else within another one, all this because we When we want to repeat some instructions until a
wanted to give three choices to the robot: finish, certain condition is satisfied, Python gives us a sim-
move or jump. You may have noticed that this pler way to write this using a new keyword: while.
forced us to indent the code further and further. Let me show you first how this would look using
Imagine what would happen if we wanted to give pseudocode:
10 mutually exclusive choices; the resulting code
would become hard to read. To help us in such While not on beeper,
situations, Python offers a keyword that represents ... keep moving;
the combination of an else statement followed by otherwise,
an if clause. That keyword is elif, which we can ... stop.
think of as an abbreviation for else if. With this
new keyword, the above code can be written as: You should agree that this expresses the same idea
as before. Using Python code, here is how we would
def move_jump_or_finish(): actually write it:
# test for end of race
if hubo.on_beeper(): while not hubo.on_beeper():
pass # race is over - do nothing hubo.move()
# is there a hurdle?
elif hubo.front_is_clear(): No more need for a for-loop with a fixed number of
hubo.move() repetitions.
else:
jump_one_hurdle() Task: (Hurdles4) Use a while loop to rewrite the
hurdles3 program so that you don’t have to use a
We can now better see, as they are indented the for-loop of fixed length. In other words, the core of
same way, that there are three possible choices. The your program should look like the following:
else condition is executed only if all the previous while not hubo.on_beeper():
conditions are false, so there is no condition associ- move_or_jump()
ated with it.
9
Task: (Harvest5) Modify your program for the think that we are done. So we need to make one
Harvest4 task so that it also works when there is move after dropping the beeper:
more than one carrot on one place, as in world file
harvest4.wld below. All carrots must be harvested, hubo.drop_beeper()
and it should also work for the previous worlds har- hubo.move()
vest1.wld and harvest3.wld. while not hubo.on_beeper():
if hubo.front_is_clear():
hubo.move()
else:
hubo.turn_left()
14 Around the world in 80 days The problem is that we assumed that we only
had to move forward or turn left to go around the
Let’s start by considering a simple problem: go world; we never took into account situations where
around the world once, assuming there is no ob- we would have wanted to make a right turn. What
stacle on the way. We have done this before, when we need to do is first to check on the right side to see
we introduced the front_is_clear() test. Here’s if there is still a wall; if not, we have to make a right
the outline of a solution which supposes that we turn. Here’s a modified program that attempts to
carry at least one beeper at the beginning: do just that:
1. Put down a beeper to mark the starting point.
2. Move forward until facing a wall. def turn_right():
3. Turn left when facing a wall. for i in range(3):
4. Repeat steps 2 and 3 until we find the beeper hubo.turn_left()
we had put down.
5. Finish when finding the beeper. hubo.drop_beeper()
The key step is 4, where we have a repeating in- hubo.move()
struction with a test. Let’s now translate the entire while not hubo.on_beeper():
solution in proper code: if hubo.right_is_clear():
turn_right()
hubo.drop_beeper() elif hubo.front_is_clear():
while not hubo.on_beeper(): hubo.move()
if hubo.front_is_clear(): else:
hubo.move() hubo.turn_left()
else:
hubo.turn_left() This still doesn’t work. The robot gets in an infinite
loop when there is no wall around him. (That’s
This does not work—the program terminates im- right: the while keyword makes it possible to write
mediately after dropping the beeper. The problem programs that never terminate.)
is that we drop the beeper, and then immedately We need to have him move after turning right:
10
15 Clarifying our intent
hubo.drop_beeper()
We seem to have designed a program that works
hubo.move()
in all situations we are likely to encounter. This
while not hubo.on_beeper():
program is to allow the robot to explore his world,
if hubo.right_is_clear():
going around once. While the program is rather
turn_right()
short, and its structure should be clear at this point,
hubo.move()
it might not be so obvious to someone who just
elif hubo.front_is_clear():
happened to see it for the first time.
hubo.move()
We discussed before that our goal should be to
else:
write programs so that they can be understood
hubo.turn_left()
by a human. It’s probably a good idea either to
add comments and/or to introduce more meaning-
But this still does not always work: ful words. Let’s start by adding comments, some-
what more verbose than we think we might need.
11
# This program lets the robot go
# around his world counterclockwise,
# stopping when he comes back to his
starting point.
def turn_right():
Even better, the same modified program can find
for i in range(3):
the exit to a maze (world file maze1.wld ):
hubo.turn_left()
def mark_starting_point_and_move():
hubo.drop_beeper()
while not hubo.front_is_clear():
hubo.turn_left()
hubo.move()
def follow_right_wall():
if hubo.right_is_clear():
# Keep to the right
turn_right()
hubo.move()
elif hubo.front_is_clear():
# move following the right wall
hubo.move()
else:
# follow the wall
17 Stepwise refinement
hubo.turn_left() We started with a simple problem to solve (going
around a rectangular world) and, by improving lit-
# end of definitions, begin solution tle by little (also called stepwise refinement), we
manage to write a program that could be used to
mark_starting_point_and_move() solve many other apparently unrelated problems.
At each step, we kept the changes small, and made
while not hubo.on_beeper(): sure we had a working solution, before considering
follow_right_wall() more complex problems. We also used more de-
scriptive names for parts of the algorithm which
Isn’t this clearer? This will also make it much easier made the program easier to read and, hopefully, to
for us to change (“maintain”) the program if we understand. This is a strategy you should use when
want to change its behaviour in the future. writing your own programs:
Steps to follow when writing a program:
16 Hurdles and mazes • start simple;
• introduce small changes, one at a time;
Change the program you just wrote to remove the • make sure that each of the changes you have
drop_beeper() instruction. After saving it, try introduced do not invalidate the work you have
this slightly modified program with the following done before;
hurdle race (world file hurdles3.wld ): • add appropriate comments that don’t simply
repeat what each instruction does; and
• choose descriptive names.
18 It’s raining!
Surprise! Other than finishing facing in an un- It was a beautifully sunny day. Hubo is playing
usual direction, the program we just wrote can solve outside with his friends. Suddenly, it started to
the hurdle problem. It also works with the uneven rain and he remembered that the windows in his
hurdle (world file hurdles4.wld ) illustrated below— house were all open. So he went back to his house
which the program we wrote before for jumping and stopped in front of the door, unsure of how to
over hurdles could not handle! proceed.
12
Verify to see if the program you wrote to help
Ami close the windows in her house can be used by
Hubo to do the same for his house. If not, change
Task: (Rain1) Help Hubo close the windows of it so that it works for both Ami and Hubo.
his house. A closed window has a beeper in front
of it. When Hubo finishes his task, he will stand in
the doorway, watching the rain fall, waiting for it 19 After the storm
to stop before he can go back and play outside, as The wind blew really hard last night. There is lit-
illustrated below. ter everywhere outside Hubo’s house. His parents
asked him to go and clean up the driveway, as well
as the path leading to the curb. They both are in
a straight line, with garbage at random places as
illustrated below.
while hubo.carries_beepers():
# do something
13
Create your own world file, corresponding to a The result looks like this:
situation like the one illustrated below. Your solu-
tion should not depend on the exact location of the
garbage, nor should it depend on the yard size!
from cs1robots import * Task: (Trash3) Rewrite your program for task
create_world() Trash2 so that Hubo and Ami work together. They
hubo = Robot("light_blue") both start at the normal starting position, and
hubo.turn_left() should work simultaneously to collect the garbage.
hubo.move() You can decide how to split the work: For instance,
for i in range(3): Ami could do the northern half of the yard, and
hubo.turn_left() Hubo the southern half. Or they can walk next to
ami = Robot("green") each other, picking up garbage in neighboring rows.
ami.move()
hubo.move()
14