3.4. Working with Turtles
Dr. Seymour Papert, at MIT, used robot turtles in the late 1960s to help children think about how to specify a procedure. The turtle had a pen in the middle of it that could be raised and lowered to leave a trail of its movements. As graphical displays became available he used a virtual turtle on a computer screen.
We are going to work with some turtle objects that move around a world. The turtles know how to move forward, turn left, turn right, and turn by some specified angle. The turtles have a pen in the middle of them that leaves a trail to show their movements. The world keeps track of the turtles that are in it.
3.4.1. Defining Classes
How does the computer know what we mean by a world and a turtle? We have to define what a world is, what it knows about, and what it can do. We have to define what a turtle is, what it knows about, and what it can do. We do this by writing class definitions for World and Turtle. In Java each new class is usually defined in a file with the same name as the class and an extension of ".java". Class names start with a capital letter and the first letter of each additional word is capitalized. So we define the class Turtle in the file Turtle.java. We define the class World in the file World.java. The class Turtle inherits from a class called SimpleTurtle (notice that the first letter of each additional word is capitalized). We have defined these classes for you so that you can practice creating and sending messages to objects.
3.4.2. Creating Objects
Object-oriented programs consist of objects. But how do we create those objects? The class knows what each object of that class needs to keep track of and what it should be able to do, so the class creates the objects of that class. You can think of a class as an object factory. The factory can create many objects. A class is also like a cookie cutter. You can make many cookies from one cookie cutter and they will all have the same shape. Or you can think of the class as a blueprint and the objects as the houses that you can create from the blueprint.
To create and initialize an object use new Class(parameterList) where the parameter list is a list of items used to initialize the new object. This asks the object that defines the class to reserve space in memory for the data that an object of that class needs to keep track of and also keep a reference to the object that defines the class. The new object's data will be initialized based on the items passed in the parameter list. There can be several ways to initialize a new object and which one you are using depends on the order and types of things in the parameter list.
One way to create an object of the class World is to use new World(). We don't have to pass any parameters to initialize the new world. Objects can have default values.
> System.out.println(new World()); A 640 by 480 world with 0 turtles in it.
When you type the above in the interactions pane you will see a window appear with the title "World", as shown in Figure 3.1. We have created an object of the World class which has a width of 640 and a height of 480. The world doesn't have any turtles in it yet. We would like to add a turtle to this world, but we have a problem. We don't have any way to refer to this World object. We didn't declare a variable that refers to that object in memory, so it will just be garbage collected after you close the window. Go ahead and close the window and let's try again, but this time we will declare a variable to let us refer to the World object again.
Figure 3.1. A window that shows a World object.
When we declare a variable we are associating a name with the memory location so that we can access it again using it's name. To declare a variable in Java you must give the type of the variable and a name for it:
The Type is the name of the class if you are creating a variable that refers to an object. So to create a variable that will refer to a World object we need to say the type is World and give it a name. The first word in the variable name should be lowercase but the first letter of each additional word should be uppercase. The name should describe what the variable represents. So, let's declare a variable that refers to an object of the class World using the name worldObj.
> World worldObj = new World(); > System.out.println(worldObj); A 640 by 480 world with 0 turtles in it.
This says to create a variable with the name of worldObj that will be of type World (will refer to an object of the class World). It will refer to the object created by the World class because of the code: new World(). We can use System.out.println(worldObj) to ask the new World object to print out some information about itself.
To create a turtle object in this world we will again use:
This time we will ask the Turtle class to create the object in our World by passing a reference to the world to create it in. We will declare a variable so that we can refer to the Turtle object again.
> Turtle turtle1 = new Turtle(worldObj); > System.out.println(turtle1); No name turtle at 320, 240 heading 0.
Now a Turtle object appears in the middle of the World object, as shown in Figure 3.2. This turtle hasn't been assigned a name and has a location of (320,240) and a heading of 0 which is north. The default location for a new turtle is the middle of the World object. The default heading is 0 (north).
Figure 3.2. A window that shows a Turtle object in a World object.
We can create another Turtle object and this time we can say what location we want it to appear at. To do this we need to pass more than one parameter in the parameter list of items used to initialize the new object. To do this separate the values with commas.
> Turtle turtle2 = new Turtle(30,50,worldObj); > System.out.println(turtle2); No name turtle at 30, 50 heading 0.
Notice that the second turtle appears at the specified location (30,50) as shown in Figure 3.3. The top left of the window is location (0,0). The x values increase going to the right and the y values increase going down.
Figure 3.3. A window that shows two Turtle objects in a World object.
3.4.3. Sending Messages to Objects
We have been talking about executing or invoking methods on classes and objects. A more object-oriented way of saying that is that we send messages to objects to ask them to do things. The full syntax for sending a message is
The objectReference is a reference to an object, message is what we want the object to do, and parameterList is any additional information that more fully describes what we want the object to do. The '.' and '()' are required even if there is no parameter list.
Turtles know how to go forward, turn left, turn right, turn by a specified angle, change their color, and set their names. So if we want turtle1 to go forward 20 steps we would use turtle1.forward(20);. If we want it to turn left we would use turtle1.turnLeft();. If we want it to turn right, we would use turtle1.turnRight();. If we want it to turn by an angle to the left by 45 degrees, we would use turtle1.turn(-45);. To turn turtle1 to the right 45 degrees, use turtle1.turn(45);. Negative angles turn to the left, and positive angles turn that amount to the right.
We actually don't need to use System.out.println(); every time we ask the computer to do something. If we want to call a method that doesn't return anything we can just ask the method to be executed by typing the variable name for the object followed by a '.' and then the method name and its input (if any) in parentheses followed by a semicolon.
> turtle1.forward(20); > turtle1.turnLeft(); > turtle1.forward(30); > turtle1.turnRight(); > turtle1.forward(40); > turtle1.turn(-45); > turtle1.forward(30); > turtle1.turn(90); > turtle1.forward(20);
In Figure 3.4 we see the trail of the first turtle's movements. Notice that all of the messages were sent to the first Turtle object that is referenced by the turtle1 variable. The messages only get sent to that object. Notice that the second Turtle object didn't move. It didn't get any messages yet. To send a message to the second Turtle object, we use the variable name that refers to that Turtle object which is turtle2.
> turtle2.turnRight(); > turtle2.forward(200); > turtle2.turnRight(); > turtle2.forward(200);
Figure 3.4. The result of messages to the first Turtle object.
In Figure 3.5 we see the trail of the second turtle's movement. Can you draw a square with a turtle? Can you draw a triangle with a turtle? Can you draw a pentagon with a turtle? How about a circle?
Figure 3.5. The result of messages to the second Turtle object.
3.4.4. Objects Control Their State
In object-oriented programming we ask an object to doing something by sending it a message. The object can refuse to do what you ask it to do. Why would an object refuse? An object should refuse when you ask it to do something that would cause its data to be wrong. The world that the turtles are in is 640 by 480. Try asking the Turtle object to go forward past the end of the world. What happens? First click the Reset button to reset the interactions pane. When you reset the interactions pane you get rid of any currently declared variables. Then create a new World and Turtle.
> World world1 = new World(); > Turtle turtle1 = new Turtle(world1); > System.out.println(turtle1); No name turtle at 320, 240 heading 0. > turtle1.turnRight(); > turtle1.forward(400); > System.out.println(turtle1); No name turtle at 639, 240 heading 90. > System.out.println(world1.getWidth()); 640
Remember that Turtle objects are first created in the middle of the world (320, 240) facing the top of the world. When the turtle turned right it was facing the right side of the window. If the turtle went forward 400 steps, it would be past the right edge of the window (320 + 400 = 720) because the x values increase to the right. Notice that the turtle stops when the middle of it reaches the limit of the window (639) as shown in Figure 3.6. This means your turtle will always have at least part of it in the world.
Figure 3.6. The turtle won't leave the world.