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:
- A recursive function named
gcd
that returns the greatest common denominator of its two positive integer parameters. - A recursive function named
stairs
that has two parameterslength
andlevels
, and draws stairs exactly as described in the guide. - A recursive function named
sierpinski
that has two parameterslength
andlevel
, and draws the Sierpinksi triangle exactly as described in the guide. - A function named
recursive_h
that has two parameterslength
andlevel
, 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 asgcd(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
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:
- draw a half-square,
- recursively draw the rest of the squares,
- 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:
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.
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:
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.
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.
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) uset.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. |