CS 124, Spring 2013
Lab 7: Turtle Subroutines

Once again this week, you will be working with subroutines. The lab uses "turtle graphics." This is an old idea in which pictures are drawn by a "turtle" that drags a pen and so leaves a line as it moves. The turtle obeys commands such as "forward 5", which moves the turtle forward five units, and "turn 90", which tells the turtle to rotate counterclockwise by 90 degrees. In this program, the turtle is represented by a triangle in a window on the screen. (In the original conception, the turtle was supposed to be a turtle-like robot drawing on a large sheet of paper.)

You should begin by creating an Eclipse project named lab7. You will need the three files from the directory /classes/cs124/lab7-files. Copy them into the src folder of your Eclipse project. Remember that they have to be in the src folder, not just in the project! The files are: TurtleGraphics.java, TurtlePanel.java, and TextIO.java.

This lab is due next Thursday. Ideally, you will make a new folder named lab7 inside your homework folder, and just copy the .java files from your project into that folder. However, it is also acceptable to copy just the entire lab7 project folder from your Eclipse workspace folder into the homework folder.

About the Turtle

In this program, the turtle is represented by a gray triangle, which is somewhat transparant so that any drawing behind it will show through. The turtle moves around in a square area that is 20-by-20 units on a side, with the point (0,0) at the center, so that the turtle's coordinates can range from -10 to 10. There is nothing to stop the turtle from moving beyond this range, out of the window. These are not pixel coordinates; the turtle uses real number coordinates, where one unit is 20 pixels in an 800-by-800 pixel window.

The turtle is always facing in some direction, the direction is which the gray triangle is pointing. Initially, the turtle faces to the right. And initially, the turtle is set up to draw using a thin, red line. However, both the width and the color of the lines can be changed.

In the program, the turtle is represented by a static member variable named turtle. You can use this variable in any subroutine to give commands to the turtle. Here are the commands that it understands:

Exercise: Writing Subroutines for the Turtle

The abilities of the turtle are actually defined in the file TurtlePanel.java. However, the turtle is used in TurtleGraphics.java, and you will only have to work on TurtleGraphics.java for this lab. The current main() routine in TurtleGraphics.java calls createTurtle(), which opens the turtle window on the screen, and it has the turtle draw a square as a demonstration. You will have to add some subroutines to TurtleGraphics.java, and you will have to modify main(). However, you should always leave the createTurtle() command at the beginning of main().

Although the turtle already has some built-in subroutines, there are other things that you might want it to do, such as draw a square. You will write some subroutines in TurtleGraphics.java that make the turtle do more complex tasks. For example, you should add a subroutine

static void square( double side )

that draws a square, where the length of the side of the square is given by the parameter side. For the body of the subroutine, you can use the square-drawing code that is currently in main(), except that you have to modify it slightly to use side. You could then use the subroutine in the main program with a command such as

square(7);

or even with something as complex as

int i;
for ( i = 0; i < 90; i++) {
   turtle.randomColor();
   square(7);
   turtle.turn(4);
}
turtle.setTurtleIsVisible(false);

which draws a rather nice picture. You should write the following static void subroutines:

Exercise: Making it Interactive

In the final version of your program, the user of the program should control the turtle by typing in commands. (This was demonstrated in class.) You should use the following main() routine or something very like it in your program:

public static void main(String[] args) {
    
    createTurtle();  // Create the turtle object, and show its window.

    System.out.println("Type in your commands for the turtle!");
    System.out.println();
    while (true) {
        try {
            System.out.print(">>> ");
            doUserCommand();
        }
        catch (Exception e) {
            System.out.println("Error in input: " + e.getMessage());
        }
        TextIO.getln();  // Discard the rest of the line of input.
    }
            
} // end of main()

You need to write the subroutine doUserCommand(). This subroutine should read one command from the user and implement it. A command is a single word, and it can be followed by the numbers that are needed as parameters to the command. You need to figure out what the command is and translate it into a call to one of the subroutines that you have written or to one of the built-in subroutines of the turtle.

For example, if the user enters forward 5, Your program should call turtle.forward(x) where the value of x has been set to the user's input, 5. If the user says poly 2 7, you might translate that into a subroutine call such as regularPolygon(x,n).

This is not very complicated. You should read the user's command using the TextIO function TextIO.getWord(). This subroutine reads one word, consisting of non-blank characters, stopping at the first blank or end-of-line. Note that it does not discard the rest of the line. (TextIO.getlnWord() would do that.) You need to keep the rest of the line around so that you can read any numbers that are on the same line. Once you have the command, you can decipher it with tests such as command.equals("forward").

Note that my main routine catches exceptions. Your subroutine should throw an IllegalArgumentException if the user enters a command that you have not implemented. This will cause the main routine to print an error message.

The problem of reading in numbers raises the issue that TextIO's method of handing bad input for numbers is not very clean. In this program, it would be better to just give an error message when the user enters a bad value for a number. For example, the program could use TextIO.getWord() to read the supposed number, and then transform it into a numerical value. Here is a subroutine that you can use to do this for numbers of type double:

static private double readDouble() {
    String num = TextIO.getWord();
    try {
        return Double.parseDouble(num);
    }
    catch (NumberFormatException e) {
        throw new IllegalArgumentException(
                    "Expected a real number but found '" + num + "'");
    }
}

(I used the try..catch here so that I could throw a more meaningful exception. The error message in the NumberFormatException didn't tell the user exactly what was wrong.)

You can write a similar subroutine for reading integers from user input. With these subroutines, the code that you need to implement a user command can usually be reduced to one line. For example, I implemented a "color" command with the subroutine call

turtle.color( new Color( readInt(), readInt(), readInt() ));

In your program, there should be a user command for each of the five subroutines that you wrote. You should also provide user commands for at least the basic built-in turtle subroutines: forward, turn, clear, home. And you should implement the command exit to end the program, by calling System.exit(0). You might also implement other built-in commands as user commands; for example, commands for setting the turtle color and line width, raising and lowering the pen, hiding and showing the turtle, and setting the delay time between turtle actions.

Bonus: An Example of Recursion

I originally wrote the TurtlePanel class for a lab last year in Intermediate Programming. That lab was about recursion. It is possible for a subroutine to call itself. That is, inside the subroutine, there is a call to the same subroutine. This is usually considered to be an advanced technique, but you can get some interesting results by using recursion with turtle graphics. As an example, I include a subroutine koch(distance,recursionLevel) in TurtleGraphics.java. Calling koch(x,r) has the same net effect as turtle.forward(x); that is, the turtle moves forward x units. However, the turtle gets there with multilevel "detours". (The true Koch curve is a fractal that is infinitely bumpy. The fractal is named after the mathematician who discovered it.)

You might want to use koch as a command in your program. If not, you can just ignore it or delete it. If you would like to see some other pictures drawn with recursion and turtle graphics, see Lab 3 from last Spring's CS 225.