CS 124, Spring 2014
Lab 5: Introduction to Subroutines
We are about to begin Chapter 4, which is mostly about subroutines. You have already used subroutines, such as System.out.println() and g.drawRect(). Now, you need to learn how to write them and to design programs that use them. In this lab, you will use another set of subroutines—ones that implement "turtle graphics"—and you will write a few subroutines of your own. Since we haven't talked about writing subroutines in class yet, the lab is more tutorial in nature than most of the labs. There is not a lot of algorithm design work.
You will need copies of two files, TurtleGraphics.java, and OnScreenTurtle.java. You should make a lab5 directory and copy the two files into it. You can find them in /classes/cs124/lab5-files.
This lab is due next Tuesday at the beginning of the next lab. Copy your lab5 folder into your homework folder in /classes/cs124/homework as usual.
About the Turtle
This program uses turtle graphics. You should imagine a turtle moving around in a window on the screen, dragging a pen that draws a line as the turtle moves. In the program, the turtle is represented by an object named turtle. This object contains subroutines that represents commands to the turtle, telling it how to move. For example, turtle.forward(5) makes the turtle move forward along a line for a distance of 5 units.
In this program, the turtle appears in the window as a gray triangle, which is somewhat transparent 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, the turtle's position inside the window 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. For example, in an 800-by-800 rectangle, each unit will be equivalent to 40 pixels.
The turtle is always facing in some direction. The gray triangle points in that direction. Initially, the turtle faces to the right. There are commands for changing the direction. Initially, the turtle is set up to draw using a one-pixel-wide red line as it moves. However, both the width and the color of the lines can be changed by appropriate commands. Also, the turtle's pen can be raised so that it can move without drawing any line.
Here are brief descriptions of the commands that are available for use with the turtle object:
turtle.forward(d)
-- move forward d units in the current direction. The type of d is double.turtle.back(d)
-- move backwards d units. The type of d is double.turtle.moveTo(x,y)
-- move directly to the point (x,y), without changing the direction that the turtle is facing. The type of x and y is double.turtle.moveBy(x,y)
-- move x units horizontally and y units vertically from the current position, without changing the turtle's direction. The type of x and y is double.turtle.turn(ang)
-- rotate by the angle ang, where a is measured in degrees, positive values being counterclockwise and negative values, clockwise. The type of a is double.turtle.face(ang)
-- point in the direction ang degrees, where zero degrees is in the direction of the positive x axis. The type of a is double.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. The default color is Color.RED.turtle.color(r,g,b)
-- set the line's color to the color with red, green, and blue components given by the parameters r, g, and b. The parameters should be in the range 0 to 255. The type of r, g, and b is int.turtle.randomColor()
-- set the line's color to be a randomly selected bright color.turtle.lineWidth(w)
-- set w to be the width of the line that the turtle will draw, measured in pixels. The default line width is one pixel. The type of w is double.turtle.reset()
-- clear the screen and reset all turtle properties to their original values.turtle.clear()
-- clear the drawing area to white, without moving the turtle or changing the color, line width, or pen state.turtle.setDelay(milliseconds)
-- By default, a short delay of 50 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. The type of milliseconds is int.turtle.wait(milliseconds)
-- Pause for an extra delay, given by the specified number of milliseconds. The type of milliseconds is int.turtle.setTurtleIsVisible(vis)
-- sets whether or not to draw the gray triangle that represents the turtle. The type of vis is boolean.
Exercise 1: First Turtle Drawing
For this lab, you will only work on TurtleGraphics.java. You should not make changes to OnScreenTurtle.java, which defines all the turtle graphics commands; it just has to be in the same directory as TurtleGraphics.java.
To begin, compile TurtleGraphics.java and run it to see what it does. You will have to close the window to end the program (or type CONTROL-C in the Terminal).
Then open TurtleGraphics.java with gedit for editing. You will see a main() routine near the top of the program. You will add some code to the main routine, and you will add some new subroutine definitions outside the main routine. There are already two examples of drawing with the turtle in main(), and there is space for each exercise that you will do for the lab. (You can keep the examples or delete them; I don't care.) Note that you do not have to put as many comments in your code as I did in the examples.
For your first exercise, you should draw a blue triangle inside a red triangle, as shown at the right. Use only turtle.forward, turtle.back, turtle.turn, and commands for setting color and maybe line width. The size and position of the triangle are up to you. Put your code under the comment that says "Exercise 1: First Turtle Drawing". Hints: All turns that you need to make are 120 degrees or 60 degrees, and the side length for the blue triangle is half of the side length for the red triangle.
Exercise 2: Using a Subroutine
If you look below the main() routine in TurtleGraphics.java, you will see the definition of a subroutine named square. It has a "parameter list" given by (double size), which means that when you use the subroutine, you must provide a parameter of type double. (The type of value that you provide can also be int, because an int value can be assigned to a double variable, and that's essentially what is done with parameters). So, for example, you can say square(10) to draw a square of size 10, or square(Math.sqrt(2)) to draw a square with size given by the square root of 2. Note that because square is defined in the same class as the code that you are writing, you will call it using a simple name: square(x) rather than something.square(x).
For your second exercise, you should draw a set of at least three nested squares, as shown in the picture. This time, you can use any turtle commands that you want, but you must use the square subroutine to draw the actual squares. The size, colors, and position of the drawing are up to you. Use random colors if you like. You will need to use turtle.penUp() and turtle.penDown() to make it possible to move the turtle without drawing a line. (You might try drawing a larger number of squares using a for loop, but you are not required to use a loop.)
The code that you write should be added to main(), under the comment that says, "Exercise 2: Using a Subroutine."
Exercise 3: Writing a Subroutine
For the third exercise, you will write your first complete subroutine definition. The definition of a subroutine cannot be placed inside another subroutine. Other than that, it can be anywhere inside the class. The order in which subroutines are defined doesn't matter, and it doesn't matter if the definition comes before or after the places in the code where the subroutine is called. To begin the exercise, copy this empty subroutine definition into your code:
private static void burst() { }
The name of the subroutine is burst. It has no parameters, so it can be called by saying burst(), with nothing in the parentheses. The exact meaning of "private static void" will be explained in class. The code that defines what the subroutine does goes between the braces. You should add code to the subroutine to draw a "burst" of lines coming out of the current turtle position, similar to the one shown in the picture. Use a for loop to draw a large number of lines. Note that you can say "turtle.forward(length); turtle.back(length);" to a draw line of a given length along the direction in which the turtle is pointing and then return to the starting point of the line (ready to change the direction and draw the next line). The length should be chosen at random. You can either use turtle.face to point the turtle in random directions, or use turtle.turn to turn a small amount between each line and the next.
For a subroutine to do anything, the subroutine has to be called. You need a call to burst() under "Exercise 3" in main(). The subroutine call is already there. You just have to uncomment it. You don't have to make any other changes to main() for this exercise. (However, you could add a call to turtle.setDelay(0) if you don't want to watch the slow process of drawing the burst.)
Add a comment for your subroutine! The comment should go before the subroutine. Follow the style that is used for other subroutines in the file.
Exercise 4: Subroutine with Parameters
Most subroutines require parameters. The required parameters are specified in the parameter list that is part of the definition, such as the (double size) in the definition of square(). The important thing to understand is that the value of a parameter is unknown when you are writing the code inside the parameter. The actual value will come from elsewhere in the program, when the subroutine is called. It is not input from the user of the program; it is a value from another part of the program itself. When square is called as square(10), the value 10 is assigned to the parameter size before the subroutine's code is executed.
For the fourth exercise, you will make a subroutine that has a parameter. It will be a simple variation on the square subroutine. In fact, you can simply copy the definition of square and make some minor modifications. (Don't forget to modify the comment, too!)
Your subroutine should be named star instead of square. It will draw a five-pointed star with a specified size. You have to change the name of the copied routine, so that the first line reads:
private static void star(double size) {
In the definition, the number of sides should be 5 instead of 4, and the angle through which the turtle turns should be −144 instead of 90. Other than that, the two subroutines are the same.
After writing the subroutine, uncomment the call to star(5) under "Exercise 4" in main(). And don't forget to add comment for your subroutine definition.
Exercise 5: Subroutine Calling Subroutine
An important aspect of subroutines is that one subroutine can call another subroutine. This makes it possible to create complex programs out of subroutines, where the individual subroutines are not very long or complex.
For this exercise, write a subroutine that draws a "spiral of shapes" similar to the one shown above. The spiral is drawn using a for loop. Each time through the loop, one shape is drawn. The shapes start out small, and a small amount is added to the size after drawing each shape. And the turtle is turned by a small amount after drawing each shape. My picture uses square shapes, but you can use stars instead if you want. You must draw the individual squares or stars by calling one of the subroutines, square() or star(). My picture uses random colors, but you can use a single color if you want. The name of the subroutine should be spiral, and it should have no parameters. So, the first line will be
private static void spiral() {
To run the subroutine, uncomment the line in main() that calls it. I suggest calling turtle.setDelay(0); otherwise, it can take quite a long time to draw the spiral.
Exercise 6: Random Walk
As a final exercise, write a subroutine that sends the turtle on a random walk. The idea is to do the following over and over: Select one of the directions 0, 90, 180, or 290 at random; face in that direction; then move forward by a small, constant amount (say 0.25). Here is a loop that does this:
while (true) { int direction; direction = 90 * (int)(4*Math.random()); turtle.face(direction); turtle.forward(0.25); }
A sample random walk created by this code is shown in the picture on the right. One could just put the code into a subroutine. However, you should write a random walk subroutine that has a parameter. The parameter could be the distance that the turtle moves forward in each step. Or the parameter could specify the number of steps that the turtle will take, instead of letting it walk forever. Another idea is to implement two different kinds of random walk, and use a boolean parameter to tell the subroutine which kind of walk to do. You can get a nice pattern by selecting the direction randomly from 0, 60, 120, 180, 240, 300 instead of from 0, 90, 180, 270. A different sort of random walk is obtained by facing in a completely random direction and moving forward a random amount each time. Another idea is to use a random color for each step in the random walk.
You should also add a call to your random walk subroutine at the end of main(), just under the comment that says "Exercise 6: Random Walk."
(Note: If the random walk uses an infinite loop, then the turtle will eventually wander off the screen. If you want to detect when that happens, note that there are functions turtle.getTurtleX() and turtle.getTurtleY() that return the current x- and y-coordinates of the turtle.)