Lab 3: More Functions and
Drawing with Turtle Graphics
Drawing with Turtle Graphics
If you find typos or problems with the lab instructions, please let us know.
The goal of this exercise is to begin to implement more complex programs and to become familiar with Python’s Turtle package. In particular, you will:
Read data from a (csv) file
Use a loop to process data
Create graphical programs using Python’s Turtle package
Visualize the path and strength of hurricanes
There is both a pair and an individual component of this lab. The individual component will be completed first. You may not start the pair portion until both you and your partner have completed the individual component. When you get to the pair portion, you must use pair programming. Remember that this means two people will work at the same computer, one driver and one navigator (since this is over Repl, obviously you can't be at the same computer, but there should still be one driver and one navigator). The driver does the typing and using the mouse, while the navigator makes suggestions, points out errors, and helps guide the process. You should switch roles every 15-30 minutes.
If you visit the page https://github.com/spis2022 you should find a repo already set up for your and your pair partner (or trio) with the name lab03-name1-name2 (or for a trio, lab03-name1-name2-name3). The name should match your pair (or trio) name from the pair-partner spreadsheet.
For example, if your pair is Aammya-Phill, the repo looks like the picture shown below.
Initially it will be public and a completely empty repository with no readme, no .gitignore, and no license. We will make the repo private a bit later, but it must initially be public.
You will need to import some starter code. If you scroll down on the screen, you will see an option to import starter code, as shown in the animation below. Please enter this URL, as shown, then click Import Code.
https://github.com/ucsd-cse-spis-2021/lab03starter.git
(Note that the 2021 here is not a typo; we are reusing a starter repo from 2021.)
It may take a few minutes, but when you return to the repo on GitHub you should see some starter code, as shown at right. GitHub will also send you an email when it has finished importing the repo.
Now, we will use the steps described and illustrated in animations below to open this project in Repl.it and import your GitHub repo. Start by going to the repl.it website.
Open repl.it in your browser and cick the blue + at top right to create a new repl.
In the pop-up, click the Import from GitHub button, upper right.
In the GitHub URL box at left, start typing in the name of your repo.
The name will start with spis2022/lab03, and the full name should be something like spis2022/lab03-name1-name2. Alternatively, you can also copy-paste the url of your repo directly.
When your repo pops up, click it, then click Import from GitHub (lower right)
If everything worked ok in the previous step, just skip to
Sharing repl with your pair partner
One way it could go wrong: If Repl.it pops up a prompt saying Upgrade to our Hacker plan to import private repositories, that means that you forgot to make your github repo public (as shown in the animation below.
Repl.it requires you to pay for an upgraded account to clone from private github repos (which is why we created a pubic github repo).
If you get the Upgrade to our Hacker plan.. message, just click ✕ at upper right (cancel)
Then, follow the instructions below; afterwards your repo should be public and you can try importing it to your repl again.
Changing private repo to public
Go to your github repo's Settings page, and scroll all the way down to Danger Zone and click Change visibility
Select Make public. Type your repo's name to confirm.
Click the button for I understand, change repository visibility
Your github repo should now be public
Now, in repl.it, there should be three windows: the code editor, the terminal, and a configuration tool like the one shown at right below.
If you see the configuration window, just click the Done button.
Now, the member of the pair (or trio) that created the repl should share it with their pair partner(s). If you don't recall how to do this, see steps 2 and 3 of these instructions:
Creating a shared repl.
Your repl is now connected with your github repo, but your github repo is still public, so we need to make it private!
Changing public repo to private
Go to your github repo's Settings page, and scroll all the way down to Danger Zone and click Change visibility
Select Make private. Type your repo's name to confirm.
Click the button for I understand, change repository visibility
Your github repo should now be private, but your repl is still able to push to it, even on the repl.it free plan!
Verify that your partner(s) can access your repl.it and your github repo before continuing; the github repo should be set up so that all members of your GitHub team can access it, but be sure before you continue. If you have any issues with this please see a mentor.
Next, you will complete the individual portion of your assignment, adding your files to this shared repo.
To warm up with the Turtle, you will use the Turtle to draw your first initial (the first letter of your first name) on the screen.
You should complete the entirety of step 1.1 and 1.2 individually. You may talk to your partner and have them help you, or give help to your partner, but you should try to do as much of it on your own as possible, and all the code you write should be done individually.
Changing indentation in repl.it
Changing indentation in repl.it
Before writing any code, we have to change the indentation of this project.
By default, Repl projects' indentation is 2 spaces, but the starter code has an indentation of 4 spaces. If the files don't have consistent indentation, you will get errors when trying to run your code!
To do this, go to your project settings (the button that looks like a gear under the left navbar), scroll down to indent size, and select 4. If your indentation is already 4, then no worries!
Create a new file named lab03Warmup_YourName.py where YourName is replaced with your first name, e.g. lab03Warmup_Phill.py (as shown below)
At the top of this file put a comment with your name and a description of what this file will do (a program to draw the first letter of your name).
In order to work with the Turtle, you need to import the turtle module. Do this by putting the line:
import turtle
at the top of your file, either before or after the comment you wrote. This above line tells Python about the turtle library and allows you to call its functions.
Next, let’s write a simple function that uses the Turtle package to draw a shape. Copy the following code into your file.
def draw_picture(the_turtle):
''' Draw a simple picture using a turtle '''
the_turtle.forward(100)
the_turtle.left(90)
the_turtle.forward(100)
the_turtle.left(90)
the_turtle.forward(100)
the_turtle.left(90)
the_turtle.forward(100)
the_turtle.left(90)
my_turtle = turtle.Turtle() # Create a new Turtle object
draw_picture(my_turtle) # make the new Turtle draw the shape
What happens when you run your file? Use python file_name.py in the Shell tab (also sometime called the terminal; it's a place where you can write Linux commands ). This is illustrated in the animation below.
This will run your file. (The first time you run this, the Repl output screen might be frozen or laggy, so you might have to run the code a few times before seeing the correct output. You might have to do this throughout the rest of the lab as well.).
If it doesn't work, make sure that you typed the command into the Shell tab, and not the Console or Markdown tabs.
Note that if you want to run the same command again in terminal, for example say above we wanted to run python lab03Warmup_Phill.py again, press the up arrow key on your keyboard (and the command will show up again). Then , you can press the enter key on your keyboard to run the command again. This shortcut can save you some typing and make testing your code easier.
More troubleshooting tips for Python Turtle Graphics
If you have having trouble seeing the graphics, here are a few tips.
If the graphics don't show up at all
Try refreshing the browser window, and then again running python filename.py (the actual filename, not literally filename.py) in the Shell window. Be sure it's the Shell window.
If the graphics windows closes too fast to see what's happening.
If you find that the window showing the graphics closes too fast for you to see what was drawn, there are two things you can do that can help.
Add turtle.done() at the bottom of your file. This will keep the graphics window open until you click the ✕ at the top right corner of the box, and shown below.
Add turtle.speed('slowest') to the top of your file. (The possible values are 'fastest', 'fast', 'normal', 'slow', and 'slowest'. There are also numerical values: 0 is fastest, then, strangely, the numbers from 0-10 range from slowest to fast.)
Here's what it looks like before you add turtle.done() and turtle.speed('slowest'):
Here's what it looks like after you add turtle.done(). Note that it's a lot easier to see what the final drawing looks like, since you can look at it until you click the ✕. You can also click to make the drawing bigger.
Here it is with both turtle.done() at the end, and turtle.speed('slowest') right after creating the turtles. Note that turtle has to match the variable actually used to create each turtle—it isn't literally turtle.speed('slowest'), but my_turtle.speed(slowest).
Suggestion to get rid of lag
(If the lag isn't too bad, you can totally skip down to "Modify the function"
We won't go into too much detail at this point, but basically the reason that there is serious lag is because Repl.it computes the Turtle results on a server and then sends it back -- so it takes a while to get the results back! Repl.it has figured out a way to fix this by having client-side Turtle Graphics (if you don't understand what this means, it doesn't matter, just continue reading).
To use the client-side Turtle Graphics and get rid of the lag, create a new Repl.it with the language "Python (with Turtle)", and then copy over the code you were planning on running in your original project to this new project. Tada, no more lag!
Note: This will only work for part 1 and the challenge problems. For part 2, there's no way to get rid of the lag.
Bigger Note: Once you've finished coding in this new project, make sure to copy the code back over to the old project! This is necessary so that you can add it to you Github repo.
Modify the function in various ways to see what happens:
Change the number in the first the_turtle.forward() call in the draw_picture function. What’s the role of the number?
Change the values in the calls to the_turtle.left(). What units are being used?
You might notice that there seem to be three names “turtle” in the code above, plus Turtle() with a capital letter, and () after it. What’s going on?!
The Turtle library works a little differently than other libraries we’ve looked at so far, in that it uses a style of coding called “Object Oriented Programming”. You’ll learn much more about objects in CSE 8A or CSE 11, but for now you can think of objects as complex types of data that also have built-in functions associated with them.
turtle on the line
my_turtle = turtle.Turtle()
is the name of the package, i.e. the built-in Python library imported at the top of the file where you will find all of the functions for the Turtle.
Turtle() is a special function (called a constructor) that creates a new Turtle object. We write turtle.Turtle() because Turtle() is part of the turtle module that we imported with: import turtle
When we write my_turtle = turtle.Turtle() the right hand side creates a new Turtle object, and the variable my_turtle becomes a reference to that object.
When we make the function call draw_picture(my_turtle) the formal parameter the_turtle in the function definition (which starts with def draw_picture(the_turtle):) gets the value of the variable my_turtle.
Therefore in the code above, the variables my_turtle and the_turtle both refer to the same Turtle object. If that's clear, great—but if not, read through the code and this explanation a few times until that sinks in.
Understanding this point—the idea that the formal parameter of a function gets the value of the argument in the function call—is a big deal in terms of understanding how to work with functions in Python, so it's worth asking for help from your mentor if this is a sticking point for you.
In the example above, my_turtle and the_turtle both referred to the same Turtle object. However, it is possible to have more than one Turtle object in a given Python program.
For example, try adding the following code to the end of your warmup file. Because there are now three different calls to turtle.Turtle() (the two new ones plus the original my_turtle = turtle.Turtle()) you now have three entirely different Turtle objects. They can move around the screen and draw things independently of each other.
Try running the code. Can you explain what’s happening? With your pair partner, take turns explaining what's going on to one another. You could also try modifying the code to see if you can predict what will happen.
Note that you can optionally add turtle1.speed('slowest') after you create turtle1, and do the same for turtle2, if you want to see things more clearly. Each turtle may have it's own different speed.
Also, if you use turtle.done(), be sure it's at the very end of the file.
turtle1 = turtle.Turtle()
turtle2 = turtle.Turtle()
turtle1.setpos(-50, -50)
turtle2.setpos(200, 100)
turtle1.forward(100)
turtle2.left(90)
turtle2.forward(100)
In the code above you noticed that by calling Turtle() three times we got three Turtles that we could control independently. However, if you know you only want to use one Turtle, Python will provide an “anonymous Turtle” that you can control without actually creating explicitly. So you might sometimes see lines of code that look like
turtle.forward(100)
turtle is still the package name, and the forward call will control the first Turtle created. Python will always create this first Turtle, even if you do not include any call to Turtle().
When we use turtle.done() at the end of the file, we are using the "anonymous Turtle"; we don't need more than one call to done() even if we have multiple turtles.
It’s a good idea to check out the library documentation (a.k.a. reference manual, API docs, etc.) when you’re learning how to use a new library (aka set of methods).
Here's the entry in the documentation for the function that lets us move forward:
turtle.forward(distance)
turtle.fd(distance)
Parameters: distance – a number (integer or float)
Move the turtle forward by the specified distance, in the direction the turtle is headed.
You can browse the documentation and learn about many, many other things you can do with your turtles, including changing color, changing the shape, etc.
Now is a good time to make sure you’ve got helpful comments in your file and that you’ve saved it (Repl should save it automatically).
Add and commit your code using these instructions: Making commits to repo via repl.it
Create a new file named drawLetter_YourFirstName.py. In that file, write the following method:
draw_A(the_turtle)
Note that the name of this method should correspond to the letter you are drawing. So if my name is Chris, my function would be named draw_C. The parameter to this method, the_turtle, is the turtle you should use to draw the letter. Note: you might have to run the turtle code several times before seeing correct output, as the Repl output screen sometimes freezes or is laggy.
Feel free to be creative here. Right now, you can draw the letter any size, using any style. You will probably find the functions penup and pendown useful, but check the documentation for other functions that will allow you to change the style of the lines your turtle draws.
Test your function to see if it works by creating at least two different Turtles, moving one of these Turtles away from its default position (you probably want to lift the pen before moving the Turtle), and then calling your draw_A function with each Turtle. You should see two letters drawn, one drawn by each Turtle (one after the other). Does the output look good? Do both the Turtles draw?
If there are bugs in your code (we’d be surprised if there weren’t!) fix them before moving on.
It's better to only do GitHub commits for working code. (If you need to commit non-working code to your repo, be sure it has an appropriate comment on it.)
Also: be careful not to move your turtle off the screen, because you won't able to see it drawing!
At this point you are almost finished with the individual portion and ready to commit and push your code so far. Here’s a checklist to make sure you haven’t forgotten anything:
Do you have a comment at the top of your files?
Do you have a draw function for your first initial?
Does this function have one parameter: a turtle?
Does this function have a reasonable docstring (recall from class what a docstring is)
Did you test your function with at least two Turtles to make sure it’s working correctly? (And is it?) If so, you are ready to submit!
Make a commit to GitHub when you have a working version (these instructions explain how)
You might get a message at this point telling you that your push failed. This will happen if your partner has pushed before you. Now you need to incorporate their changes to the central repository into your local version of the repository before github will allow you to push. To do this you will need to execute a pull command in the Shell window:
git pull origin master
Then you will be able to push and everything should be okay. Since you and your partner have been editing different files, you should not have any merge conflicts, but if you do, stop and ask a mentor for help.
See the documentation on this page for a bit more information.
At this point you are done with the individual portion. If your partner is not yet done, DO NOT CONTINUE. Here are some things you can do while you wait for your partner:
Work on APS
View and respond to the enrichment video
Make your letter prettier/more interesting
Make more functions to draw more letters (try drawing out your whole name!)
STOP! Do not start on this part of the lab until BOTH partners have completed the individual portion. When you do start, make sure you use pair programming.
Discuss the following questions with your partner, then put your joint answers into comments at the top of a new file named lab3Letters_pair.py.
What is the “anonymous turtle”?
In the code below, what is the difference between turtle and Turtle()?
my_turtle = turtle.Turtle()
draw_picture(my_turtle)
Imagine that I have a turtle in a variable named my_turtle. What line of code will change that turtle’s y-position to 100?
Choose one of your draw_A (or whatever letter you used) functions and copy it into your lab03letters_pair.py file. Then, modify the function as follows:
Add another parameter to your draw_A function: draw_A(the_turtle, size)
size will determine the size of your letter.
Modify the code in the method so that it respects the size parameter. You are free to use any interpretation of size that you wish, but your letter must be drawn in different sizes for different values of that parameter. Make sure you modify the docstring for your modified function so that it accurately reflects what this new version of the function does.
Finally, test your new function to make sure that it respects the size parameter. Fix any bugs you find, then commit and push.
Switch driver and navigator if you have not yet done so.
This part of the assignment is based on a SIGCSE Nifty Assignment.
In this part, you and your partner will use the Turtle to animate the paths of some real hurricanes! Your goal is to build a program that functions like the one in this video. Here are the details:
Open the starter file irma.py.
In the irma_setup() function, the following are done for you:
Creating the screen and turtle
The turtle’s shape is changed to that of a hurricane
Loading a background image of the Atlantic
Setting the world coordinates of the screen to match the latitude and longitude on the map
Do not change the irma_setup() function!
You will add your code into the function irma(). Read the comments and the code in that function and make sure you understand what it does so far. Try moving the turtle (t) around and see it animate. Notice the coordinates of the world have been changed from their default. The x values in the new coordinate system range from -90 to -17.66 and the y values range from 0 to 45. Since the Turtle starts at (0,0) you cannot see it. Try moving it to a location where you can see it in the new coordinate frame and making it move.
You will also notice that this starter code uses a file named irma.csv in the data directory. This data was scraped from Weather Underground, last access 9/14/2017. This file contains data about hurricane Irma. Each line contains 6 columns separated by commas (thus the .csv file extension). The file can be opened directly in Repl or opened in Excel for a columnar view. The first line of the file describes what each column is. Here are the first 3 lines of the file, separated into their columns:
Date Time Lat Lon Wind Pressure
30-Aug 15:00 GMT 16.4 -30.3 50 1004
30-Aug 21:00 GMT 16.4 -31.2 60 1001
The only columns relevant to your code are Lat (the latitude), Lon (the longitude), and Wind (the wind speed in miles per hour). Carefully check out the Lon (longitude) and Lat (latitude) values -- which value correlates with x and which value correlates with y?
You might have to run the turtle code several times before seeing correct output, as the Repl output screen sometimes freezes or is laggy.
Using the data in irma.csv, your irma function must show hurricane Irma’s path. Your solution should look like the video and must include the following:
Correctly show each point in the data file (together with lines between each point)
At each point, if the storm has hurricane strength winds, you must write what category the storm is, otherwise, draw no text. You can use Google to find the wind speeds for each category of hurricane.
Color code the hurricane strength:
Red for Category 5
Orange for Category 4
Yellow for Category 3
Green for Category 2
Blue for Category 1
White if not hurricane strength
The thickness of the line should change in proportion to the hurricane category.
Check out the video example listed earlier if you're wondering how this should look like.
Notice that the starter code already uses the csv package to read the lines from the data file. The csv package is already imported at the top of the irma.py file, and we’ve provided some comments and code to help you understand how you can read the lines and get the data out of the lines in the file. Make sure you understand how this works before you attempt to change the code. If you have questions, ask a mentor.
We have provided some comments in the starter code to help you out, but this is a more open-ended task than you’ve done in the past. Make a plan with your partner before you attempt to start coding
Make sure you run your code, and look at the data, to test your code.
Ensure the following:
Does your line start in the same position as the demo? Remember, you need to move the turtle to the starting latitude/longitude with the pen UP so you don’t draw a line that’s not part of the hurricane data.
Do the number and order of 1s, 2s, 3s, etc, on your path as well as the color of the points in your path, match the demo?
Does the path go in the same direction as in the demo?
When you run your code on other data files, do you get the path you’d expect (after looking at the data)? You can change which file is opened inside of the irma() function (look for hurricaneFile)
Once you’ve got your hurricane tracker working, make sure of the following:
Do you have your names and a comment at the top of each file?
Do you have docstrings for each function you wrote?
Did you use meaningful variable names?
Did you use appropriate commenting in your code when something might be unclear to the reader?
Once you can answer yes to all of the following make sure you add ALL of your files from this lab (the two files from the individual part (4 between the two of you) and the two files you created for the pair part) to your repo, then commit and push to github.
Then you are done! But of course, you’re never done, so keep working on creative extensions of your own!
For this challenge problem, you are asked to build your own game with turtle. Check out the sample code below. The only really new concept is how to react to key strokes with the onkey function: for each key stroke event (i.e., pressing that key), there is an associated function that is executed in response. For example, turtle.onkey(speedup,"d") causes the speedup() function to be called each time the “d” key is pressed. That line of code doesn’t actually call the function; it simply informs the program that each time “d” is pressed, the function should be executed.
Also note the use of the keyword global in the speedup() function. It tells the function that the variable speed1 is a global variable, which means that it is defined outside of the function. Without specifying that speed1 is something Python can find outside the function, it would complain that inside the function is has never heard of speed1 before. Remember, inside a function, only the variables that are defined there (such as the arguments that are passed to a function) are available. The way you have modified global variables before was to pass them as arguments to a function and then return the result. Directly modifying a global variable is an alternate way of doing this. It is generally considered less desirable, as it is not as obvious what is going on to someone reading your code and if you make a mistake it can cause undesired effects that are hard to debug. However, since we are tying a function to a keystroke in our interactive turtle example and we cannot return anything, it is useful here.
Important: For the onkey function to work while you're running the python program, you need to be focused on the output window. So click on the output window with your mouse after starting the program in the terminal window.
import turtle
import time
# Create a turtle
player1 = turtle.Turtle()
# Set the simulation speed to max. This is not the speed of the turtle, but specifies
# how fast the animations are updated. A value of "0" is best for smooth animations
player1.speed(0)
# Function that will be executed on a specific key stroke (see below)
# It causes player1 to turn left
def turnleft():
player1.left(30)
# Function that will be executed on a specific key stroke (see below)
# It causes the speed of player 1 to double
def speedup():
global speed1
speed1 = speed1 * 2
# This code makes the turtle library listen to key strokes
# Then for the keys listed, e.g., the left arrow or the "d" key,
# it specifies which function will be executed when that key is pressed.
# You can list additional keys and tie them to your own functions
turtle.listen()
turtle.onkey(turnleft, "Left")
turtle.onkey(speedup,"d")
# The initial speed of player 1
speed1 = 0.25
# Print the current time
# This is Unix time, i.e., the time in seconds since 1/1/1970
print(time.time())
# This is the main game loop
# It is an infinite loop; this code is executed forever
while True:
player1.forward(speed1)
Task 1: Expand the game where the speed of the player is doubled automatically every 5 seconds. Remove the “d” key input, but instead use the right arrow to turn right. The player dies when they go outside of the field, past the boundary (think, how does the player "die"?). Define as the score, the number of seconds the player was able to stay alive.
Task 2: Add other fun features to the game. Maybe have another turtle run around randomly, which you also must avoid. You can also use another turtle to implement objects you can grab, which would lower the speed again. Maybe the right and left key swap functionality every so often. Think of some more variations …
Task 3: Create your own game. How about a multiplayer game? We’d all love to see what you can come up with.