Having trouble making the Sierpinski Carpet using recursion only and python turtle - python

import turtle
window = turtle.Screen()
window.screensize(1920,1080)
window.setup(width=1.0, height=1.0, startx=None, starty=None)
# T is a global variable
T = turtle.Turtle()
T.shape('turtle')
T.speed(2)
def forward_and_rotate(distance, degree, t):
t.forward(distance)
t.left(degree)
def draw_square_recursion(distance, t, sides_left=4):
t.pendown()
if sides_left == 0:
return
else:
forward_and_rotate(distance, 90, t)
draw_square_recursion(distance, t, sides_left-1)
t.penup()
def draw_filled_square(t, side_len, color='black'):
current_color = T.pencolor()
t.pendown()
t.color(color)
t.begin_fill()
draw_square_recursion(side_len, t, 4)
t.end_fill()
t.penup()
T.color(current_color)
# Starter code
def sier_carp(t, l, n):
if n == 0:
home = t.pos()
t.goto(home + (l/3, l/3))
draw_filled_square(t, l/3)
t.goto(home)
else:
sier_carp(t, l/3, n - 1)
t.fd(l/3)
sier_carp(t, l/3, n -1)
t.fd(l/3)
sier_carp(t, l/3, n -1)
t.fd(l/3)
t.left(90)
t.fd(l/3)
sier_carp(t, l/3, n - 1)
t.fd(l/3)
sier_carp(t, l/3, n -1)
t.fd(l/3)
t.left(90)
t.fd(l/3)
sier_carp(t, l/3, n - 1)
t.fd(l/3)
sier_carp(t, l/3, n -1)
t.fd(l/3)
t.left(90)
t.fd(l/3)
sier_carp(t, l/3, n - 1)
t.fd(l/3)
sier_carp(t, l/3, n -1)
t.fd(l/3)
t.left(90)
t.fd(l/3)
# add more cases!
T.penup()
sl = 200
draw_square_recursion(sl, T, 4)
sier_carp(T, sl, 1)
I'm having a hard time drawing the Sierpinski carpet using recursion only and no loops. However after the first three squares are drawn and I try to rotate to the left and do the next 2 squares on the nest site, the squares go out of line. If you want you can try out this code and see how it looks like. Btw the color are inversed to white around and black inside and not the usual carpet version of black around and white square.

I'm going to do a simplied version of your code -- although repetitive, your starter code does achieve your goal of recursion only. However, I'm going to use stamping instead of drawing so we can focus on the recursive image and not the mechanics of drawing squares with turtles:
from turtle import Screen, Turtle
SIDE_LENGTH = 200
CURSOR_SIZE = 20
def draw_filled_square(t, side_len):
t.shapesize(side_len / CURSOR_SIZE)
t.stamp()
def sier_carp(t, l, n):
draw_filled_square(t, l)
if n < 1:
return
x, y = t.position()
t.setx(x + l)
sier_carp(t, l/3, n - 1)
t.sety(y + l)
sier_carp(t, l/3, n - 1)
t.setx(x)
sier_carp(t, l/3, n - 1)
t.setx(x - l)
sier_carp(t, l/3, n - 1)
t.sety(y)
sier_carp(t, l/3, n - 1)
t.sety(y - l)
sier_carp(t, l/3, n - 1)
t.setx(x)
sier_carp(t, l/3, n - 1)
t.setx(x + l)
sier_carp(t, l/3, n - 1)
t.goto(x, y)
screen = Screen()
screen.setup(width=1.0, height=1.0)
turtle = Turtle()
turtle.hideturtle()
turtle.shape('square')
turtle.penup()
sier_carp(turtle, SIDE_LENGTH, 4)
screen.exitonclick()
I intentionally avoided speeding things up with screen.tracer(False) as you should see how it's drawn in a counterclockwise pattern.
I believe the problem is simpler than you're making it. It's important in this sort of drawing that your recursive function return the turtle to where it was when the function started. This allows the caller to make valid assumptions about its position.

Here are the concepts I've noted before starting:
Define a function that takes in a coordinate and a square size, and return the 8 surrounding coordinates.
Define a function that takes in a coordinate and a square size, and draw a square with the given size as its length, and the given coordinate as its center point.
Define the main function that will take in a coordinate, a size and the number of recursions. The coordinate will be the center of the Sierpinski Carpet and the size will be the size of the finished Sierpinski Carpet.
With the above notes in mind, let's begin to construct our program:
Import the turtle module, turn off tracer (optional) for efficiency and hide the turtle cursor:
import turtle
turtle.tracer(0)
turtle.hideturtle()
Define the function that takes in coordinate and a unit, and return a list of all 8 coordinates surrounding the given coordinate:
def surrounding(cor, size):
x, y = cor
return [(x - size, y + size),
(x + size, y + size),
(x - size, y - size),
(x + size, y - size),
(x - size, y),
(x + size, y),
(x, y + size),
(x, y - size)]
Define a recursive function that will take in a coordinate, size and a count. It will draw a square with its center at the given coordinate and its size one third of the given size, so that the sides of the finished carpet will end up the given size. The count is there to tell the function when to stop recursing; when the count reaches 0:
def draw_square(cor, size, count):
size /= 3
count -= 1
turtle.penup()
turtle.goto(cor[0] - size / 2, cor[1] + size / 2)
turtle.pendown()
turtle.begin_fill()
turtle.forward(size)
turtle.right(90)
turtle.forward(size)
turtle.right(90)
turtle.forward(size)
turtle.right(90)
turtle.forward(size)
turtle.right(90)
turtle.end_fill()
if count:
func(surrounding(cor, size), size, count)
Define a recursive function that takes in a coordinate and a size. It will use the surroundings function defined earlier to convert that coordinate into a list of surround coordinates. Then, it will use the draw_square function defined earlier to draw a square at each of the surrounding coordinates:
def func(cors, size, count):
if len(cors) == 2:
draw_square(cors, size, count)
else:
draw_square(cors[0], size, count)
draw_square(cors[1], size, count)
draw_square(cors[2], size, count)
draw_square(cors[3], size, count)
draw_square(cors[4], size, count)
draw_square(cors[5], size, count)
draw_square(cors[6], size, count)
draw_square(cors[7], size, count)
Finally, call the main function, func, and update the turtle screen (if tracer was set to 0).
func((0, 0), 600, 5)
turtle.update()
Altogether:
import turtle
turtle.tracer(0)
turtle.hideturtle()
def surrounding(cor, size):
x, y = cor
return [(x - size, y + size),
(x + size, y + size),
(x - size, y - size),
(x + size, y - size),
(x - size, y),
(x + size, y),
(x, y + size),
(x, y - size)]
def draw_square(cor, size, count):
size /= 3
count -= 1
turtle.penup()
turtle.goto(cor[0] - size / 2, cor[1] + size / 2)
turtle.pendown()
turtle.begin_fill()
turtle.forward(size)
turtle.right(90)
turtle.forward(size)
turtle.right(90)
turtle.forward(size)
turtle.right(90)
turtle.forward(size)
turtle.right(90)
turtle.end_fill()
if count:
func(surrounding(cor, size), size, count)
def func(cors, size, count):
if len(cors) == 2:
draw_square(cors, size, count)
else:
draw_square(cors[0], size, count)
draw_square(cors[1], size, count)
draw_square(cors[2], size, count)
draw_square(cors[3], size, count)
draw_square(cors[4], size, count)
draw_square(cors[5], size, count)
draw_square(cors[6], size, count)
draw_square(cors[7], size, count)
func((0, 0), 600, 5)
turtle.update()
Output:

Related

Drawing radiating circular trapezoid pattern with Turtle

I was just wondering how to draw some trapezoids in my turtle code.
I want my output to be like this:
What my output is right now:
Here's the code I've written so far:
import turtle as trtl
num_sides = 6
side_length = 15
circumradius = side_length
trtl.pencolor((245, 176, 66))
trtl.pensize(8)
for move_turtle in range(1):
trtl.penup()
trtl.sety(-circumradius)
trtl.pendown()
trtl.circle(circumradius, steps = num_sides)
circumradius *= 2
side_length = side_length + 8
trtl.pensize(12)
for move_turtle in range(1):
trtl.pencolor((255, 83, 71))
trtl.penup()
trtl.sety(-circumradius)
trtl.pendown()
trtl.circle(circumradius, steps = num_sides)
circumradius *= 2
for move_turtle in range(1):
trtl.pencolor((247, 220, 67))
trtl.penup()
trtl.sety(-circumradius)
trtl.pendown()
trtl.circle(circumradius, steps = num_sides)
circumradius *= 2
trtl.hideturtle()
What techniques can I use to get the desired output?
The approach I often take when given patterns like this is to think about a slight-of-hand trick that can produce the result with less trouble than figuring out how to draw something potentially fussy like a trapezoid.
In this case, if you remove the white spaces, we have a "pie"-shaped circle of different solid colors radiating from the middle. If we can manage to solve that simpler problem, then we can draw white lines on top and wind up with the desired result.
The only trig we need is the angle-to-coordinates formula:
x = cos(radians(angle)) * radius
y = sin(radians(angle)) * radius
Using this, we can iterate over n points (with some rotational offset) along the circumference of a circle with a loop and fill in the colors to make a pie:
import math
import turtle
def draw_pie(t, r, n, colors, rot_offset=0.5):
for i in range(n + 1):
a = 360 / n * (i + rot_offset)
t.color(colors[i%len(colors)])
t.begin_fill()
t.goto(0, 0)
x = math.cos(math.radians(a)) * r
y = math.sin(math.radians(a)) * r
t.goto(x, y)
t.end_fill()
if __name__ == "__main__":
t = turtle.Turtle()
t.screen.setup(540, 540)
t.penup()
t.speed("fastest")
sides = 6
draw_pie(t, 450, sides, ["#dd2", "orange", "#d02"])
t.ht()
turtle.exitonclick()
Next, we need to draw a bunch of thick white lines with square corners. Unfortunately, turtle draws rounded edges by default which aren't suitable for our needs. There are many tricks for making square edges (stamping is a reasonable method). The approach I used here was writing a custom rectangle drawing function that takes advantage of turtle.distance and turtle.setheading(turtle.towards(x, y)) which lets me point to the next point along the circumference of the circle. The rest is just loops to compute angles and picking the right values.
Here's the code:
import math
import turtle
def draw_rect(t, x, y, xx, yy, width):
t.goto(x, y)
t.setheading(t.towards(xx, yy))
t.begin_fill()
for d in [t.distance(xx, yy), width] * 2:
t.forward(d)
t.left(90)
t.end_fill()
def draw_pie(t, r, n, colors, rot_offset=0.5):
for i in range(n + 1):
a = 360 / n * (i + rot_offset)
t.color(colors[i%len(colors)])
t.begin_fill()
t.goto(0, 0)
x = math.cos(math.radians(a)) * r
y = math.sin(math.radians(a)) * r
t.goto(x, y)
t.end_fill()
def draw_reg_polygon(t, r, n, thickness, rot_offset=0.5):
for i in range(n):
a = 360 / n * (i + rot_offset)
x = math.cos(math.radians(a)) * r
y = math.sin(math.radians(a)) * r
a = 360 / n * (1 + i + rot_offset)
xx = math.cos(math.radians(a)) * r
yy = math.sin(math.radians(a)) * r
draw_rect(t, x, y, xx, yy, thickness)
if __name__ == "__main__":
t = turtle.Turtle()
t.screen.setup(540, 540)
t.penup()
t.speed("fastest")
sides = 6
draw_pie(t, 450, sides, ["#dd2", "orange", "#d02"])
t.color("white")
for r in range(50, 500, 100):
draw_reg_polygon(t, r, sides, 45)
t.ht()
turtle.exitonclick()

Using recursion to draw a pattern of squares

So far this is the code I've written with the great help of my tutor.
def square(t, x, y, side):
t.up()
t.goto(x-side/2, y+side/2)
t.down()
for i in range(4):
t.forward(side)
t.right(90)
def squares(t, x, y, size, n):
if n == 0:
return
if n >= 1:
square(t, x, y, size)
half = size / 2
squares(t, x - half, y + half, size / 2.2, n - 1)
squares(t, x + half, y + half, size / 2.2, n - 1)
squares(t, x + half, y - half, size / 2.2, n - 1)
squares(t, x - half, y - half, size / 2.2, n - 1)
s = Screen()
t = Turtle()
t.pensize(2)
squares(t, 0, 0, 200, 3)
s.exitonclick()
So this program will draw a square with 4 squares on each point and 4 squares on each of their points. This was done within 1.5 hours of tutoring and my guy had to leave for family reasons and I couldn't finish this alone. Here is what the assignment wants.
So what I want is a square with a square with a square with a square
I can tell you got alot from your tutoring session...
just change
squares(t, 0, 0, 200, 3)
to
squares(t, 0, 0, 200, 5)

Draw Checkerboard on Python

I want to draw a checkerboard on Python, but I only get one black square.
Can you help me fix this program?
import turtle
def filled_square(size, color, x, y):
turtle.setpos(x, y)
turtle.color(color)
turtle.begin_fill()
for i in range(4):
angle = 90
turtle.fd(size)
turtle.lt(angle)
turtle.end_fill()
turtle.up()
import sys
n = int(sys.argv[1])
s = int(sys.argv[2])
square_size = s//n
y=0
for i in range(n):
x = 0
for j in range(n):
if (i+j)%2==0:
filled_square(square_size, "red", x, y)
else:
filled_square(square_size, "black", x, y)
x+=square_size
turtle.down()
turtle.done()
y=0
for i in range(n):
x = 0
for j in range(n):
if (i+j)%2==0:
filled_square(square_size, "red", x, y)
else:
filled_square(square_size, "black", x, y)
x+=square_size
Couple problems here.
There's not much point in increasing the value of x when you reset it right back to zero in the next iteration, so the initial assignment should be above the for loop.
you never update the value of y.
x = 0
for i in range(n):
y=0
for j in range(n):
if (i+j)%2==0:
filled_square(square_size, "red", x, y)
else:
filled_square(square_size, "black", x, y)
y+=square_size
x+=square_size
Now you should get the checkerboard shape you want.
Alternate solution: you can avoid the problem of bookkeeping the values of x and y by not having those values at all. You can derive the coordinates of the squares straight from i and j.
for i in range(n):
for j in range(n):
if (i+j)%2==0:
filled_square(square_size, "red", i*square_size, j*square_size)
else:
filled_square(square_size, "black", i*square_size, j*square_size)
It might also be nice to consolidate the common logic in your if and else blocks, and only differentiate the values that actually change (namely, the color)
for i in range(n):
for j in range(n):
if (i+j)%2==0:
color = "red"
else:
color = "black"
filled_square(square_size, color, i*square_size, j*square_size)
You need to increment both x and y. Also note that x should be incremented in the inner loop. Here is the working code,
import turtle
def filled_square(size, color, x, y):
turtle.setpos(x, y)
turtle.color(color)
turtle.begin_fill()
for i in range(4):
angle = 90
turtle.fd(size)
turtle.lt(angle)
turtle.end_fill()
turtle.up()
import sys
n = int(sys.argv[1])
s = int(sys.argv[2])
square_size = s//n
y=0
for i in range(n):
x = 0
for j in range(n):
if (i+j)%2==0:
filled_square(square_size, "red", x, y)
else:
filled_square(square_size, "black", x, y)
x+=square_size
y+=square_size
turtle.down()
turtle.done()
I recommend using functional programming like this for board/game work. It'll make everything easier to maintain and make implementing new features easier.
import turtle
def filled_square(size, color, x, y):
turtle.up()
turtle.setpos(x, y)
turtle.color(color)
turtle.begin_fill()
for i in range(4):
angle = 90
turtle.fd(size)
turtle.lt(angle)
turtle.end_fill()
def board(length, size, x_pos=0, y_pos=0):
for y in range(length):
for x in range(length):
if (x+y)%2==0:
filled_square(
size, "red", (x * square_size) - x_pos, (y * square_size) - y_pos)
else:
filled_square(
size, "black", (x * square_size) - x_pos, (y * square_size) - y_pos)
turtle.done()
Keyword arguments are great too. If you decided you wanted to add more colors, it is as easy as adding an two parameters,
def board(length, size, x_pos=0, y_pos=0, color1="red", color2="black"):
for y in range(length):
for x in range(length):
if (x+y)%2==0:
filled_square(
size, color1, (x * square_size) - x_pos, (y * square_size) - y_pos)
else:
filled_square(
size, color2, (x * square_size) - x_pos, (y * square_size) - y_pos)

Python: draw tangent graph using math & turtle libraries

I was stuck on this task for several days. Although, the solution should be simple. I apply math and turtle libraries for drawing 3 graphs: sine, cosine and tangent with amplitude 200. The problem is I cant build the tangent graph as It should be drawn.
This is what I should do:
This is what I got:
As you see, my turtle goes up and doesn't come back anymore.
Pls, don't suggest me to use numpy. It's out of my task.
Thank you for advance!
import math
import turtle
ws = turtle.Screen()
ws.bgcolor("white")
t = turtle.Turtle()
for i in [(0,250), (0,0), (0,-250), (0,0), (400,0), (0,0)]:
t.goto(i, None)
t.write(i, font=("Arial", 12))
t.color("red")
for angle in range(360):
y = math.sin(math.radians(angle))
t.goto(angle, y * 200)
t.penup()
t.setpos(0,200)
t.goto(0,200)
t.pendown()
t.color("blue")
for angle in range(360):
y = math.cos(math.radians(angle))
t.goto(angle, y * 200)
t.penup()
t.setpos(0,0)
t.goto(0,0)
t.pendown()
t.color("green")
for angle in range(360):
y = math.tan(math.radians(angle))
t.goto(angle, y * 200)
ws.exitonclick()
To show that it should work, below is my minimalist implementation of plotting sine, cosine and tangent using turtle graphics:
import math
from turtle import Turtle, Screen
RESOLUTION = 0.1
def plot(x_points, y_points):
for i, y in enumerate(y_points):
if abs(y) <= 2.0:
yertle.goto(x_points[i], y)
yertle.pendown()
else:
yertle.penup()
yertle.penup()
screen = Screen()
screen.setworldcoordinates(0, -1.5, 2 * math.pi / RESOLUTION, 1.5)
yertle = Turtle()
yertle.penup()
x = range(int(2 * math.pi / RESOLUTION))
yertle.color("blue")
plot(x, (math.cos(n * RESOLUTION) for n in x))
yertle.color("red")
plot(x, (math.sin(n * RESOLUTION) for n in x))
yertle.color("dark green")
plot(x, (math.tan(n * RESOLUTION) for n in x))
screen.exitonclick()
OUTPUT
My guess is you're not waiting long enough for tangent to plot, i.e. it's slowly plotting lots of points off the window and will eventually reappear on-screen. My code works around that issue.
try about this. Near to work for me, no time to get better:
for angle in range(360):
y=0
y = math.tan(math.radians(angle))
if y<1 and y>-1:
t.goto(angle, y * 200)
With asipmtotas
for angle in range(360):
t.penup()
y = math.tan(math.radians(angle))
if y<1 and y>-1:
t.pendown()
t.goto(angle, y * 200)
else:
t.penup()
#t.pendown()
t.goto(angle, 200)

How can I fill each petal separately using begin_fill()?

I have the following code that generates a petal pattern for a flower I'm trying to build. However, the problem is the fill part.
What should happen is each petal to be filled individually:
Instead, what happens is this:
import turtle
import math
wn = turtle.Screen()
wn.bgcolor("white")
def draw_leaf(turtle, side, theta = 0):
angle = 2
turtle.color("#67bd3c")
for x in range(-180,180):
y = math.sin(math.radians(angle))
angle += 1
y = y * side
x_axis = (x % 180) * math.cos(math.radians(theta)) + y * math.sin(math.radians(theta))
y_axis = (x % 180) * (-1 * (math.sin(math.radians(theta)))) + y * math.cos(math.radians(theta))
turtle.goto(-1 * x_axis, -1 * y_axis)
return
def draw_flower(turtle, petals):
for x in range(petals):
theta = 180/(petals - 1)
turtle.pendown()
turtle.begin_fill()
draw_leaf(turtle, 35, theta * x)
turtle.end_fill()
turtle.penup()
turtle.left(theta)
return
draw_flower(turtle,4)
wn.exitonclick()
It looks like each draw_leaf call begins when the turtle is at the far end of the leaf that it just previously drew. So the polygon that is filled during the current draw_leaf includes that end point. This is more apparent if you draw each leaf with a different color.
One possible solution is to goto the center of the flower after your penup, before you draw the next leaf.
def draw_flower(turtle, petals):
start = turtle.pos()
for x in range(petals):
theta = 180/(petals - 1)
turtle.pendown()
turtle.begin_fill()
draw_leaf(turtle, 35, theta * x)
turtle.end_fill()
turtle.penup()
turtle.goto(*start)
turtle.left(theta)
return

Categories