Search code examples
pythonrecursionturtle-graphicsfractals

Varying recursive function at given depth


I have a recursive function drawing a fractal, using the python turtle module:

def fract(t, order, size):
    if order == 0:
        t.forward(size)

    else:
        for angle in (60, -120, 60, 0):
            fract(t, order - 1, size / 3)
            t.left(angle)

I have another function calling the first function and modifying the last angle, so that the fractals build a circle

def circle(t, order, size):
    for i in range(order):
        fract(t, 2, size)
        t.right(360 / order)

circle(t, 4, 300)

While this works as intended, the real goal is to get the same result in a single recursive function.

Obviously this is not a real programming case, but a task from a python beginner's book, which I'm totally stuck with. I suppose the awkward title of the question reflects my lack of understanding the problem.


Solution

  • I agree with the sentiments of @quamrana on this matter, but let's solve what could be a difficult problem. With a bit of trickery.

    First, your consolidated function has to take four argument as the order argument to circle() isn't related to the order argument to fract(). We'll rename the first of these to sides() as that's what it represents.

    Second, your fract() function isn't completely recursive, it uses iteration internally. I'm going to follow that same pattern in my solution.

    Finally, we need a bit of hidden information to work with -- you could use a defaulted fifth argument that changes internally but instead I'm going to play with the type of sides to achieve this result:

    import turtle as t
    
    def fract(t, sides, order, size):
    
        if order == 0:
            t.forward(size)
    
        elif sides is not None:
            for _ in range(sides):
                fract(t, None, order, size)
                t.right(360 / sides)
        else:
            for angle in (60, -120, 60, 0):
                fract(t, None, order - 1, size / 3)
                t.left(angle)
    
    t.speed('fastest')  # because I have no patience
    
    fract(t, 4, 2, 300)
    
    t.hideturtle()
    
    t.exitonclick()
    

    I believe this achieves your desired result with minimal change to your original code. In addtion to the fract(t, 4, 2, 300) invocation, which produces your original figure, we can also do variations like fract(t, 3, 3, 300):

    enter image description here

    The next problem you might want to tackle is how to center these images on the screen so that fract(t, 5, 1, 300) doesn't fall off the edge.