Koch snowflake in Python with Turtle After finding a piece of code on StackOverflow that drew the Koch snowflake fractal, I made a ton of modifications to it and used it to produce an animation divided in two parts:


Constant size, recursion depth increasing.
Constant recursion depth, size increasing.


You can see the animation here.

I am interested mainly in:


Have I got too many constants? I like them because they make the program easy to customize, but 12 constants looks like a lot.
Any idea to avoid repetition in these blocks of code?

write_as_title(\
    "Constant size, recursion depth increasing.", font=FONT)
time.sleep(WRITING_SLEEPING_INTERVAL)

for deepness in range(MAX_DEPTH):
    costum_reset()
    snowflake(deepness, SIZE_WHEN_CONSTANT_DEPTH)
    time.sleep(DRAWING_SLEEPING_INTERVAL)




costum_reset()
write_as_title(\
    "Constant recursion depth, size increasing.", font=FONT)
time.sleep(WRITING_SLEEPING_INTERVAL)
for size in RANGE_OF_SIZES:
    costum_reset()
    snowflake(DEPTH_WHEN_CONSTANT_SIZE, size)


They are similar but I cannot quite abstract the similarity out.




from __future__ import division

import turtle
import time

WIDTH, HEIGHT = 800, 600
FONT = ("Arial", 25, "normal")
DRAWING_SLEEPING_INTERVAL = 0.5
WRITING_SLEEPING_INTERVAL = 2.5
MAX_DEPTH = 5
SIZE_WHEN_CONSTANT_DEPTH = 300
DEPTH_WHEN_CONSTANT_SIZE = 4
RANGE_OF_SIZES = range(0, 5000, 5)
SIDES_OF_AN_EQUILATERAL_TRIANGLE = 3
EQUILATERAL_TRIANGLE_INTERNAL_ANGLE = 120
DRAWING_START_X = - 200
DRAWING_START_Y = 150

def snowflake(n, size=300):

    for _ in range(SIDES_OF_AN_EQUILATERAL_TRIANGLE):
        snowflake_edge(n, size)
        turtle.right(EQUILATERAL_TRIANGLE_INTERNAL_ANGLE)

    turtle.update()

def snowflake_edge(n, size=300):
    if n == 0:
        turtle.forward(size)
        return

    for movement in (lambda: turtle.left(EQUILATERAL_TRIANGLE_INTERNAL_ANGLE // 2),
                   lambda: turtle.right(EQUILATERAL_TRIANGLE_INTERNAL_ANGLE),
                   lambda: turtle.left(EQUILATERAL_TRIANGLE_INTERNAL_ANGLE // 2)):
        snowflake_edge(n - 1, size / SIDES_OF_AN_EQUILATERAL_TRIANGLE)
        movement()

    snowflake_edge(n - 1, size / SIDES_OF_AN_EQUILATERAL_TRIANGLE)

def costum_reset():
    turtle.reset()
    turtle.penup()
    turtle.setx( DRAWING_START_X )
    turtle.sety( DRAWING_START_Y )
    turtle.pendown()

def write_as_title(title, font=FONT):
    turtle.setx(- (WIDTH // 2.5) )
    turtle.sety(HEIGHT // 3)
    turtle.write(title, font=font)

if __name__ == "__main__":
    turtle.setup(width=WIDTH, height=HEIGHT)
    turtle.hideturtle()
    turtle.tracer(0, 0)

    write_as_title(\
        "Constant size, recursion depth increasing.", font=FONT)
    time.sleep(WRITING_SLEEPING_INTERVAL)

    for deepness in range(MAX_DEPTH):
        costum_reset()
        snowflake(deepness, SIZE_WHEN_CONSTANT_DEPTH)
        time.sleep(DRAWING_SLEEPING_INTERVAL)

    costum_reset()
    write_as_title(\
        "Constant recursion depth, size increasing.", font=FONT)
    time.sleep(WRITING_SLEEPING_INTERVAL)
    for size in RANGE_OF_SIZES:
        costum_reset()
        snowflake(DEPTH_WHEN_CONSTANT_SIZE, size)
Your code reads well and is easily understandable. On top of also finding that you might have too much constant (more on that in a little bit), I just have a few nitpicks:

whitespace in expressions is weird sometimes;
a docstring would help understanding the recursion involved in snowflake_edge;
you don't need the newline continuation (\) inside parenthesis, they already act as an implicit one;
you allow to parametrize write_as_title with a custom font but nothing similar is done with other kind of parameters.

On reducing the similarities between the two drawings
I would have put a call to costum_reset (costum? really?) at the beginning of both write_as_title and snowflake since both drawings need a clear state to begin with.
I would also put time.sleep(WRITING_SLEEPING_INTERVAL) at the end of write_as_title. Considering its value, it won't hurt anyone in an interactive session and is still needed in a program for people to be able to read the text.
This way, the main part would look like:
if __name__ == &quot;__main__&quot;:
    turtle.setup(width=WIDTH, height=HEIGHT)
    turtle.hideturtle()
    turtle.tracer(0, 0)

    write_as_title(
        &quot;Constant size, recursion depth increasing.&quot;, font=FONT)    
    for deepness in range(MAX_DEPTH):
        snowflake(deepness, SIZE_WHEN_CONSTANT_DEPTH)
        time.sleep(DRAWING_SLEEPING_INTERVAL)

    write_as_title(
        &quot;Constant recursion depth, size increasing.&quot;, font=FONT)
    for size in RANGE_OF_SIZES:
        snowflake(DEPTH_WHEN_CONSTANT_SIZE, size)

You can even make the two sensible parts two functions if you want to separate the concerns better.
On reducing the amount of constants
Of your 12 constants, a few are really constant, some are arbitrary fixed for the purpose of your application, and some could be tweaked to achieve a better (prettier?) result.
Maybe that allowing to change the latter group using the command line could be more intuitive to tweak the parameters of the display. I personally would keep the following constants:

WIDTH, HEIGHT = 800, 600
FONT = (&quot;Arial&quot;, 25, &quot;normal&quot;)
WRITING_SLEEPING_INTERVAL = 2.5
SIDES_OF_AN_EQUILATERAL_TRIANGLE = 3
EQUILATERAL_TRIANGLE_INTERNAL_ANGLE = 120
DRAWING_START_X = - 200
DRAWING_START_Y = 150

And use argparse to provide default values or custom ones from the command line for:

DRAWING_SLEEPING_INTERVAL
MAX_DEPTH
SIZE_WHEN_CONSTANT_DEPTH
DEPTH_WHEN_CONSTANT_SIZE
SIZES_LIMIT

Note the use of SIZES_LIMIT which will then create the range object RANGE_OF_SIZES.
All of these last &quot;constants&quot; are only used within the if __name__ == '__main__' so it's pretty easy to adapt.