0% found this document useful (0 votes)
139 views

Robot Notes

This document discusses using robots to learn programming concepts. It introduces a robot world environment with a robot that can move, turn left, and pick up and drop objects. The document shows how to write simple programs to control the robot by giving it commands. It discusses using functions to avoid repeating the same instructions. Programming robots requires thinking about how to write code that is easy for humans to understand.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
139 views

Robot Notes

This document discusses using robots to learn programming concepts. It introduces a robot world environment with a robot that can move, turn left, and pick up and drop objects. The document shows how to write simple programs to control the robot by giving it commands. It discusses using functions to avoid repeating the same instructions. Programming robots requires thinking about how to write code that is easy for humans to understand.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 15

Learning Programming with Robots∗

Edited by Otfried Cheong


January 26, 2010

1 Robot world
We will learn programming by solving problems
faced by a robot living in a small grid-like world.
Our robot is capable of only four basic actions:
moving one step forward, turning left, picking up
and putting down beepers. Through the magic of
programming, you will learn to have him combine
those four basic actions in order to perform very
complicated tasks.
Before we learn how to do all this, let’s have a We can now start giving instructions to the
look at the robot’s world. Open a Python inter- robot. In Python, you give instructions to a robot
preter window, and type in this code: by writing the robot’s name, a dot, and the com-
mand:
from cs1robots import * hubo.move()
create_world()
And immediately hubo will move one step forward:
A new window will open showing a new robot
world, like this:

Let’s try a different instruction:

hubo.turn_left()

Our robot will turn to face northwards:


This robot world consists of 10 vertical avenues
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()

A robot will appear at the intersection of avenue 1


and street 1:

∗ These lecture notes are edited versions of the RUR- You can now control your robot by entering com-
PLE lessons by André Roberge, which can be found at mands one by one—the robot executes each com-
http://rur-ple.sourceforge.net/. mand as soon as you have entered it.

1
2 First program
from cs1robots import *
But controlling a robot by typing in commands on create_world()
a keyboard is not programming. “Programming” a hubo = Robot()
robot means to design a whole sequence of instruc- hubo.set_trace("blue")
tions (an algorithm) for the robot, which is then hubo.set_pause(2)
executed automatically by the robot to solve some ...
task.
Let’s try this: Type the following text and save The number in parentheses is the length of the
it as a file square.py: break in seconds.

from cs1robots import * 3 Bugs!


create_world()
hubo = Robot() What happens when you mistype a command:
hubo.move()
hubo.turn_left() hubo.Move()
hubo.move()
hubo.turn_left() (Yes, Python distinguishes lower-case and upper-
hubo.move() case letters.) Make sure you try it to see exactly
hubo.turn_left() what happens. In the future, if you see the same
hubo.move() kind of complaint, it might help you discover what’s
hubo.turn_left() wrong with your program. In other words, you will
find how to recognize one kind of bug.
The following program demonstrates a different
Let your Python interpreter execute this code. A bug:
robot world window will open, and you will see the
robot walking around a little square. Everything from cs1robots import *
will happen quite fast, however, and it will be hard create_world()
to see what is going on. hubo = Robot()
The first thing we can do is to turn on a trace for hubo.move()
our robot, as follows: hubo.turn_left()
hubo.turn_left()
hubo.turn_left()
from cs1robots import *
hubo.move()
create_world()
hubo = Robot()
hubo.set_trace("blue") What happens? Robots, like humans, cannot walk
... through walls. If you make him hit a wall, your
program terminates.

This is much better already, as we can see precisely


4 Write programs for humans
what the robot did:
Beginning programmers tend to think about pro-
gramming as talking to the computer, and feel
happy as long as the computer understands them.
You should, however, strive to write programs that
make it easy for other people to read your program
and understand what it does. This takes a lot of
practice and usually requires a fair bit of thinking.
In the end, such programs are more likely to be cor-
rect, and can be maintained much more easily. (In
practice, programs are never finished—there always
are new features that need to be added or behavior
that needs to be changed.)
If you also want to be able to observe the robot’s We can use a trick of writing notes meant for
movement in more detail, we can ask him to make other humans only (and not for the computer) in-
a little break after each move: side the program. We call these notes comments.

2
There are a few ways to write comments in a pro-
gram. I’m going to teach you the simplest way used
in Python: 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

5 Turning left and turning right It becomes quite tedious to write and read this
code. This is because we repeat ourselves; in other
We already saw that our robot can turn left, as long
words, the same sequence of instructions appears
as it is by exactly 90 degrees. There is no other
in many different places in the program. One basic
steering mechanism—our robots are rather cheap.
rule of programming is: Do not repeat yourself !
In Python, one can give a simple name to a se-
Task: (Square) Write a program that makes hubo ries of instructions. For example, we can define a
walk around a bigger square of size two, like this: turn_right command:
from cs1robots import *
create_world()
hubo = Robot()
hubo.turn_left()

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.)

Task: (ZigZag1) Write a program zigzag.py that


makes your robot visit the entire world in a zigzag 7 Worlds
fashion:
So far, our robot has lived in a rather boring, empty
world. We can let him play in more interesting
worlds containing walls and beepers, by loading
such a world instead of the create_world() call:
from cs1robots import *
load_world("worlds/hurdles1.wld")
hubo = Robot()

(You can also call load_world() without giving a


filename. Then Python will let you choose a file
when the program is run.)

Task: (Hurdles1) Hubo has entered a hurdles


race, with obstaces as in the world file hurdles1.wld.
Write a program that have him follow the path in-
Use functions to avoid repetitions and to make dicated below in his way to the finish line. He has
your code as clear as possible. finished when he has picked up the beeper.

6 Beepers
Beepers are small objects (drawn fairly big on the
screen) that make a small beeping sound when they Avoid repetition by defining useful functions.
are turned on. A robot can only hear a beeper if Maybe you would rather like to build a more
he is standing right on top of them, on the same interesting world. You can do that using the
corner. He can pick them up and carry them in his edit_world() function:
pockets, or he can put them down.
You can instruct the robot to pick up a beeper from cs1robots import *
through the pick_beeper() command, or to put create_world(avenues = 20, streets = 5)
some down, through the drop_beeper() command. edit_world()
If you ask a robot to pick up beepers where there save_world("myworld.wld")
are none, or to put down beepers if he doesn’t carry
any, you will get an error message and your program The edit_world function places your world in an
will terminate. editable state. You can click on the world to add
The following small program will drop a beeper and remove walls. If you click with the left mouse
at the intersection of avenue 3 and street 1: button on an intersection, a beeper is placed there.
from cs1robots import * You can remove beepers with the right mouse but-
create_world() ton. Press the Return key when you are done edit-
hubo = Robot(beepers = 1) ing. Your program will then continue and save your
hubo.set_trace("blue") world. (Again, you can call save_world() without
giving a filename. Then Python will let you choose
hubo.move()
a file when the program is run.)
hubo.move()
hubo.drop_beeper() Note that you can only create or load a world
hubo.move() once in a Python program or from the Python in-
hubo.move() terpreter. To load another world, you have to rerun
your program, or restart the interpreter.

4
8 Top-down design This approach to solving a given problem is called
top-down design, because we started at the top of
Let’s solve the following problem: Hubo delivers
the problem, with an outline for a solution, and
newspapers in his local neighborhood. Have him
then focused on each part of the solution one-by-
climb the stairs to the front door of a house, drop
one.
the newspaper (represented by a beeper) on the top
step, and return to his starting point as illustrated
below. The world file is newspaper.wld : Task: (Harvest1) It’s harvest time! Have the
robot pick up all the carrots (represented by beep-
ers) in this garden. The world file is harvest1.wld.
Use top-down design.

If we just type in commands to accomplish this


task, we would end up with a sequence of more than
50 commands. This requires a fair bit of typing—
and the more typing one does, the more errors can
occur!
A better way to approach such a problem is to
start with an outline:
• Climb (up) four stairs.
• Drop the newspaper.
• Turn around.
9 Repetitions
• Climb (down) four stairs.
Let’s write this outline in a Pythonic form: We learnt the rule: Do not repeat yourself ! We
used functions for sequences of instructions that our
climb_up_four_stairs() robot needs to use repeatedly. But if we look at this
hubo.drop_beeper() function:
turn_around()
def turn_right():
climb_down_four_stairs()
hubo.turn_left()
hubo.turn_left()
This is easy to read, and instead of one large prob- hubo.turn_left()
lem we now need to solve a few small problems:
How to climb up four stairs? We can decompose It still contains a lot of repetition: The same in-
these smaller problems even further: struction is used three times. We can rewrite this
using a for-loop:
def climb_up_one_stair():
hubo.turn_left() def turn_right():
hubo.move() for i in range(3):
turn_right() hubo.turn_left()
hubo.move()
hubo.move()
We will see later what this means exactly,
but for the moment it’s enough to know that
def climb_up_four_stairs():
hubo.turn_left() will be repeated exactly three
climb_up_one_stair()
times.
climb_up_one_stair()
climb_up_one_stair()
climb_up_one_stair() Task: (Hurdles2) Redo the Hurdles1 task above
using for-loops.

Task: (Newspaper1) Finish this program by writ- Task: (Harvest2) Redo the Harvest1 task above
ing functions to perform all the smaller tasks. using for-loops.

5
Task: (Harvest3) Some problem as Harvest2, but • The colon (:) precedes the series of instructions
for the harvest2.wld world file: that the robot must follow if the condition is
true;
• 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:

and we want the final position to be:

10 If only the robot could decide. . .


How would we solve the harvesting problem on a
world like this? (The world file is harvest3.wld.)
So, we want to:
• Take a step forward;
• Check to see if there is a beeper;
• Pick up the beeper if there is one; otherwise
ignore and keep going;
repeating the above steps 9 times. Here is how we
can do this:
def move_and_pick():
hubo.move()
if hubo.on_beeper():
hubo.pick_beeper()

for i in range(9):
move_and_pick()

It would be nice if we could just run the progrem Task: (Harvest4) Modify your program for the
we wrote in task Harvest2. But it doesn’t work, Harvest2 task so that it works for the world har-
because calling pick_beeper() when there is no vest3.wld. Note that the new program should also
beeper causes an error. automatically work for the original harvest1.wld
If only hubo could check if there is a beeper, and world. Try it!
pick it up only when a beeper is really there. . . To be able to harvest in the fall, you have to
The Python keyword if does exactly this: put seeds in the ground in the spring. Can we
write a program that will put seed potatoes in the
if hubo.on_beeper(): ground, but skip any spot where there already is
hubo.pick_beeper() a potato? In other words, starting with the world
harvest3.wld, we should end up with the world of
Let’s look at the meaning of the above code: harvest1.wld !
• The instruction if implies that some condi- To do this, hubo needs to drop a beeper if there
tion, whose value is true or false, is going to is no beeper at the current location. The following
follow; code will do this:
• hubo.on_beeper() is a condition (or test) if not hubo.on_beeper():
which is true if hubo is on top of a beeper and hubo.drop_beeper()
false otherwise;

6
The Python word not inverts the sense
of the condition: If hubo.on_beeper() from cs1robots import *
is true, then not hubo.on_beeper() is create_world(avenues = 5, streets = 5)
false. If hubo.on_beeper() is false, then
not hubo.on_beeper() is true. This is the hubo = Robot()
simplest example of a Python expression. hubo.set_trace("blue")

def move_or_turn():
Task: (Plant1) Write a program that will plant if hubo.front_is_clear():
potatoes so that the field will look like harvest1.wld hubo.move()
at the end. It should skip any spot where there else:
already is a potato. (Note that you have to cre- hubo.turn_left()
ate your robot with sufficiently many beepers by
using hubo = Robot(beepers=36). Try your pro- for i in range(20):
gram with an empty world and with the worlds har- move_or_turn()
vest1.wld and harvest3.wld.
On a small world this gives the following end result:

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:

hubo.front_is_clear()
hubo.left_is_clear()
We can make this more interesting by doing a
hubo.right_is_clear()
“dance” if we can move forward, and dropping a
beeper if we have to turn. The following program
Let’s use the first one to have the robot explore his does just that, going only partly around the world:
world. We will have him follow the boundary of
his world by asking him to move forward, if there hubo = Robot(beepers = 10)
is no wall in front of him, and turn left otherwise.
The following simple program is the basis of what def dance():
is needed: for i in range(4):
hubo.turn_left()
if hubo.front_is_clear():
def move_or_turn():
hubo.move()
if hubo.front_is_clear():
else:
dance()
hubo.turn_left()
hubo.move()
else:
We learnt a new Python keyword here: else. It hubo.turn_left()
introduces an alternative sequence of instructions. hubo.drop_beeper()
In this example, if there is no wall in front of hubo,
then hubo.front_is_clear() is true, and the se- for i in range(18):
quence after the if line is executed. If there is move_or_turn()
a wall, then hubo.front_is_clear() is false, and
the sequence after the else line is executed. Do Notice how the instructions dance() and
not forget the colon after else, and watch out for hubo.move() are aligned after the if statement
the correct indentation! and indented from it, indicating that they belong
Let’s repeat this simple conditional instruction in the same block of instructions. The instructions
many times to go around the world: hubo.turn_left() and hubo.drop_beeper()

7
are similarly aligned, indented from the else
statement to which they belong. The result of def move_or_turn():
running the program is indicated below. if hubo.front_is_clear():
dance()
hubo.move()
else:
hubo.turn_left()
hubo.drop_beeper()

Now, hubo.drop_beeper() no longer belongs to


the definition of the function move_or_turn at all,
as it is not indented to be aligned with other in-
structions within the definition. It is a single in-
struction, the first in fact that the robot must fol-
low, before executing the move_or_turn() function
18 times. The result is the following:
Now, what happens if we do not align
the instruction hubo.drop_beeper() with
hubo.turn_left(), but align it instead with
the else statement, as indicated below?

def move_or_turn():
if hubo.front_is_clear():
dance()
hubo.move()
else:
hubo.turn_left()
hubo.drop_beeper() So, as you can see, much information is given
through blank spaces (that is, the indentation of the
instructions within blocks). Through practice, you
Now, the definition of move_or_turn() includes a will learn to use this to your advantage and realise
choice resulting in either a dance and a move for- that Python allows you to write very readable code
ward, or a left turn, followed every time by the in- by indenting instructions.
struction hubo.drop_beeper(). The result of run- Our robot has become quite good at jumping
ning this program is indicated below: hurdles. He now enters races of different lengths:
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 :

As you can see, after every step forward, a beeper


has been put down. Each corner now has two beep-
ers: one from the previous move to reach the corner,
and one for the left turn after reaching the corner.
Now, suppose we align the hubo.drop_beeper() Task: (Hurdles3) Assume that there are no races
instruction with the def statement as indicated be- longer than 20 units. Define a function that looks
low? somewhat like the following:

8
If we have more than three choices, all we need
def move_jump_or_finish(): to do is add other elif statements
# test for end of race
if not hubo.on_beeper(): def move_jump_or_finish():
# is there a hurdle? # test for end of race
if hubo.front_is_clear(): if hubo.on_beeper():
hubo.move() pass # race is over - do nothing
else: # is there a hurdle?
jump_one_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. . .
The previous hurdles task required you to write
an if/else within another one, all this because we
13 For a while. . .
wanted to give three choices to the robot: finish, When we want to repeat some instructions until a
move or jump. You may have noticed that this certain condition is satisfied, Python gives us a sim-
forced us to indent the code further and further. pler way to write this using a new keyword: while.
Imagine what would happen if we wanted to give Let me show you first how this would look using
10 mutually exclusive choices; the resulting code pseudocode:
would become hard to read. To help us in such
situations, Python offers a keyword that represents While not on beeper,
the combination of an else statement followed by ... keep moving;
an if clause. That keyword is elif, which we can otherwise,
think of as an abbreviation for else if. With this ... stop.
new keyword, the above code can be written as:

def move_jump_or_finish(): You should agree that this expresses the same idea
# test for end of race as before. Using Python code, here is how we would
if hubo.on_beeper(): actually write it:
pass # race is over - do nothing
# is there a hurdle? while not hubo.on_beeper():
elif hubo.front_is_clear(): hubo.move()
hubo.move()
else:
jump_one_hurdle() No more need for a for-loop with a fixed number of
repetitions.
We can now better see, as they are indented the
same way, that there are three possible choices. The Task: (Hurdles4) Use a while loop to rewrite the
else condition is executed only if all the previous hurdles3 program so that you don’t have to use a
conditions are false, so there is no condition associ- for-loop of fixed length. In other words, the core of
ated with it. your program should look like the following:
The keyword pass which we introduced here is
Python’s way of saying “do nothing.” It is neces- while not hubo.on_beeper():
sary, because it is not possible to write an empty se- move_or_jump()
quence of instructions (an empty block ) in Python.

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()

This works well on an empty world, but here is an


example where it does not work:

Task: (ZigZag2) Rewrite your program for


ZigZag1 so that the robot can visit an empty world
of any size in zigzag fashion. Make sure that it
works for even and odd numbers of streets and av-
enues. (You can assume that there are at least two
streets and at least two avenues.)
The problem is that we assumed that we only
14 Around the world in 80 days had to move forward or turn left to go around the
world; we never took into account situations where
Let’s start by considering a simple problem: go we would have wanted to make a right turn. What
around the world once, assuming there is no ob- we need to do is first to check on the right side to see
stacle on the way. We have done this before, when if there is still a wall; if not, we have to make a right
we introduced the front_is_clear() test. Here’s turn. Here’s a modified program that attempts to
the outline of a solution which supposes that we do just that:
carry at least one beeper at the beginning:
1. Put down a beeper to mark the starting point. def turn_right():
2. Move forward until facing a wall. for i in range(3):
3. Turn left when facing a wall. hubo.turn_left()
4. Repeat steps 2 and 3 until we find the beeper
we had put down. hubo.drop_beeper()
5. Finish when finding the beeper. hubo.move()
The key step is 4, where we have a repeating in- while not hubo.on_beeper():
struction with a test. Let’s now translate the entire if hubo.right_is_clear():
solution in proper code: turn_right()
elif hubo.front_is_clear():
hubo.drop_beeper() hubo.move()
while not hubo.on_beeper(): else:
if hubo.front_is_clear(): hubo.turn_left()
hubo.move()
else: This still doesn’t work. The robot gets in an infinite
hubo.turn_left() loop when there is no wall around him.

This does not work—the program terminates im- Task: (InfiniteLoop) Create a world where this
mediately after dropping the beeper. The problem program fails.
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, before we forget, is to allow the robot to
if hubo.right_is_clear():
explore his world, going around once. While the
turn_right()
program is rather short, and its structure should
hubo.move()
be clear at this point, it might not be so obvious to
elif hubo.front_is_clear():
someone who just happened to see it for the first
hubo.move()
time.
else:
We discussed before that our goal should be to
hubo.turn_left()
write programs so that they can be understood
by a human. It’s probably a good idea either to
But this still does not always work: add comments and/or to introduce more meaning-
ful words. Let’s start by adding comments, some-
what more verbose than we think we might need.

# Define function for turning right


def turn_right():
for i in range(3):
hubo.turn_left()

# Mark the starting point with a beeper


hubo.drop_beeper()

# Find a clear direction and start moving


while not hubo.front_is_clear():
hubo.turn_left()
hubo.move()
Do you see why? We were too hasty in moving
forward after putting down a beeper. We need to
# We know we have gone around the world
check if there is a wall preventing us from moving
# when we come back to the beeper.
first. Here’s a solution to the problem:
while not hubo.on_beeper():
hubo.drop_beeper() if hubo.right_is_clear():
if not hubo.front_is_clear(): # Keep to the right
hubo.turn_left() turn_right()
hubo.move() hubo.move()
while not hubo.on_beeper(): elif hubo.front_is_clear():
# continue as before # move following the right wall
hubo.move()
Can you imagine situations where this does not else:
work? Well, here is one (try it!): # follow the wall
hubo.turn_left()

While this sort of clarifies our intent for each in-


struction, it is not really that helpful in summariz-
ing the method (also known as the algorithm) used
in solving the problem. Therefore, these comments
might not be as helpful to another reader as we
might have wished. Reading over the comments,
we note that the program has two parts:
1. mark the starting point;
2. follow the right wall until we come back to the
To make it work, we need to replace the if we start.
just added by a while. Try it! Let’s rewrite this program so that these two parts

11
become clearer, and writing the comments differ- hurdle (world file hurdles4.wld ) illustrated below—
ently. which the program we wrote before for jumping
over hurdles could not handle!
# This program lets the robot go
# around his world counterclockwise,
# stopping when he comes back to his
starting point.

def turn_right():
for i in range(3):
hubo.turn_left() Even better, the same modified program can find
the exit to a maze (world file maze1.wld ):
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
hubo.turn_left()
17 Stepwise refinement
# end of definitions, begin solution We started with a simple problem to solve (going
around a rectangular world) and, by improving lit-
mark_starting_point_and_move() tle by little (also called stepwise refinement), we
manage to write a program that could be used to
while not hubo.on_beeper(): solve many other apparently unrelated problems.
follow_right_wall() At each step, we kept the changes small, and made
sure we had a working solution, before considering
Isn’t this clearer? This will also make it much easier more complex problems. We also used more de-
for us to change (“maintain”) the program if we scriptive names for parts of the algorithm which
want to change its behaviour in the future. made the program easier to read and, hopefully, to
understand. This is a strategy you should use when
writing your own programs:
16 Hurdles and mazes Steps to follow when writing a program:
Change the program you just wrote to remove the • start simple;
drop_beeper() instruction. After saving it, try • introduce small changes, one at a time;
this slightly modified program with the following • make sure that each of the changes you have
hurdle race (world file hurdles3.wld ): introduced do not invalidate the work you have
done before;
• add appropriate comments that don’t simply
repeat what each instruction does; and
• choose descriptive names.

Surprise! Other than finishing facing in an un-


usual direction (perhaps bowing to the crowd af-
18 It’s raining!
ter winning), the program we just wrote can solve It was a beautifully sunny day. Hubo is playing
the hurdle problem. It also works with the uneven outside with his friends. Suddenly, it started to

12
rain and he remembered that the windows in his Verify to see if the program you wrote to help
house were all open. So he went back to his house Ami close the windows in her house can be used by
and stopped in front of the door, unsure of how to Hubo to do the same for his house. If not, change
proceed. it so that it works for both Ami and Hubo.

19 After the storm


The wind blew really hard last night. There is lit-
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
Task: (Rain1) Help Hubo close the windows of illustrated below.
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
to stop before he can go back and play outside, as
illustrated below.

Task: (Trash1) Hubo should collect all the litter,


and put it in the garbage can, situated north of his
starting point. The final situation should look like
the following:

The world file is rain1.wld ; make sure Hubo car-


ries more than enough beepers to accomplish his
task.

Task: (Rain2) Ami, Hubo’s friend, lives in a big- Important: To put all the trash in one pile, you
ger house. Ami was playing outside with Hubo will need to use the test hubo.carries_beepers()
when it started raining. Help Ami close the win- which I hadn’t told you about ... yet! Try some-
dows in her house. The starting position is: thing like

while hubo.carries_beepers():
# do something

Make sure your program works for both world files:


trash1.wld and trash2.wld.

The corresponding world file is rain2.wld.


When Ami is finished, she too will be standing Task: (Trash2) Hubo’s parents are so proud of his
in the doorway of her house as illustrated below. work, that they ask him to pick up all the garbage
that got blown away in their backyard during the
windstorm. Have him pick up all the garbage and
bring it back with him to his starting position. Try
to generalise from the program you just wrote to
clean up the driveway.
Create your own world file, corresponding to a
situation like the one illustrated below. Your so-
This problem is a bit more tricky to solve. Per- lution should not depend on the exact location of
haps you should consider the possibility that Ami the garbage, nor should it depend on the size of the
will need to step back sometimes... yard!

13
We have two robots now—the light blue one
is hubo, the green one is ami (available colors
are gray, green, yellow, purple, blue, and
20 The robot’s compass light_blue).
If you run this interactively in the Python inter-
Our robot has a built-in compass, which allows him preter, you can now control both robots manually:
to tell if he is facing in the north direction or not. When you say hubo.move(), Hubo makes a step.
You can use this test in an if or while statement: When you say ami.move(), then Ami makes a step.
Each robot is a Python object, and by call-
if hubo.facing_north(): ing methods of the object using the dot-notation
# do something you can inquire about the state of the object
(as in ami.on_beeper()), or ask the object to
perform some action and change its state (as in
hubo.turn_left()).
Task: (Return) Write a program that will allow Starting with the situation above, we can now
Hubo to return to his usual starting position (av- write a small race:
enue 1, street 1, facing east), starting from any po-
sition and orientation in an empty world. You can for i in range(8):
create robots with a given position and orientation hubo.move()
like this: ami.move()

hubo = Robot(orientation = "W",


This is not very exciting as they are exactly equally
avenue = 3, street = 5)
fast - we’ll see later how we can change that.

22 Functions with parameters


21 Hubo’s family
Earlier, we defined a function turn_right(). It
Let’s have a look at the following code: still works—it will make hubo turn right. But now
we have two robots—how do we make ami turn
from cs1robots import * right? Should we define a new function? And then
again another one for every robot we make?
create_world() What we need now is a function that can work
hubo = Robot("light_blue") for any robot:
hubo.turn_left()
hubo.move() def turn_right(robot):
hubo.turn_left() for i in range(3):
hubo.turn_left() robot.turn_left()
hubo.turn_left()
The word robot here is a function parameter.
ami = Robot("green") When we call the function, we have to provide a
ami.move() robot as a function argument:
hubo.move()
turn_right(ami)
turn_right(hubo)
The result looks like this:

14
The function call turn_right(ami) executes the
function turn_right with the parameter robot set
to refer to the robot ami.

Task: (Trash3) Rewrite your program for task


Trash2 so that Hubo and Ami work together. They
both start at the normal starting position, and
should work simultaneously to collect the garbage.
You can decide how to split the work: For instance,
Ami could do the northern half of the yard, and
Hubo the southern half. Or they can walk next to
each other, picking up garbage in neighboring rows.

15

You might also like