Need help making Sierpinski triangle with turtle - python

I was watching a video by Aperture on youtube: https://youtu.be/1w40fxsyraE?t=325
At the provided timestamp (5:25), he begins talking about a way to create a fractal. I tried to replicate this in a python program, but I am getting a different output. I have no idea why I am getting this output, but the math seems right, so I don't know what to change. Can anyone explain why my output looks different than the one in the video?
import turtle as t
drawer = t.Turtle()
drawer.speed(1000)
# drawer.hideturtle()
drawer.penup()
#make dot A
dotAx = 125
dotAy = 150
drawer.goto(dotAx, dotAy)
drawer.dot(10, "red")
#
#make dot B
dotBx = 185
dotBy = 0
drawer.goto(dotBx, dotBy)
drawer.dot(10, "red")
#
#make dot C
dotCx = 0
dotCy = 0
drawer.goto(dotCx, dotCy)
drawer.dot(10, "red")
#
#make middle dot
dotPx = 100
dotPy = 75
drawer.goto(dotPx, dotPy)
drawer.dot(5,"yellow")
#
#draw dots v
x = 0
drawer.pendown()
while True:
if x == 0:
dotPx = (dotPx + dotAx)/2
dotPy = (dotPy + dotAy)/2
drawer.goto(dotPx, dotPy)
drawer.dot(5,"black")
print("A", dotPx, dotPy)
x+=1
if x == 1:
dotPx = (dotPx + dotBx)/2
dotPy = (dotPy + dotBy)/2
drawer.goto(dotPx, dotPy)
drawer.dot(5, "black")
print("B", dotPx, dotPy)
x+=1
if x == 2:
dotPx = (dotPx + dotCx)/2
dotPy = (dotPy + dotCy)/2
drawer.goto(dotPx, dotPy)
drawer.dot(5, "black")
print("C", dotPx, dotPy)
x = 0

I watched the video and played with your code and can't see the inconsistency. But looking further afield, I found that by changing your x (corner selection) from being cyclic, to instead being random, it works fine:
from turtle import Turtle
from random import randint
turtle = Turtle()
turtle.speed('fastest')
turtle.hideturtle()
turtle.penup()
# make dot A
dotAx, dotAy = 0, 0
turtle.goto(dotAx, dotAy)
turtle.dot(10, 'red')
# make dot B
dotBx, dotBy = 150, 260
turtle.goto(dotBx, dotBy)
turtle.dot(10, 'red')
# make dot C
dotCx, dotCy = 300, 0
turtle.goto(dotCx, dotCy)
turtle.dot(10, 'red')
# make random dot inside triangle
dotPx, dotPy = 100, 75
turtle.goto(dotPx, dotPy)
turtle.dot(5, 'green')
# draw dots
while True:
x = randint(0, 2) # pick a random corner
if x == 0:
dotPx = (dotAx + dotPx)/2
dotPy = (dotAy + dotPy)/2
turtle.goto(dotPx, dotPy)
turtle.dot(5)
elif x == 1:
dotPx = (dotBx + dotPx)/2
dotPy = (dotBy + dotPy)/2
turtle.goto(dotPx, dotPy)
turtle.dot(5)
elif x == 2:
dotPx = (dotCx + dotPx)/2
dotPy = (dotCy + dotPy)/2
turtle.goto(dotPx, dotPy)
turtle.dot(5)
Perhaps this will narrow your search as to why your orignal approach, as suggested by the video, failed. If I were writing this from scratch, and attempting to get finer detail (and speed), I might take greater advantage of turtle and Python by doing:
from turtle import Screen, Turtle, Vec2D
from random import choice
VERTICES = [Vec2D(0, 0), Vec2D(150, 260), Vec2D(300, 0)]
point = Vec2D(100, 75) # random point inside triangle
def doit():
global point
point = (choice(VERTICES) + point) * 0.5
turtle.goto(point)
turtle.dot(2)
screen.update()
screen.ontimer(doit)
screen = Screen()
screen.tracer(False)
turtle = Turtle()
turtle.hideturtle()
turtle.penup()
for vertex in VERTICES:
turtle.goto(vertex)
turtle.dot(5, 'red')
turtle.goto(point)
turtle.dot(5, 'green')
doit()
screen.mainloop()

Related

Is there a way to check a radius around a random point in order for random points to be separate from each other

I'm currently creating a drawing project, where I use random to create random star points in the sky. I currently have the ability for two points to not be the same, but I would like to find a way to make it so they wouldn't land in a x radius circle. Is there any way in python to complete this
import turtle as t, random as r
screen=t.Screen()
screen.bgcolor("#3A3B3C")
t.speed(1)
def randomStars(y, num):
t.penup()
t.pensize(2)
t.color("white")
locations = []
for x in range(num):
repeat = True
t.penup()
t.seth(90)
y_pos = starLocationY(y)
x = starLocationX()
while repeat == True:
if [x,y_pos] in locations:
y_pos = starLocationY(y)
x = starLocationX()
else:
locations.append([x,y_pos])
repeat = False
t.goto(x,y_pos)
t.pendown()
t.seth(60)
t.fd(2)
t.fd(-2)
t.seth(-60)
t.fd(2)
t.fd(-2)
t.seth(240)
t.fd(2)
t.fd(-2)
t.seth(120)
t.fd(2)
t.fd(-2)
randomStars(85,30)
p.s: I'm using trinket for the project, as required by the class, so the modules are limited
link to trinket:https://trinket.io/python/9776ba1b8a
We can use any on a generator invoking turtle's distance() method on the elements of your locations list. Easier than it sounds:
from turtle import Screen, Turtle
from random import randint
EXCLUSION_RADIUS = 35 # in pixels
def starLocationY(y):
return randint(y, 190)
def starLocationX():
return randint(-190, 190)
def randomStars(y, number):
turtle.penup()
turtle.pensize(2)
turtle.color('white')
locations = []
for _ in range(number):
y_pos = starLocationY(y)
x = starLocationX()
while True:
turtle.goto(x, y_pos)
if any(turtle.distance(location) < EXCLUSION_RADIUS for location in locations):
y_pos = starLocationY(y)
x = starLocationX()
else:
locations.append((x, y_pos))
break
turtle.pendown()
for heading in range(0, 360, 120):
turtle.setheading(heading)
turtle.forward(2)
turtle.backward(4)
turtle.forward(2)
turtle.penup()
screen = Screen()
screen.bgcolor('#3A3B3C')
turtle = Turtle()
turtle.hideturtle()
turtle.speed('fastest') # because I have no patience
randomStars(85, 20)
screen.exitonclick()

How can I start drawing from a specific place in Python turtle?

I'm trying to draw grass and I'm struggling to make my drawing start from the bottom left then go all the way to the bottom right. I know there is something wrong with my code but can't figure it out.
The code I have starts from the middle and that's not what I want.
import turtle
import random
window = turtle.Screen()
bob = turtle.Turtle()
window.bgcolor("white")
window.title("TURTLE")
bob.pencolor("green")
window.colormode(255)
position = 0
height = 0
height11 = 0
height12 = 0
height13 = 0
height14 = 0
def draw_grass(bob):
green = random.randint (100, 200)
length = random.randint(10,20)
bob.fillcolor(0,green,0)
bob.begin_fill()
bob.setheading(90)
for i in range (2):
bob.forward(length)
bob.right(90)
bob.forward(3)
bob.right(90)
bob.end_fill()
bob.penup()
bob.pendown()
return length
for i in range (10):
height = draw_grass(bob)
position = position + 3
bob.goto(position, 0)
if height == 11 :
height11 = height11 + 1
elif height == 12:
height12 = height12 + 1
elif height == 13:
height13 = height13 + 1
You need to understand that the origin (0, 0) is in the center of the window and you have to work with positive and negative numbers. The screen methods window_width() and window_height() are useful for figuring out the size of a window that you didn't configure yourself. Here's a rework of your code that incorporates these ideas:
from turtle import Screen, Turtle
from random import randint
def draw_grass(turtle):
green = randint(100, 200)
length = randint(10, 20)
turtle.fillcolor(0, green, 0)
turtle.begin_fill()
for _ in range(2):
turtle.forward(3)
turtle.left(90)
turtle.forward(length)
turtle.left(90)
turtle.end_fill()
turtle.penup()
turtle.pendown()
return length
screen = Screen()
screen.bgcolor('white')
screen.title("TURTLE")
screen.colormode(255)
turtle = Turtle()
turtle.pencolor('green')
turtle.speed('fastest') # because I have no patience
turtle.penup()
turtle.setposition(6 - screen.window_width()//2, 12 - screen.window_height()//2)
turtle.pendown()
height11 = 0
height12 = 0
height13 = 0
height14 = 0
while turtle.xcor() < screen.window_width()//2 - 6:
height = draw_grass(turtle)
if height == 11:
height11 += 1
elif height == 12:
height12 += 1
elif height == 13:
height13 += 1
elif height == 14:
height14 += 1
turtle.forward(3)
screen.exitonclick()

Getting dots to follow bigger dot

So this is a program I made for some dots to be attracted to a bigger dot and for that bigger dot to grow. The issue I'm facing right now is that the dots don't follow the bigger dot but rather seem to move away from it. The way I'm getting it to get closer is by translating the points, one to (0,0), the other to [t2.xcor() - t1.xcor() , t2.ycor()- t1.ycor()] , and then finding C with the Pythagorean theorem, and then using arc cosine to find the angle it needs to face in order to move towards the bigger dot.
from turtle import *
import sys
from math import *
#grows t1 shape + has it follow cursor
def grow(x, y):
t1.ondrag(None)
t1.goto(x,y)
global big, nig
t1.shapesize(big,nig)
big += .004
nig += .004
t1.ondrag(grow)
follow()
#has create()'d dots follow t1
def follow():
global count
#t1.ondrag(None)
screen.tracer(0,0)
for p in lx:
#print(lx[0:5])
t2.goto(p, ly[count])
t2.dot(4, "white")
if ly[count] != 0:
yb = abs(t2.ycor() - t1.ycor())
xb = abs((t2.xcor() - t1.xcor()))
c = sqrt((xb**2 + yb**2))
#print(y,x,c)
#print(lx)
t2.seth(360 - degrees(acos(yb/c)))
else:
t2.seth(0)
t2.forward(20)
t2.dot(4, "purple")
lx.pop(count)
ly.pop(count)
lx.insert(count, t2.xcor())
ly.insert(count, t2.ycor())
count += 1
#print(lx[0:5])
#screen.update()
screen.tracer(1,10)
count = 0
#t1.ondrag(follow)
#quits program
def quit():
screen.bye()
sys.exit(0)
#create()'s dots with t2
def create():
screen.tracer(0,0)
global nux, nuy, count3
while nuy > -400:
t2.goto(nux, nuy)
if t2.pos() != t1.pos():
t2.dot(4, "purple")
lx.append(t2.xcor())
ly.append(t2.ycor())
nux += 50
count3 += 1
if count3 == 17:
nuy = nuy - 50
nux = -400
count3 = 0
screen.tracer(1, 10)
#variables
count3 = count = 0
big = nig = .02
lx = []
ly = []
nux = -400
nuy = 300
screen = Screen()
screen.screensize(4000,4000)
t2 = Turtle()
t2.ht()
t2.pu()
t2.speed(0)
t2.shape("turtle")
t1 = Turtle()
t1.shape("circle")
t1.penup()
t1.speed(0)
t1.color("purple")
t1.shapesize(.2, .2)
create()
screen.listen()
screen.onkeypress(quit, "Escape")
t1.ondrag(grow)
#t1.ondrag(follow)
#screen.update()
screen.mainloop()
I see two (similar) issues with your code. First, you can toss the fancy math as you're reinventing turtle's .towards() method which gives you the angle you seek. Second, you're reinventing stamps which, unlike most turtle elements, can be cleared cleanly off the screen via clearstamp(). Also, you're using parallel arrays of coordinates which indicates lack of a proper data structure. I've replaced this with a single array containing tuples of positions and stamps.
I've adjusted the dynamics of your program, making the dots act independently (on a timer) and not rely on the movement of the cursor. I.e. they move towards the cursor whether it's moving or not. Also, I've made the cursor only grow when a dot reaches it and disappears:
from turtle import Turtle, Screen
CURSOR_SIZE = 20
def move(x, y):
""" has it follow cursor """
t1.ondrag(None)
t1.goto(x, y)
screen.update()
t1.ondrag(move)
def grow():
""" grows t1 shape """
global t1_size
t1_size += 0.4
t1.shapesize(t1_size / CURSOR_SIZE)
screen.update()
def follow():
""" has create()'d dots follow t1 """
global circles
new_circles = []
for (x, y), stamp in circles:
t2.clearstamp(stamp)
t2.goto(x, y)
t2.setheading(t2.towards(t1))
t2.forward(2)
if t2.distance(t1) > t1_size // 2:
new_circles.append((t2.position(), t2.stamp()))
else:
grow() # we ate one, make t1 fatter
screen.update()
circles = new_circles
if circles:
screen.ontimer(follow, 50)
def create():
""" create()'s dots with t2 """
count = 0
nux, nuy = -400, 300
while nuy > -400:
t2.goto(nux, nuy)
if t2.distance(t1) > t1_size // 2:
circles.append((t2.position(), t2.stamp()))
nux += 50
count += 1
if count == 17:
nuy -= 50
nux = -400
count = 0
screen.update()
# variables
t1_size = 4
circles = []
screen = Screen()
screen.screensize(900, 900)
t2 = Turtle('circle', visible=False)
t2.shapesize(4 / CURSOR_SIZE)
t2.speed('fastest')
t2.color('purple')
t2.penup()
t1 = Turtle('circle')
t1.shapesize(t1_size / CURSOR_SIZE)
t1.speed('fastest')
t1.color('orange')
t1.penup()
t1.ondrag(move)
screen.tracer(False)
create()
follow()
screen.mainloop()
You should be able to rework this code to do whatever it is you want. I strongly recommend you spend some time reading the Turtle documentation so you don't need to reinvent its many features.

How do I control where game pieces go for checkerboard in Python?

So, I have asked a similar question before, but I was still unable to solve my problem. I need to make a checkerboard with 6 game pieces evenly on each side. This code was helpful, but it randomly distributes the game pieces, I've played around with it in multiple lines and attempted to understand but it's not clicking for me. How can I get six pieces to be on top half and six to be on lower half with space in the middle? Like a regular checkerboard set up?
from turtle import Turtle, Screen
from random import randrange
CURSOR_SIZE = 20
SQUARE_SIZE = 40
SQUARES_PER_SIDE = 8
def drawRect(color):
turtle.color(color)
turtle.pendown()
turtle.begin_fill()
for iterations in range(4):
turtle.forward(SQUARE_SIZE)
turtle.left(90)
turtle.end_fill()
turtle.penup()
def pushTurtleForward():
turtle.forward(SQUARE_SIZE)
def drawHorizontal(inverted=False):
if inverted:
for horizontal in range(SQUARES_PER_SIDE):
if horizontal > 0:
if horizontal % 2 == 1:
pushTurtleForward()
drawRect("white")
else:
pushTurtleForward()
drawRect("black")
else:
drawRect("black")
else:
for horizontal in range(SQUARES_PER_SIDE):
if horizontal > 0:
if horizontal % 2 == 1:
pushTurtleForward()
drawRect("black")
else:
pushTurtleForward()
drawRect("white")
else:
drawRect("white")
screen = Screen()
screen.bgcolor("Grey")
turtle = Turtle(visible=False)
turtle.speed('fastest')
for drawVertical in range(SQUARES_PER_SIDE):
turtle.setposition(0, SQUARE_SIZE * drawVertical)
if drawVertical % 2 == 0:
drawHorizontal(inverted=True)
else:
drawHorizontal()
# Checker graphics demonstration. Distribute 12 red checkers around
# black squares on board without any two landing on the same spot.
red_checkers = []
for _ in range(12):
checker = Turtle('circle')
checker.color('black', 'red')
checker.shapesize(SQUARE_SIZE / CURSOR_SIZE)
checker.penup()
red_checkers.append(checker)
position = checker.position() # a position guaranteed to fail
while any(map(lambda checker, p=position: checker.distance(p) <
SQUARE_SIZE/2, red_checkers)):
x, y = 0, 1 # a parity guaranteed to fail
while x % 2 != y % 2:
x, y = (SQUARES_PER_SIDE),(SQUARES_PER_SIDE)
position = (x * SQUARE_SIZE + SQUARE_SIZE/2, y * SQUARE_SIZE +
SQUARE_SIZE/2)
checker.goto(position)
screen.mainloop()
I think this is what you want. Replace the for _ in range(12): loop with this:
# Returns the drawing co-ords of checker space
def get_square_coords(row, col):
return (col * SQUARE_SIZE + SQUARE_SIZE/2, row * SQUARE_SIZE + SQUARE_SIZE/2)
# Creates 2 rows of checkers
def create_checkers(row, color):
checkers = []
col = 0
for y in range(row, row+2):
for x in range(col, SQUARES_PER_SIDE, 2):
checker = Turtle('circle')
checker.color('black', color)
checker.shapesize(SQUARE_SIZE / CURSOR_SIZE)
checker.penup()
position = get_square_coords(y, x)
checker.goto(position)
checkers.append(checker)
col += 1
return checkers
red_checkers = create_checkers(0, 'red')
green_checkers = create_checkers(6, 'green')
It creates this:

Python, user input generates turtles

I am trying to setup a program in which the user decides how many turtles to generate and then they have a race after. My current solution is to just get an int input from the user and execute the code below (the code keeps repeating with larger numbers). I have tried putting in a loop but I am running into troubles since they all need to preform random movements in the end. Any help?
if turtNum >= 1:
turt1 = Turtle()
turt1.color(turtColour[0])
turt1.shape('turtle')
turt1.penup()
turt1.goto(0, -10)
turt1.pendown()
if turtNum >= 2:
turt2Name = input('Enter a name for the second turtle: ')
turt2 = Turtle()
turt2.color(turtColour[1])
turt2.shape('turtle')
turt2.penup()
turt2.goto(0, -25)
turt2.pendown()
This is the code I tried but got this error "list indices must be integers or slices, not str"
turtName = []
maxLengthList = turtNum
while len(turtName) < maxLengthList:
name = input('Enter the names for the turtles: ')
turtName.append(name)
for i in turtName:
turtName[i] = Turtle()
turtName[i].color(turtColour[0])
turtName[i].shape('turtle')
turtName[i].penup()
turtName[i].goto(0, -10)
turtName[i].pendown()
You can't dangle the concept of turtle racing without expecting us to get excited about seeing it happen. Below is a rough implementation that addresses the issues you had about entering the number of turtles, entering individual turtle information, storing it all and random motion:
from turtle import Turtle, Screen
from itertools import cycle
from random import randrange
MAX_TURTLES = 20
LANE_WIDTH = 25
FONT_SIZE = 18
FONT = ("Arial", FONT_SIZE, "normal")
COLORS = cycle(['red', 'green', 'blue', 'cyan', 'black', 'yellow'])
FINISH_LINE = 350
START_LINE = -200
NAME_LINE = START_LINE - 150
DELAY = 100 # milliseconds
MAX_STEP = 10
turtles = dict()
def race():
for name, turtle in turtles.items(): # should shuffle turtles
turtle.forward(randrange(MAX_STEP + 1))
if turtle.xcor() > FINISH_LINE:
return # the race is over
screen.ontimer(race, DELAY)
magic_marker = Turtle(visible=False)
magic_marker.penup()
turtNum = 0
while not 1 <= turtNum <= MAX_TURTLES:
turtNum = int(input('Enter the number of turtles: '))
for i in range(turtNum):
name = input('Enter a name for the turtle #{}: '.format(i + 1))
turtle = Turtle(shape="turtle")
turtle.color(next(COLORS))
y_offset = LANE_WIDTH * i - LANE_WIDTH * turtNum // 2
magic_marker.color(turtle.pencolor())
magic_marker.goto(NAME_LINE, y_offset - FONT_SIZE / 2)
magic_marker.write(name, font=FONT)
turtle.penup()
turtle.goto(START_LINE, y_offset)
turtle.pendown()
turtles[name] = turtle
magic_marker.color('red')
magic_marker.goto(FINISH_LINE, -FINISH_LINE)
magic_marker.pendown()
magic_marker.goto(FINISH_LINE, FINISH_LINE)
magic_marker.penup()
screen = Screen()
screen.ontimer(race, DELAY)
screen.exitonclick()

Categories

Resources