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:
turtle.forward(double d)
-- move forward d units in the current direction.turtle.back(double d)
-- move backwards d units.turtle.moveTo(double x, double y)
-- move directly to the point (x,y).turtle.turn(double a)
-- rotate by the angle a, where a is measured in degrees, positive values being counterclockwise and negative values, clockwise.turtle.face(double a)
-- point in the direction a degrees, where zero degrees is in the direction of the positive x axis.turtle.penUp()
-- raise the pen, so that the turtle will not draw any line when it moves.turtle.penDown()
-- lower the pen, so that the turtle will draw.turtle.color(Color c)
-- set c to be the color of the line that the turtle will draw.turtle.randomColor()
-- set the turtle's color to be a randomly selected bright color.turtle.lineWidth(double w)
-- set w to be the width of the line that the turtle will draw, measured in pixels.turtle.clear()
-- clear the drawing area to white, without moving the turtle or changing the color, line width, or pen state.turtle.home()
-- reset the turtle to its initial properties: at (0,0), facing to the right, with pen down, and drawing one-pixel wide red lines.turtle.setDelay(int milliseconds)
-- By default, a short delay of 25 milliseconds is inserted after each motion of the turtle, to make the sequence of motions more apparent. This subroutine changes the length of the delay. A delay of zero means there is no delay at all between motions. A delay of 1000 would insert a full second of delay between motions.turtle.setTurtleIsVisible(boolean v)
-- sets whether or not to draw the gray triangle to represent the turtle.
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:
- A subroutine to draw a square, with a parameter to give the length of its side.
- A subroutine to draw a star. You want to draw a five-pointed star with five lines in the same way you would probably draw a star by hand. The length of the lines should be a parameter to the subroutine. Drawing a star is something like drawing a square, except that there are five lines and the angle between one line and the next is -144 degrees. Note that after drawing the star, the turtle should be back in its original position, facing in the same direction as when it started.
- A subroutine to draw a line of stars. Use two parameters: one to give the length of the line used to draw the squares, and one to give the number of squares. Here is an example of a line of nine stars:
- A subroutine to draw a "burst", where a burst is defined to be a lot of lines coming out of the same point, in random directions with random colors and random lengths. Two parameters should give the maximum length of the lines and the number of lines. (Note that you can use turtle.forward(x) followed by turtle.back(x) to draw a line and leave the turtle at the point where it started. Here is an example:
- One more subroutine, of your own design, that does something interesting. For example, you could draw a square spiral like this one: This is made by drawing multiple squares, with a turn through a small angle between each square and the next, just as in the example code given above. However, each square also gets a little bigger than the previous one.
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.