Colby Seal
CS 151: Computational Thinking
(Spring 2014)

Syllabus

Teachers
Beatrice
Caitlin
Stephen
Dan
Lanya
Victoria
Matt
Hieu
Jacob
Scott
Hieu


Assignments


Other Pages

Project 2:
A Shape Collection


Assigned: Wed Feb 12 2014
Due: 11:59:59 PM on Tue Feb 18 2014
Team Size: 1
Language: Python


We'll spend some more time this week learning Python syntax and creating functions, which are collections of code that we may want to execute more than once. An important aspect of functions are parameters, which are a way of making a function do more than one thing.

In addition, we'll focus on overall code organization. How you write and organize your code makes a big difference in how easy it is to write, modify, debug, and re-use. For example, we will be grouping functions that do similar things (all the shape-drawing functions belong in the same file), we will have only one copy of each function, and we will be adding comments to our code. Good code organization saves you time and effort.


Setup

  1. Mount the Personal fileserver, and make a folder called Proj2 in your directory.
  2. Open a text editor (e.g TextWrangler). Create a new file called lab2.py and save it in the Proj2 folder. As you did in the project last week, we're going to put a series of Python commands into the file and then tell python to execute those commands.
  3. Put a docstring at the top of the file with your name, date, and the name of the file. (See below for an example.) You should always have a docstring at the top of each file.
  4. In Python, you make a comment by using a #. Anything on the same line after the # is not read by the interpreter. Comments are important in code. They have three important functions.
    • If you write out your algorithm as comments before writing the actual code, it helps you design your algorithms efficiently, reason about what code structures you need, and it makes code writing go much faster.
    • Comments helps you to re-use and modify code by reminding you of what the code does and why you wrote it the way you did.
    • Comments help other people to analyze and use your code by making it easier for them to understand the algorithm.

    From now on we'll be grading your code not only on its functionality, but also on whether it is well-commented and easy to read. Well-commented code has a comment (1 sentence) at the beginning of each function describing what it does and the meaning of its parameters. It will also generally have a comment before each major section of the function.

    The comment at the beginning of each function is special. We call it a docstring and use different characters to set it apart from the code. We use three double-quotes at the beginning and at the end. Below, I show an example of a file that I wrote, using the commenting conventions we want you to use.

    '''simpleTurtle.py - Uses the turtle to draw a triangle.
       Author: Kyle Burke
       Date: 2/14/2014 '''
    
    import turtle
    
    def drawTriangle(edgeLength):
        """ Draws an equilateral triangle.
            Each edge is edgeLength long.
        """
        turtle.forward(edgeLength)
        turtle.left(120)
        turtle.forward(edgeLength)
        turtle.left(120)
        turtle.forward(edgeLength)
        turtle.left(120)
    
    # main code
    drawTriangle(50)
    drawTriangle(100)
    raw_input("Press enter when ready. ")
    
  5. After file header comments, write the command to tell python to import the turtle package:

    import turtle

    From now on, we'll be importing packages using the above syntax. It requires you to access the turtle functions slightly differently, but it enforces better code organization and style, in addition to reducing unintended errors.

  6. To test that everything is working, put the following commands in the file and then run it on the command line.
    turtle.reset()
    turtle.forward(100)
    raw_input('Press enter to continue.')

    The above code is pretty much identical to how we started last week, but since we included the turtle package using the import turtle syntax, we have to use the prefix turtle. prior to any function from the turtle package.

    Execute the file in a terminal. (Remember that instead of typing the absolute path to your Proj2 direcotry, you can use the Finder to drag and drop your project folder into the terminal.)

  7. Once your file works, go back and delete the reset and forward commands. Before the raw_input line, make a new function, goto, that sends the turtle to a particular location without drawing anything. This function should take two parameters to hold the new x location and y location.

    Function syntax reminders:

    • The function definition begins with the keyword def
    • The function parameters are a set of symbols (names or strings) inside parentheses.
    • The function definition line must end with a colon.
    • The commands inside a function must be tabbed or spaced relative to the def statement.

    The goto function should raise the pen using the turtle.up() function, use the turtle.goto() function to jump to the location specified by the parameters, and then put down the pen using turtle.down().

    While you are still learning to interpret code, it may also be useful to have print statements in your functions that indicate what is happening. Print statements are probably your most important debugging tool, because you can use them to find out information about both the order of operations and the value of parameters at different times during the process. At the beginning of your goto function, put the following print statement.

        print 'goto(): going to', x, y

    Once you have written the function, test it by using the goto function and drawing some lines. For example, after the function definition, but before the raw_input() call, you could do the following.

    print 'running main code'
    goto(100, 100)
    turtle.forward(50)
    goto(-100, 50)
    turtle.forward(50)
    

    Note the difference between the function goto() and turtle.goto(). Without the turtle prefix, python will call the function you defined. With the turtle prefix, python will call the goto function in the turtle package.

    It's also important to start thinking about where functions and top-level code are placed relative to each other in your file. Top-level code should always come after all function definitions. If you intersperse code and functions, it will lead to confusion and be difficult to debug or modify.

    Execute your code and make sure your goto function works properly. Note what it prints out and make sure you understand the order of operation.


  8. Now we want to make a block function that draws a rectangular shape of a given size in a given location. This function will need four parameters: x location, y location, width, and height. Call the function block, and define it after your goto function. The function should jump to the given location and draw the rectangle with the given width and height. You can call functions from other functions, so your block function will probably start with the following. Keep including print statements so you can follow the order in which the computer executes the code.

    Copy and paste the following into your file. Then select all of the text in your file (cmd-a) and select the Entab option under the Text menu. This makes sure all of your white space is properly formatted.

    def block(x, y, width, height):
        goto(x, y)
    
        print 'block(): drawing block of size', width, height
        # tell the turtle to go foward by width
        # tell the turtle to turn left by 90 degrees
        # tell the turtle to go forward by height
        # tell the turtle to turn left by 90 degrees
        # repeat the above 4 commands
    

    Remove your old main code (the goto and turtle.forward commands) and replace them with a couple of calls to the block function.


  9. Now we want to make a function that draws a bunch of blocks relative to a central location. Call the function bunchOfBlocks. It should take three parameters: x location, y location, and a scale factor. A scale factor of 1.0 should draw the shape in its natural size.

    The first command in the function should be a print statement. The remainder of the function should call the block function 2-4 times with blocks in different locations relative to the given (x, y) location. The example below shows the function definition, the print statement, and three calls to the block function. Note the use of the scale factor in both the location offsets and the size of the block.

    def bunchOfBlocks(x, y, scale):
        print 'bunchOfBlocks(): drawing blocks at location', x, y
    
        # put several calls to the block function here
        block(x, y, 45*scale, 90*scale)
        block(x+15*scale, y+90*scale, 15*scale, 30*scale)
        block(x+20*scale, y+120*scale, 5*scale, 15*scale)
    

    The picture below is result of three calls to the bunchOfBlocks function. Note that the relative sizes and locations of the blocks are all identical, despite different scale factors.

    Make your own bunchOfBlocks function. If you want, just make some changes to the code above and see what happens. Then change the main code in your file to call the bunchOfBlocks function several times with different x, y, and scale parameters. Then run your file.

  10. Now we can have some fun with our bunchOfBlocks function. At the top of your file, put the following statement.
    import random

    Replace the top-level code (code outside of any function definitions) with the following. Note the use of the function random.random() in both the x, y location and scale factor. The function returns a random number between 0 and 1.0. The for expression executes the code inside the block the number of times specified in the argument to the range function.

    for i in range(10):
        bunchOfBlocks(random.random()*600 - 300,
                      random.random()*600 - 300,
                      random.random()*2 + 0.5 )
    

The goal of this assignment is to give you practice in creating and using functions and function parameters in python. You'll continue to create shapes using turtle graphics, just like we did in lab. You will use those shapes to create an underwater scene.

Remember that you need to write the code yourself. You can discuss the assignment and ideas regarding the assignment with others in English, but not Python. If you have questions about your python (other than very simple syntax questions), please ask one of the lab assistants or professors. Please see the syllabus for guidelines on collaboration and attribution.


Assignment

  1. Make a copy of your lab2.py file and rename it shapes.py. Delete the main code from the file so that only the import statements and the functions remain. You should not have any top level code in the shapes.py file. It is meant to form a library of shape-drawing functions that other code files will be able to import and use.

    Your first task is to create a main.py file that imports your shapes.py functions. To do this, create a file called main.py in a text editor, and at the top (after your name and a date) put the following.

    import shapes

    In your main.py file, make a function called undersea. Put a single call to the undersea function at the end of the file, followed by a raw_input call.

    Write some python code in your undersea function that calls some of the functions from your shapes.py file to draw a simple image (like a few blocks). Note that to call a function from your shapes file you have to prepend shapes. in front of the function name. For example shapes.block(10, 10, 20, 30) would be how you would call the block function.

    You can run your main.py file using:

    python main.py

    You don't need to save this image, as you'll be replacing the code in the undersea function later on.

  2. In your shapes.py file, make 2 more functions for drawing basic shapes like the block function from lab. Basic shapes should take in at least an x location, y location, and size information. For example, you could make a triangle function or a hexagon function with the size value being the length of a side, in pixels. Put a print statement at the beginning of each function and test them out by calling the functions in your main.py function.

    Each of your shapes should draw properly no matter where it is drawn on the screen, what the size parameter is, or what the orientation of the turtle happens to be when the shape function executes. Test this out for all of your functions.

    In your writeup, include a picture demonstrating that your shape functions work properly. This is required picture 1.

    When you have a picture or scene you want to save, make a screen capture of it (Command-shift-4 for a Mac). The picture will automatically be saved to a file on your Desktop. You can then move and rename it to a more appropriate location. Make sure the entire scene is visible in the image.

  3. In the shapes.py file, make 2 functions that draw iconic versions of more complex things that are going to be in your scene. These aggregate shapes might be coral, rocks, or a creature. They should incorporate several of the simpler shapes from your library.

    When you define these aggregate shape functions, they should all take at least three parameters. The first two (x0, y0) will define the starting location for the shape and the third should define the scale of the shape. A scale factor of 1.0 should draw the shape in its natural size. Feel free to add additional parameters as you like to control other aspects of the shape. As with the basic shapes, your shapes should draw properly at any scale, location, or turtle orientation.

    Tips for making aggregate shapes: The goal of the next couple of assignments is to get you thinking about processes, organization, and taking advantage of repetition and the use of variables in place of hard numbers. The latter, in particular, is a form of generalization. If you can use variables instead of numbers, then a single function can serve many purposes.

    The following is a quick note on one way of thinking about making an aggregate shape, which is a more complex shape that is made out of simpler shapes. In the case below, a chair, or bench, is made from three blocks. The challenge is to describe the relative location of the blocks in such a way that the function will draw the chair appropriately no matter the location or size.

    The process is as follows.

    • Draw the complex object you want to create out of the simpler objects on a piece of paper. Graph paper is nice because you can count blocks, but you can also just eyeball relative measurements. The image below is an image of a chair built from three blocks.

    • Next, pick one of the blocks as the main block and circle its lower left corner. In the example above, it is the red circle. This is the origin of the chair shape.
    • Make a table with a row for each block, similar to the one to the right of the grid. For each block find the offset of its lower left corner from the origin. In the example, block A has offsets of (0, 0) because its lower left corner is the origin; block B has an offset of (-1, 5). Then write down the width and height of each block. In the example, block A has a width of 1 and a height of 5; block B has a width of 8 and a height of 1.
    • Now you are ready to write code. For each block in your shape, use the block function call as given in the figure, substituting in the numbers from the table for xoff, yoff, width, and height. For example, the proper function to create the chair would be as follows.
      def chair( x, y, scale ):
          # block A
          block(x, y, 1*scale, 5*scale)
          # block B
          block(x - 1 * scale, y + 5 * scale, 8*scale, 1*scale)
          # block C
          block(x + 6*scale, y, 1*scale, 12*scale)
      
    • If you call the chair function with different arguments, it should draw the chair in different locations with different sizes. Try out the following as part of your main code.
      chair(-100, 0, 10)
      chair(0, 0, 5)
      chair(100, 0, 20)
      
    • Note that this method of defining the relative locations of the blocks will not work if you rotate the turtle. That requires a more sophisticated formula for the x and y offsets. But since a tipsy chair wouldn't work very well, that's ok for this example.

    In both of of your new functions, use one or more of the basic shape functions to draw the shape. For example, a building might incorporate the block and triangle functions. Test out your new functions before proceeding.

    In your writeup, include a picture demonstrating that each of your aggregate shape functions work properly. This is required picture 2.

  4. Now it is time to replace the code in your undersea function. Using the functions you created in steps 2 and 3, create an underwater scene. In your scene, make use of the fact that your functions can draw the shapes with different sizes in different locations. You can also use the random package and for loops to make more complex scenes.

    Save the image and include it in your writeup. This is required picture 3.

  5. Make a second scene function, otherScene in your main.py file. This one can be any scene that doesn't include Colby.

    Save the image and include it in your writeup. This is required picture 4.


Extensions

Each assignment will have a set of suggested extensions. The required tasks constitute about 85% of the assignment, and if you do only the required tasks and do them well you will earn a B+. To earn a higher grade, you need to undertake one or more extensions. The difficulty and quality of the extension or extensions will determine your final grade for the assignment. One complex extension, done well, or 2-3 simple extensions are typical.

  • Make additional basic or complex shape functions that make use of your other shape functions.
  • Make additional scenes that show creative use of functions, loops, or randomness.
  • Make use of the for loop control structure from the lab (you can also read ahead in the book).
  • Make use of the random package to make your scene more interesting. For example, if you import the random package into your program:

    import random

    Then you can use it to generate random integers using the following expression, where a and b are the upper and lower bounds of the random numbers you want.

    random.randint(a, b)

  • Make use of other turtle properties such as line width, color, and fill capability to make your shapes more interesting.
  • Add additional parameters to your shapes to control properties like line width and color.
  • Use a Python feature that is new to you.

Writeup and Hand-in

Turn in your code by putting it into your private handin directory on the Courses server. All files should be organized in a folder titled "Proj2" and you should include only those files necessary to run the program. We will grade all files turned in, so please do not turn in old, non-working, versions of files.

Make a new wiki page for your assignment. Put the label cs151s14project2 in the label field on the bottom of the page. But give the page a meaningful title (e.g. Kyle's project 2).

In general, your intended audience for your write-up is your peers not in the class. Your goal should be to be able to use it to explain to friends what you accomplished in this project and to give them a sense of how you did it. Follow the outline below.

  • A brief summary of the task, in your own words. This should be no more than a few sentences. Give the reader context and identify the key purpose of the assignment.
  • A description of your solution to the tasks, including any images you created. This should be a description of the form and functionality of your final code. You may want to incorporate code snippets in your description to point out relevant features. Note any unique computational solutions you developed. Code snippets should be small segments of code--usually less than a whole function--that demonstrate a particular concept. If you find yourself including more than 5-10 lines of code, it's probably not a snippet.
  • A description of any extensions you undertook, including images demonstrating those extensions. If you added any modules, functions, or other design components, note their structure and the algorithms you used.
  • A brief description (1-3 sentences) of what you learned.
  • A list of people you worked with, including students who took the course in previous semesters, TAs, and professors. Include in this list anyone whose code you may have seen.
  • Don't forget to label your writeup so that it is easy for others to find. For this lab, use cs151s14project2

This is an adaptation of this project by Stephanie R. Taylor.