HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Koch snowflake in Python with Turtle

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
turtlesnowflakewithkochpython

Problem

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(

Solution

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__ == "__main__":
    turtle.setup(width=WIDTH, height=HEIGHT)
    turtle.hideturtle()
    turtle.tracer(0, 0)

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

    write_as_title(
        "Constant recursion depth, size increasing.", 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 = ("Arial", 25, "normal")



  • 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 "constants" are only used within the if __name__ == '__main__' so it's pretty easy to adapt.

Code Snippets

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)    
    for deepness in range(MAX_DEPTH):
        snowflake(deepness, SIZE_WHEN_CONSTANT_DEPTH)
        time.sleep(DRAWING_SLEEPING_INTERVAL)

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

Context

StackExchange Code Review Q#115453, answer score: 6

Revisions (0)

No revisions yet.