Sierpinski triangle recursion using turtle graphics
Question:
I am trying to write a program that draws a sierpinski tree with python using turtle. Here is my idea:
import turtle
def draw_sierpinski(length,depth):
window = turtle.Screen()
t = turtle.Turtle()
if depth==0:
for i in range(0,3):
t.fd(length)
t.left(120)
else:
draw_sierpinski(length/2,depth-1)
t.fd(length/2)
draw_sierpinski(length/2,depth-1)
t.bk(length/2)
t.left(60)
t.fd(length/2)
t.right(60)
draw_sierpinski(length/2,depth-1)
window.exitonclick()
draw_sierpinski(500,1)
The program does not reach the 2nd line after the else statement and I don’t know why. Can anyone help me?
Answers:
I don’t think you should be creating the turtle or window object inside the function. Since draw_sierpinski
gets called four times if you originally call it with depth 1, then you’ll create four separate windows with four separate turtles, each one drawing only a single triangle. Instead, I think you should have only one window and one turtle.
import turtle
def draw_sierpinski(length,depth):
if depth==0:
for i in range(0,3):
t.fd(length)
t.left(120)
else:
draw_sierpinski(length/2,depth-1)
t.fd(length/2)
draw_sierpinski(length/2,depth-1)
t.bk(length/2)
t.left(60)
t.fd(length/2)
t.right(60)
draw_sierpinski(length/2,depth-1)
window = turtle.Screen()
t = turtle.Turtle()
draw_sierpinski(500,1)
window.exitonclick()
Result:
These results look pretty good for a depth 1 triangle, but what about when we call draw_sierpinski(100,2)
?
Ooh, not so good. This occurs because the function should draw the shape, and then return the turtle to its original starting position and angle. But as is evident from the depth 1 image, the turtle doesn’t return to its starting position; it ends up halfway up the left slope. You need some additional logic to send it back home.
import turtle
def draw_sierpinski(length,depth):
if depth==0:
for i in range(0,3):
t.fd(length)
t.left(120)
else:
draw_sierpinski(length/2,depth-1)
t.fd(length/2)
draw_sierpinski(length/2,depth-1)
t.bk(length/2)
t.left(60)
t.fd(length/2)
t.right(60)
draw_sierpinski(length/2,depth-1)
t.left(60)
t.bk(length/2)
t.right(60)
window = turtle.Screen()
t = turtle.Turtle()
draw_sierpinski(100,2)
window.exitonclick()
Result:
Here you go.
import turtle
def sier(side, level):
if level == 1:
for i in range(3):
turtle.fd(side)
turtle.left(120)
else:
sier(side/2, level-1)
turtle.fd(side/2)
sier(side/2, level-1)
turtle.bk(side/2)
turtle.left(60)
turtle.fd(side/2)
turtle.right(60)
sier(side/2, level-1)
turtle.left(60)
turtle.bk(side/2)
turtle.right(60)
def main():
sier(200, 4)
if __name__ == '__main__':
main()
turtle.mainloop()
This is the best code for sierpinski triangle
def sierpinski(a, n):
if n == 0:
t.begin_fill()
for i in range(3):
t.fd(a)
t.lt(120)
t.end_fill()
return
sierpinski(a / 2, n - 1)
t.pu()
t.fd(a / 2)
t.pd()
sierpinski(a / 2, n - 1)
t.pu()
t.lt(120)
t.fd(a / 2)
t.rt(120)
t.pd()
sierpinski(a / 2, n - 1)
#
# We should return home! This is important!
#
t.pu()
t.lt(60)
t.bk(a / 2)
t.rt(60)
t.pd()
from turtle import *
import turtle
t = turtle.Turtle()
Window = turtle.Screen()
Window.bgcolor('white')
turtle.color('white')
goto(-200, -200)
def serp_tri(side, level):
if level == 1:
for i in range(3):
turtle.color('black')
turtle.ht()
turtle.fd(side)
turtle.left(120)
turtle.speed(100000)
else:
turtle.ht()
serp_tri(side/2, level-1)
turtle.fd(side/2)
serp_tri(side/2, level-1)
turtle.bk(side/2)
turtle.left(60)
turtle.fd(side/2)
turtle.right(60)
serp_tri(side/2, level-1)
turtle.left(60)
turtle.bk(side/2)
turtle.right(60)
turtle.speed(100000)
def main():
serp_tri(400, 8)
if __name__ == '__main__':
main()
turtle.mainloop()
I looked at a similar program and wrote this using some of the same things. This will give you the biggest triangle that you can get. Hope this helps!
As a suggestion here is my solution. Any comments are very much appreciated as it seems like it is still not the most efficient algorithm.
import turtle
def sier(tur, order, size):
""" Draw Sierpinski triangle """
if order == 0:
for _ in range(3):
tur.forward(size)
tur.left(120)
else:
step = size / 2
for t1, m1, t2, m2 in [(0, step, 0, 0),
(120, step, -120, 0),
(-60, step, 60, -(step))]:
sier(tur, order - 1, step)
tur.left(t1)
tur.forward(m1)
tur.left(t2)
tur.forward(m2)
if __name__ == '__main__':
odr = int(input("Enter the order: "))
sz = int(input("Enter the size: "))
root = turtle.Screen()
root.bgcolor("lightgreen")
alex = turtle.Turtle()
alex.color('blue')
alex.speed(100)
sier(alex, odr, sz)
root.mainloop()
# PEP8 Verified
'''
The Sierpinski function relies heavily on the getMid function. getMid takes
as arguments two endpoints and returns the point halfway between them. In
addition, this program has a function that draws a filled triangle using
the begin_fill and end_fill turtle methods.
'''
import turtle
def drawTriangle(points, color, myTurtle):
myTurtle.fillcolor(color)
myTurtle.up()
myTurtle.goto(points[0][0], points[0][1])
myTurtle.down()
myTurtle.begin_fill()
myTurtle.goto(points[1][0], points[1][1])
myTurtle.goto(points[2][0], points[2][1])
myTurtle.goto(points[0][0], points[0][1])
myTurtle.end_fill()
def getMid(p1, p2):
return ((p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2)
def sierpinski(points, degree, myTurtle):
colormap = ['blue', 'red', 'green', 'white', 'yellow', 'violet', 'orange']
drawTriangle(points, colormap[degree], myTurtle)
if degree > 0:
sierpinski([points[0],
getMid(points[0], points[1]),
getMid(points[0], points[2])],
degree-1, myTurtle)
sierpinski([points[1],
getMid(points[0], points[1]),
getMid(points[1], points[2])],
degree-1, myTurtle)
sierpinski([points[2],
getMid(points[2], points[1]),
getMid(points[0], points[2])],
degree-1, myTurtle)
def main():
myTurtle = turtle.Turtle()
myWin = turtle.Screen()
myPoints = [[-100, -50], [0, 100], [100, -50]]
sierpinski(myPoints, 3, myTurtle)
myWin.exitonclick()
main()
starting from Navneet Sinha, i would suggest this:
def sierpinski(t,order,size):
try:
order=int(order)
size=float(size)
if order==0:
for i in range(0,3):
t.pendown()
t.forward(size)
t.left(120)
t.penup()
else:
for (angle,move) in ([0,size],[60,-size],[-60,-size]):
sierpinski(t,order-1,size/2)
t.right(angle)
t.forward(move/2)
t.left(angle)
except ValueError:
None
def test_turtle():
import turtle
screen=turtle.Canvas()
tess=turtle.Turtle()
tess.shape("arrow")
tess.shapesize(0.2)
tess.speed(0)
ords=input("define the order of the fractal: ")
sz=input("define the size of the segment: ")
tess.penup()
tess.backward(float(sz)/2)
tess.right(90)
tess.forward(float(sz)/3)
tess.left(90)
tess.pendown()
sierpinski(tess,ords,sz)
screen.mainloop()
test_turtle()
The code is really simple:
def sierpinski(size:int,depth:int,up:bool = True):
if depth==0:
t.forward(size)
return
size = size/2
depth = depth-1
left = lambda deg:t.left(deg)
right = lambda deg:t.right(deg)
if not up:
k = left
left = right
right = k
del k
left(60)
sierpinski(size,depth,not up)
right(60)
sierpinski(size,depth,up)
right(60)
sierpinski(size,depth,not up)
left(60)
A recursive way i found to draw what i think you were expecting. My goal was actually drawing it using thousands of dots but after breaking my head a little, i’ve settled for this solution:
import turtle
def sierpinski(length, level):
if level == 0:
for i in range(3):
turtle.forward(length)
turtle.left(120)
else:
sierpinski(length/2, level-1)
turtle.forward(length/2)
sierpinski(length/2, level-1)
turtle.backward(length/2)
turtle.left(60)
turtle.forward(length/2)
turtle.right(60)
sierpinski(length/2, level-1)
turtle.left(60)
turtle.backward(length/2)
turtle.right(60)
turtle.speed(0)
sierpinski(200, 4)
I am trying to write a program that draws a sierpinski tree with python using turtle. Here is my idea:
import turtle
def draw_sierpinski(length,depth):
window = turtle.Screen()
t = turtle.Turtle()
if depth==0:
for i in range(0,3):
t.fd(length)
t.left(120)
else:
draw_sierpinski(length/2,depth-1)
t.fd(length/2)
draw_sierpinski(length/2,depth-1)
t.bk(length/2)
t.left(60)
t.fd(length/2)
t.right(60)
draw_sierpinski(length/2,depth-1)
window.exitonclick()
draw_sierpinski(500,1)
The program does not reach the 2nd line after the else statement and I don’t know why. Can anyone help me?
I don’t think you should be creating the turtle or window object inside the function. Since draw_sierpinski
gets called four times if you originally call it with depth 1, then you’ll create four separate windows with four separate turtles, each one drawing only a single triangle. Instead, I think you should have only one window and one turtle.
import turtle
def draw_sierpinski(length,depth):
if depth==0:
for i in range(0,3):
t.fd(length)
t.left(120)
else:
draw_sierpinski(length/2,depth-1)
t.fd(length/2)
draw_sierpinski(length/2,depth-1)
t.bk(length/2)
t.left(60)
t.fd(length/2)
t.right(60)
draw_sierpinski(length/2,depth-1)
window = turtle.Screen()
t = turtle.Turtle()
draw_sierpinski(500,1)
window.exitonclick()
Result:
These results look pretty good for a depth 1 triangle, but what about when we call draw_sierpinski(100,2)
?
Ooh, not so good. This occurs because the function should draw the shape, and then return the turtle to its original starting position and angle. But as is evident from the depth 1 image, the turtle doesn’t return to its starting position; it ends up halfway up the left slope. You need some additional logic to send it back home.
import turtle
def draw_sierpinski(length,depth):
if depth==0:
for i in range(0,3):
t.fd(length)
t.left(120)
else:
draw_sierpinski(length/2,depth-1)
t.fd(length/2)
draw_sierpinski(length/2,depth-1)
t.bk(length/2)
t.left(60)
t.fd(length/2)
t.right(60)
draw_sierpinski(length/2,depth-1)
t.left(60)
t.bk(length/2)
t.right(60)
window = turtle.Screen()
t = turtle.Turtle()
draw_sierpinski(100,2)
window.exitonclick()
Result:
Here you go.
import turtle
def sier(side, level):
if level == 1:
for i in range(3):
turtle.fd(side)
turtle.left(120)
else:
sier(side/2, level-1)
turtle.fd(side/2)
sier(side/2, level-1)
turtle.bk(side/2)
turtle.left(60)
turtle.fd(side/2)
turtle.right(60)
sier(side/2, level-1)
turtle.left(60)
turtle.bk(side/2)
turtle.right(60)
def main():
sier(200, 4)
if __name__ == '__main__':
main()
turtle.mainloop()
This is the best code for sierpinski triangle
def sierpinski(a, n):
if n == 0:
t.begin_fill()
for i in range(3):
t.fd(a)
t.lt(120)
t.end_fill()
return
sierpinski(a / 2, n - 1)
t.pu()
t.fd(a / 2)
t.pd()
sierpinski(a / 2, n - 1)
t.pu()
t.lt(120)
t.fd(a / 2)
t.rt(120)
t.pd()
sierpinski(a / 2, n - 1)
#
# We should return home! This is important!
#
t.pu()
t.lt(60)
t.bk(a / 2)
t.rt(60)
t.pd()
from turtle import *
import turtle
t = turtle.Turtle()
Window = turtle.Screen()
Window.bgcolor('white')
turtle.color('white')
goto(-200, -200)
def serp_tri(side, level):
if level == 1:
for i in range(3):
turtle.color('black')
turtle.ht()
turtle.fd(side)
turtle.left(120)
turtle.speed(100000)
else:
turtle.ht()
serp_tri(side/2, level-1)
turtle.fd(side/2)
serp_tri(side/2, level-1)
turtle.bk(side/2)
turtle.left(60)
turtle.fd(side/2)
turtle.right(60)
serp_tri(side/2, level-1)
turtle.left(60)
turtle.bk(side/2)
turtle.right(60)
turtle.speed(100000)
def main():
serp_tri(400, 8)
if __name__ == '__main__':
main()
turtle.mainloop()
I looked at a similar program and wrote this using some of the same things. This will give you the biggest triangle that you can get. Hope this helps!
As a suggestion here is my solution. Any comments are very much appreciated as it seems like it is still not the most efficient algorithm.
import turtle
def sier(tur, order, size):
""" Draw Sierpinski triangle """
if order == 0:
for _ in range(3):
tur.forward(size)
tur.left(120)
else:
step = size / 2
for t1, m1, t2, m2 in [(0, step, 0, 0),
(120, step, -120, 0),
(-60, step, 60, -(step))]:
sier(tur, order - 1, step)
tur.left(t1)
tur.forward(m1)
tur.left(t2)
tur.forward(m2)
if __name__ == '__main__':
odr = int(input("Enter the order: "))
sz = int(input("Enter the size: "))
root = turtle.Screen()
root.bgcolor("lightgreen")
alex = turtle.Turtle()
alex.color('blue')
alex.speed(100)
sier(alex, odr, sz)
root.mainloop()
# PEP8 Verified
'''
The Sierpinski function relies heavily on the getMid function. getMid takes
as arguments two endpoints and returns the point halfway between them. In
addition, this program has a function that draws a filled triangle using
the begin_fill and end_fill turtle methods.
'''
import turtle
def drawTriangle(points, color, myTurtle):
myTurtle.fillcolor(color)
myTurtle.up()
myTurtle.goto(points[0][0], points[0][1])
myTurtle.down()
myTurtle.begin_fill()
myTurtle.goto(points[1][0], points[1][1])
myTurtle.goto(points[2][0], points[2][1])
myTurtle.goto(points[0][0], points[0][1])
myTurtle.end_fill()
def getMid(p1, p2):
return ((p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2)
def sierpinski(points, degree, myTurtle):
colormap = ['blue', 'red', 'green', 'white', 'yellow', 'violet', 'orange']
drawTriangle(points, colormap[degree], myTurtle)
if degree > 0:
sierpinski([points[0],
getMid(points[0], points[1]),
getMid(points[0], points[2])],
degree-1, myTurtle)
sierpinski([points[1],
getMid(points[0], points[1]),
getMid(points[1], points[2])],
degree-1, myTurtle)
sierpinski([points[2],
getMid(points[2], points[1]),
getMid(points[0], points[2])],
degree-1, myTurtle)
def main():
myTurtle = turtle.Turtle()
myWin = turtle.Screen()
myPoints = [[-100, -50], [0, 100], [100, -50]]
sierpinski(myPoints, 3, myTurtle)
myWin.exitonclick()
main()
starting from Navneet Sinha, i would suggest this:
def sierpinski(t,order,size):
try:
order=int(order)
size=float(size)
if order==0:
for i in range(0,3):
t.pendown()
t.forward(size)
t.left(120)
t.penup()
else:
for (angle,move) in ([0,size],[60,-size],[-60,-size]):
sierpinski(t,order-1,size/2)
t.right(angle)
t.forward(move/2)
t.left(angle)
except ValueError:
None
def test_turtle():
import turtle
screen=turtle.Canvas()
tess=turtle.Turtle()
tess.shape("arrow")
tess.shapesize(0.2)
tess.speed(0)
ords=input("define the order of the fractal: ")
sz=input("define the size of the segment: ")
tess.penup()
tess.backward(float(sz)/2)
tess.right(90)
tess.forward(float(sz)/3)
tess.left(90)
tess.pendown()
sierpinski(tess,ords,sz)
screen.mainloop()
test_turtle()
The code is really simple:
def sierpinski(size:int,depth:int,up:bool = True):
if depth==0:
t.forward(size)
return
size = size/2
depth = depth-1
left = lambda deg:t.left(deg)
right = lambda deg:t.right(deg)
if not up:
k = left
left = right
right = k
del k
left(60)
sierpinski(size,depth,not up)
right(60)
sierpinski(size,depth,up)
right(60)
sierpinski(size,depth,not up)
left(60)
A recursive way i found to draw what i think you were expecting. My goal was actually drawing it using thousands of dots but after breaking my head a little, i’ve settled for this solution:
import turtle
def sierpinski(length, level):
if level == 0:
for i in range(3):
turtle.forward(length)
turtle.left(120)
else:
sierpinski(length/2, level-1)
turtle.forward(length/2)
sierpinski(length/2, level-1)
turtle.backward(length/2)
turtle.left(60)
turtle.forward(length/2)
turtle.right(60)
sierpinski(length/2, level-1)
turtle.left(60)
turtle.backward(length/2)
turtle.right(60)
turtle.speed(0)
sierpinski(200, 4)