Lab 3

Published

February 28, 2025

Download Starter Files Submit on Gradescope

Objectives

By the end of the lab, you will have:

  • Gain some more practice working with recursive functions
  • Do a little drawing with the turtle
  • Learn about fractals

Working with the turtle

Python includes a module called turtle, which allows us to draw some simple graphics. The turtle can draw wherever it walks. So, we issue commands to tell the turtle where to go, and a drawing appears as it moves. Some of the most important commands are listed in the table below (more can be found in the online documentation). Give the table a quick look before you move on.

Command Description
forward(distance) Move the turtle forward by the specified distance in the direction it is facing.
backward(distance) Move the turtle backwards by the specified distance in the opposite direction to where it is facing.
right(angle) Turn the turtle to its right by the specified angle. Note that this can be any angle
left(angle) Turn the turtle to its left by the specified angle.
goto(x, y) This drives the turtle immediately to the specified location without changing its orientation.
down() Put the pen down so that the movement of the turtle leaves a track.
up() Pick the pen up so that there are no tracks when the turtle moves.
pencolor(colorstring) Change the color of the pen that the turtle is using. This can be a string such as ‘red’, ‘yellow’, ‘blue’, etc. Of course, you have to pick a valid color name.
tracer(state) Turn on or off watching the turtle trace out the shapes. This is useful when you are trying to draw complex shapes and don’t want to wait. Pass in either True or False to turn the tracing on or off.
update() If you turn off tracing, you can use this function to get everything the turtle has drawn so far to appear.
window_width() Returns the current width of the turtle window.
window_height() Returns the current height of the current turtle window.

Remember that to use a module/library in python, you need to import it by using the import command (e.g. import turtle). Note that all of these must be prefaced with turtle.

Turtle and the autograder

The turtle module provides significant flexibility, defining multiple functions that do the same thing. The autograder expects that you will use the functions listed in the table above, rather than any equivalent functions that you can find in the online turtle documentation. Please limit yourself to these functions in your solution.

Part 0: Setup

To begin, download the file lab03.zip by clicking the blue “Download Starter Files” button at the top of the lab, and extract the files from the zip file into a directory called lab03 inside your labs directory.

Part 1: The Koch curve

You are going to write a function called draw_koch(length, generations) that draws a fractal shape called the Koch Curve. The Koch curve is a fractal – a shape that is self-similar, that is to say that it can be decomposed into smaller versions of essentially the same shape (hmmm… sounds recursivy…). The function is defined as follows; the rest of this section describes how to complete the function.

draw_koch
Parameters
length int
generations int
Returns None

Many of the techniques that we use to generate fractals computationally rely on the repetition of a set of steps, so we tend to talk about generations, which are the number of iterations that we used to produce the drawing. The Koch curve is a nice one since we can see its structure most clearly, by looking at how it changes from one generation to the next.

The starting place (generation 0) is a straight segment.

For the first generation, we cut the line into thirds and replace the middle segment with two segments of the same length, forming two sides of an equilateral triangle.

The second generation is formed by applying this same process to every segment in the previous generation (generation 1).

Further generations are formed by repeating this process, taking each segment, breaking it into thirds and replacing the middle segment with a bump. Conceptually, if you take this out to infinity, you will have a very curious mathematical phenomenon – an infinitely long line between two fixed points at a finite distance away. In practical terms, however, when the length of the individual lines becomes 1 pixel long, that is pretty much as small as we can get on a digital screen…

To actually draw the shape, the turtle will do the opposite of what we described above. We don’t want to draw a segment and then go back and remove a piece (nor would that be trivial once we have drawn it). Instead, we will jump right to drawing the highest generation of the fractal.

If you look at a generation 2 curve, it is made up of four generation 1 curves, each 1/3 the length of the generation 2 curve (go back to the images. Can you see it?)

Each of those generation 1 curves is made up of four generation 0 curves, each 1/3 of the length of the generation 1 curve. (Again, go back to the figures and convince yourself!)

What that means is that we don’t actually directly draw any of the higher generation curves, but we tell each generation to leverage the previous generations. The only thing is going to be actually drawn is a whole bunch of generation 0 curves (straight lines!) in different locations and directions.

Let’s see how!

Writing the function

  • Step one: Open the lab03.py script in Thonny, if you haven’t already. There is some boilerplate in the draw_koch_scene function that just moves the turtle over to the left a little to give us some more room. Don’t worry too much about it – you can pick it apart later. The only really important thing in there is that call to the draw_koch() function (that you will write next).

  • Step two: Start writing the draw_koch() function above the draw_koch_scene() function (Python reads from the top down and, if it doesn’t see a function when you call it, it is going to complain). Define the function and make sure it takes integer arguments length and generations. Don’t forget your docstring! We will be drawing this shape using (surprise!) a recursive function. Before you continue, make sure that you can call draw_koch_scene in the shell without any errors. If there are errors, fix them! You may see warnings in the console about “Secure coding” – you can safely ignore that

  • Step three: Write the base case. When generations is 0, draw a straight line of length length (it doesn’t matter which way the turtle is facing, we just go forward).

  • Step four: Save and test this by calling draw_koch_scene in the shell. It should draw a line that is 200 pixels long.

  • Step five: We are going to start working on the recursive case, but it won’t be recursive yet. When generations is greater than 0, draw a first generation curve. We want the curve to fit within length pixels. So, move forward length / 3 pixels. Turn left by 60 degrees. Move forward length / 3 pixels (putting you at the apex of the triangle). Turn right (by how much?) and draw the second side of the triangle. Turn left again (by how much?) and draw the final base line. You should now have a generation 1 curve that fits on a length length line.

  • Step six: Save and test this by calling draw_koch_scene in the shell. Make sure that when you change the number of generations in draw_koch_scene() so that you get one generation one curve.

  • Step seven: Time to go recursive. Instead of drawing straight lines in the recursive case, each call to turtle.forward() should be replaced with a call that draws a Koch curve of the same length (i.e., one third length), and one generation less.

  • Step eight: Test this by entering some different generations into the call in draw_koch_scene(). We recommend starting small with something like a length of 200 and 1 generation just to make sure your code is working. Then you can work up to bigger curves and higher generations. IMPORTANT: Keep the number of generations low or you will be waiting FOREVER for it to finish. if you want to try higher generations, you should turn off the turtle tracer (the tracer is what animates the drawing process on the screen. With it off, you just get the final picture). You can do that by putting the line turtle.tracer(False) before it the call to the draw_koch() function and the line turtle.update() when you have something you want to display (i.e., before turtle.done()). A second problem you may encounter is if the length gets to be less than 1 before you hit the base case, the line segments drawn by the base case will all have a length of 0, and the curve will not draw at all.

Part 2: The Koch Snowflake

If you draw three Koch curves corresponding to the three sides of an equilateral triangle, you will get a shape called the Koch snowflake.

Write a function called draw_snowflake(length, generations), which calls the draw_koch() function turns right 120 degrees, calls it again, turns 120 degrees, and then calls it again.

draw_snowflake
Parameters
length int
generations int
Returns None

Write a second scene function called draw_snowflake_scene(). You can start by copying draw_koch_scene().

Within this function you should draw at least three snowflakes. Each one should be unique with respect to size, spikiness (i.e., number of generations), and color (Color? Yes, color. See the list of turtle functions above). There should also be no connections between them (in other words, you need to use up() and down() and some movement functions between each call to the draw_snowflake() function).

draw_snowflake_scene
Parameters
Returns None

The file you hand in should not call any functions when it is run. So, if you open your file and push “play”, you should see nothing. However, if you call draw_snowflake_scene() in the shell, you will see your snowflakes!

Turning in your work

One you are done, submit lab03.py to gradescope and ensure that you have passed all of the tests. Your file should contain four functions: draw_koch(), draw_koch_scene(), draw_snowflake(), and draw_snowflake_scene().