Programming Assignment 6: Recursive Fractals

Initial Due Date: 2024-10-31 8:00AM
Final Due Date: 2024-11-14 4:15PM

This assignment is an individual assignment.

Background

This assignment has four components, “GCD”, “Stairs”, “Sierpinski”, and “Recursive H”, all of which should be implemented in the same file. The purpose of this assignment is to practice recursion and recursive implementations, so all function must be implemented recursively (the only loops allowed are for drawing triangles).

Specification

Downloading the starter file pa6_fractals.py. Replace each pass statement in the required functions with your implementation as you develop it.

At a minimum your submission should have:

  1. A recursive function named gcd that returns the greatest common denominator of its two positive integer parameters.
  2. A recursive function named stairs that has two parameters length and levels, and draws stairs exactly as described in the guide.
  3. A recursive function named sierpinski that has two parameters length and level, and draws the Sierpinksi triangle exactly as described in the guide.
  4. A function named recursive_h that has two parameters length and level, and draws the recursive H exactly as described in the guide.

Gradescope will test your functions by generating images using your code and the reference implementation and comparing them pixel by pixel. Thus your functions need to match the specification exactly.

Guide

Greatest Common Divisor

One of the oldest algorithms, dating back to the 5th century BCE, is Euclid’s algorithm for computing the greatest common divisor (GCD) of two integers. It works as follows:

  • Let \(a\), \(b\) be positive integers.
  • Let \(r\) be the remainder of \(a / b\).
  • If \(r\) is 0, then the gcd(a,b) is \(b\).
  • Otherwise, gcd(a,b) is the same as gcd(b,r).

Write a recursive function gcd(a,b) that implements Euclid’s algorithm to return the GCD of any two positive integer parameters a and b. Your function should return the GCD and not use the input or print functions. For example:

>>> gcd(36, 81)
9
>>> gcd(13, 6)
1

Stairs

Stairs

Using turtle graphics, write a recursive function stairs(length, levels) to draw pictures such as those shown above. Assume the turtle starts in the top left corner, facing right (always test using stairs_demo, which will set the turtle’s starting position for you). The first square should have a side length of length, and each subsequent square should be 1/2 the size. There should be a total of levels squares. Do this all in one continuous drawing motion; do not pick up the pen, and do not repeat any lines. To do so, employ the following strategy:

Powers of 2 work well for starting lengths.
  1. draw a half-square,
  2. recursively draw the rest of the squares,
  3. draw the last half-square to return to the starting position and orientation.

Sierpinski Triangle

Next we’ll draw another interesting fractal shape called the Sierpinski triangle. Here is what it looks like taken out to six levels:

Sierpinksi triangle

There are many ways to draw this shape, but here is one. Start with the assumption that when you draw the triangle, the turtle will be end up in the same position and orientation it started from. Given that assumption, drawing a level n triangle just involves drawing three level n-1 triangles with sides half the length, like shown below.

Sierpinksi triangle recursive relationship

To draw a level n triangle, draw the first level n-1 triangle, move to the location of the next n-1 triangle, draw again, move to the last triangle and draw that one, then restore the turtle to the original location and orientation. Level 0 is just a single equilateral triangle.

More specifically, here are the drawings for levels 0, 1, 2, and 3:

Sierpinksi triangle for generations 0, 1, 2, and 3 (left to right)

Write a function sierpinski(length, iterations). The parameter length will be the length of the base of the figure and iterations will determine how many generations to draw. Use the sierpinski_demo function in the starter file for testing.

Recursive H

A recursive H is an H where the end of each vertical line of the H may have another recursive H. The recursive H is defined by a level.

Level 1 “H”

Level 2 “H”

Level 3 “H”

A level 1 recursive H is just an H with size 10 dots at its tips, while a level 2 recursive H has another smaller H at the end of each line. A level 3 recursive H has another smaller H at the end of the each of these Hs. Notice that the smallest level of H always ends in a size 10 dot (e.g., a “level 0” H).

The size of an H is defined by the parameter length. The vertical bars are of length length and the horizontal bar of length 2 * length. Assume that the turtle starts in the middle of the drawing window, and thus the middle of the horizontal bar, facing to the right. At the end of drawing, the turtle should be at the same position and heading as when it started.

Level 1 “H” showing starting/ending position of the turtle

Write a function named recursive_h that takes two parameters: the length of the vertical bars (the horizontal bar will be twice the length) and number of recursive levels to draw. Level 1 should just draw an H with four dots at the end, level 2 an H with 4 smaller H’s at the end of the vertical bars, and so on. The smallest H’s will all have black dots at the end of their vertical bars. As shown in the images, the length of the vertical bar (length) should be halved with each level. Use the recursive_h_demo function in the starter file for testing.

General suggestions

  • Walk through the four steps for defining a recursive function described in class. When doing this for graphical components, the key is often trying to finish a statement like: “A recursive H is …”, where somewhere after “is” you should end up using the term “recursive H”. For example, a broccoli is a line with three smaller broccolis off of the end of that line. A tree is a line with two smaller trees off the end of that line. Once you have defined your recursive relationship, think about what the base case would be.
  • It can help to mentally walk through your recursive steps and draw out the figure by hand. Be the turtle!
  • If relevant, think about how you might decompose the problem into smaller functions to make your code easier to debug and read.
  • The default drawing speed for turtle is quite slow. You could consider adding t.speed(0) to your demo functions to speed up the drawing somewhat. Or, to get the drawing immediately (without watching the turtle draw at all) use t.tracer(False) as in the provided demo functions.

Creativity suggestions

Here are some possible creativity additions, although you are encouraged to include your own ideas. Make sure to document your additions in the docstring comment at the top of the file.

  • [1 point] Add some color to the recursive H that changes with the size or other recursive feature so that your function can still be invoked with the two parameters specified above.

  • [1-2 points] Implement another recursive function that draws a different recursive structure (for example using circles or triangles).

  • [2 points] Challenge Problem 1: “Sum Nested”

    Write a recursive function sum_nested(a_list) that takes in a nested list of integers as its parameter and returns the sum of all the lists. A nested list of integers contains 0 or more integers and 0 or more nested lists of integers. For example:

    >>> sum_nested([])
    0
    >>> sum_nested([1, 2, 3, 4, 5])
    15
    >>> sum_nested([1, [2, [3]], 4, 5])
    15
    >>> sum_nested([1, [2, [3], []], [], 4, 5])
    15
    >>> sum_nested([[2, 4, 6, 8], [3, 6, 9], [1, [1, 10]]])
    50

    As a hint, you can use the isinstance function to check the type of a value, e.g.,

    >>> isinstance([1,2], list)
    True
    >>> isinstance(1, list)
    False  

Note: Do not change the output of the required functions, instead create new functions with your creativity additions. For example, if you modify the color of the recursive H, defined a new function (e.g., recursive_h_creativity) with those additional features. That way the required functions continue to match the specification exactly (pixel for pixel).

When you’re done

Make sure that your program is properly documented:

  • You should have a docstring at the very beginning of the file briefly describing your program and stating your name, section and creativity additions.
  • Each function should have an appropriate docstring (including arguments and return value if applicable).
  • Other miscellaneous inline/block comments if the code might otherwise be unclear.

In addition, make sure that you’ve used good code design and style (including helper functions where useful, meaningful variable names, constants where relevant, vertical white space, removing “dead code” that doesn’t do anything, removing testing code, etc.). For this assignment, when run your program should call no functions and produce no output.

When you’re done, invoke the function drawing_demo() to run all three drawing functions, and then take a screenshot.

Submit your program via Gradescope. Your program program file must be named pa6_fractals.py and your screenshot named pa6_screenshot.png (produced from the output of drawing_demo()). You can submit multiple times, with only the most recent submission (before the due date) graded. Note that the tests performed by Gradescope are limited. Passing all of the visible tests does not guarantee that your submission correctly satisfies all of the requirements of the assignment.

Gradescope will import your file for testing so that make sure that no code executes on import. That is when imported your program should not try to draw any images.

Grading

Assessment Requirements
Revision needed Some but not all tests are passing.
Meets Expectations All tests pass, the required functions are implemented correctly and your implementation uses satisfactory style.
Exemplary All requirements for Meets Expectations, 2 creativity points, and your implementation is clear, concise, readily understood, and maintainable.