CS 110 Lab Project 3
Creating Pictures through Recursion
In this lab, we will introduce Turtle graphics, a complement to the Graphics library we have been using thus far. The Turtle class builds on the Arrow shape provided in the Graphics library. Turtle graphics are particularly helpful in recursive functions that perform drawing, as they can perform a set of drawing operations that are relative to the notion of a "current position" on the drawing screen, and can avoid using parameters carrying information about the absolute and global coordinates in which we are drawing. We will use these in the Snowflake and the Binary Tree drawings, while we will see the contrast with absolute coordinates when we program Sierpenski's carpet.
Background: Turtle Graphics and a Template main() function
Start by going to the SHARED volume, and, under the Resources/lab03 subfolder, copy the Turtle.py file onto your local system, and in the same folder where you will be developing your Lab03 programs. If you haven't already, now would be a good time to create the folder named Lab03 that you will use for this assignment. Also go ahead and copy the turtleExample.py file in the same folder under Resources.
In turtle graphics, we start with a standard Window object. The coordinate system is as before, with (0,0) in the upper left corner and x increasing to the right and y increasing down. Coordinates and distances are again measured in screen pixels.
Now imagine a computer/robotic "turtle" starting at the center of the Window. Through Python, we can issue method invocations to this turtle, telling it to move forward, turn left or right by a certain number of degrees. As the turtle moves, the movement is animated, and the turtle draws a line showing the path of the given commands, as if the turtle has a pen associated with it. We can also issue other commands like setting the color of the pen, or its line width, and even raising and lowering the pen so that we can move the turtle without the associated drawing taking place.
Just to get a feel for this model, start by bringing up Calico and opening and running turtleExample.py:
from Graphics import * from Turtle import * win = Window('Turtle', 500, 500) t = Turtle(win) t.forward(100) t.left(120) t.width(3) t.color('red') t.forward(75) t.up() t.forward(25) t.left(60) t.down() t.forward(50)
Experiment for yourself, by issuing additional method invocations in the shell. If you want to know the full set of functions available in the Turtle class, issue the help(Turtle) function invocation to the shell (after a from Turtle import * has already been issued). You can get the description for an individual function by invoking help(Turtle.forward), and replacing forward with the desired function.
Problem 1: Practicing with turtles and functions
Write a program in a file named polygon.py. The main() function should create the Window and the Turtle and then use function invocations to thoroughly demonstrate the functions specified in the list below.
- Write a fuction called square that takes parameters for a Window, win, and a Turtle named t. It should use the turtle to draw a square.
- Add another parameter, named length, to your definition of function square. Modify the body so the length of the sides of the square is length, and then modify the function call to provide the additional length argument. From your main() invoke the function multiple times from different locations and headings to demonstrate its correct operation.
- Next modify the main program to call the input function to get a length from the user, assigning to a variable. Use this length as the argument to the function invocation to square.
- Make a copy of the square function and change the name to polygon. Add another parameter named n and modify the body so it draws an n-sided regular polygon. Hint: The exterior angles of an n-sided regular polygon are 360.0/n degrees
- Write a function called circle that takes a Window, w, a Turtle, t, and a radius, r, as parameters and then draws an approximate circle by invoking polygon with an appropriate length and number of sides. Test your function with a range of values of r.
Hint: First calculate the circumference given the radius. (Google if you don't remember the calculation for a circle's circumference.) Then compute length by observing that length * n should equal the circumference.
- Make a more general version of circle called arc that takes an additional parameter, called angle, which determines what fraction of a circle to draw. angle is in units of degrees, so when angle=360, arc should draw a complete circle. Note: if this one is giving you trouble, save it for later and move on to the recursive drawing problems described below. ... just don't forget to come back to this one before the due date.
Problem 2: Sierpinski's Carpet
Note:This first drawing problem does not need the Turtle class. The only drawing required is that of a Rectangle, so the Graphics library is sufficient.Sierpinski’s carpet is defined as follows. A problem instance is defined by giving an (x, y) coordinate for the upper left corner of the instance along with a length of a side. Consider the window region defined by the problem instance (i.e. from (x, y) to (x+side, y+side)). Partition this square-sized region into 9 equal-sized smaller regions in a 3 by 3 region grid. Color in the middle region with a filled Rectangle. Recursively invoke Sierpinski’s carpet for the remaining 8 squares. The parameters to our function also require a depth variable, so that the base of the recursion can be determined when depth is zero; we start with depth at perhaps 4 or at most 5, and each recursive invocation decrements depth by one. Here is what you should get:
Create a new program called carpet.py. In the main function, set up for an 972 x 972 turtle screen. Even in the base case, the drawing of the center square is performed. Hint: My solution had a function header for the recursive carpet function as follows:
def carpet(depth, win, x, y, side):This function has no return value.
Problem 3: Snowflake
Create a new program called snowflake.py. In the main function, set up for an 800 x 350 window. Create a Turtle object and set the pen width to 2, and the pen position to an initial position at coordinate (35, 275) (look for the moveTo() function in the documentation). Be careful to not let the pen draw as the position moves from the center of the screen to this initial position.
Create a function called snow() which returns nothing but is given a window, a turtle object and two ints called depth and length. This function should do one of two things.
- First, if iteration is 0, all you should do is use the forward function on the turtle that was passed in, drawing forward the distance given by length.
- The more complicated case is if iteration is greater than 0. In that case, you will have 7 steps to perform. The first, third, fifth, and seventh operations are all recursive calls to the function snow. Each of these recursive function calls will be identical, and will be passed the window, the turtle, depth-1, and length/3. That is, if we aren’t down to iteration 0 yet, our function will call the snow function four times with a lower depth value and shorter lengths.
This is the part that allows the problem "solved" by our recursive invocations to be smaller instances of the original problem.
Between each of these recursive calls (i.e., the second, fourth, and sixth steps), we’ll change the heading of our pen. To do this, you’ll use the function left(). The three rotations should be 60, -120 and 60 degrees respectively.
A correct solution should look something like this:
Problem 4: Binary Tree
A Binary Tree is a tree where, at each branching point, we branch into exactly two directions. Binary trees are a great application of recursion, whether we are drawing the tree or have a data structure that utilizes the tree structure. In this case, I give you the picture of the desired result, but I want you to figure out how to use recursion to solve the problem of drawing a binary tree. As in the carpet recursion above, we can use a parameter for governing the depth of the recursion. Part of the problem solving is determining the set of parameters and finding a "consistent" relative position and heading that can be assumed as a precondition to any invocation of the tree() recursive function. Here is what you should get: