How to make all the turtles move simultaneously - python

I want to move all the turtles at the same time, and I want to be able to create 100 turtles. I have to create each one separately in the code so creating 100's of them, or more, will take a long time. I want a way to be able to set the number of turtles I want -- a number from 100 and up. And I want them to move all the same time. I would like to set boundary for as well. Any ideas on how to do any of this or all of it will be appreciated.
To conclude, I want to be able to:
Set the number of turtle generated.
Move all of it at once not one
each time.
Set boundary so it can't go anywhere.
Note: I also know there are a couple questions that were asked but no efficient answer was provided. My code:
import turtle
import numpy as np
tlist = list()
colorlist = ["red", "green", "black", "blue", "brown"]
for i in range(5):
tlist.append(turtle.Turtle(shape="turtle"))
tlist[i].color(colorlist[i])
tlist[i].speed(1)
screen = turtle.getscreen()
for i in range(30):
for t in tlist:
t.speed(1)
t.right((np.random.rand(1) - .5) * 180)
t.forward(int((np.random.rand(1) - .5) * 100))
screen.update()

You don't need "one smart person in here" to solve this, you need to spend more of your own time searching the wealth of turtle examples on SO. However, I will take this on as a personal challenge to write minimalist code to put 100 turtles into bounded random motion:
from turtle import Screen, Turtle
from random import randint
WIDTH, HEIGHT = 600, 600
CURSOR_SIZE, TURTLE_SIZE = 20, 10
TURTLES = 100
def move():
for turtle in screen.turtles():
turtle.forward(1)
x, y = turtle.position()
if not (TURTLE_SIZE - WIDTH/2 < x < WIDTH/2 - TURTLE_SIZE and TURTLE_SIZE - HEIGHT/2 < y < HEIGHT/2 - TURTLE_SIZE):
turtle.undo() # undo forward()
turtle.setheading(randint(1, 360)) # change heading for next iteration
screen.update()
screen.ontimer(move)
screen = Screen()
screen.tracer(False)
screen.setup(WIDTH, HEIGHT)
for turtle in range(TURTLES):
turtle = Turtle()
turtle.penup()
turtle.shapesize(TURTLE_SIZE / CURSOR_SIZE)
turtle.shape('turtle')
turtle.setheading(randint(1, 360))
turtle.goto(randint(TURTLE_SIZE - WIDTH/2, WIDTH/2 - TURTLE_SIZE), randint(TURTLE_SIZE - HEIGHT/2, HEIGHT/2 - TURTLE_SIZE))
move()
screen.exitonclick()

Related

How can I detect the collision of multiple turtle objects?

I have a bunch of turtle objects on the screen.
They don't do anything yet but I am trying to detect collision of turtle objects the simplest way. I know I am supposed to compare all turtle elements turtle.pos with the turtle.pos of all the other elements, but I am not sure how to do it.
Also, I am concerned with not comparing any one item with its own position.
from turtle import *
import random
screen = Screen()
screen.bgcolor("grey")
screen.setup(width=800, height=600)
my_turtles = []
for i in range(10):
turtle = Turtle()
turtle.penup()
turtle.setx(random.randrange(-50, 50))
turtle.sety(random.randrange(-50, 50))
my_turtles.append(turtle)
#for t in turtles:
# if t.pos == anyother t.pos_except_itself
# do something
screen.listen()
screen.exitonclick()
The last thing you want to do is directly compare one turtle.position() with another turtle.position(). The reason is that turtles wander a floating point plane, and directly comparing coordinate values rarely works the way you want. Instead, you need to decide on the minimal distance between (the centers of) two turtles that will be considered a collision. Once you know that distance, then a loop like:
for a in turtles:
if any(a != b and a.distance(b) < SAFE_MARGIN for b in turtles):
# collision, do something here
Here's a rework of your code that does this, causing any turtles that have overly invaded each other's personal space to blink:
from turtle import Screen, Turtle
from random import randrange
RADIUS = 10
def blink_turtles():
for a in turtles:
if any(a != b and a.distance(b) < RADIUS for b in turtles):
a.color(*reversed(a.color()))
screen.ontimer(blink_turtles, 1000)
screen = Screen()
screen.setup(width=800, height=600)
turtles = []
for _ in range(15):
turtle = Turtle()
turtle.hideturtle()
turtle.shape('turtle')
turtle.color('red', 'green')
turtle.penup()
turtle.goto(randrange(-50, 50), randrange(-50, 50))
turtle.showturtle()
turtles.append(turtle)
blink_turtles()
screen.exitonclick()

Filling in a shape drawn by two turtles

How do I fill in this shape I drew?
Here's the code:
t = turtle.Turtle()
t2 = t.clone()
#the shapes do not go beyond the screen
for count in range(2):
t.circle(100, 180)
t.right(180)
t2.circle(200, 180)
set t.color(turtle.bgcolor())
start begin_fill on first t2 and then t
draw
end_fill t2 first
and then end_fill t
Entire code:
>>> import turtle
>>> t = turtle.Turtle(); t2 = t.clone()
>>> t.color(turtle.bgcolor())
>>> t2.begin_fill(); t.begin_fill()
>>> t2.circle(200, 180)
>>> for count in range(2):
t.circle(100, 180)
t.right(180)
>>> t2.end_fill(); t.end_fill()
My inclination would be to draw the shape with a single turtle to simplify the problem:
import turtle
turtle.begin_fill()
turtle.circle(100, 180)
turtle.circle(200, -180)
turtle.circle(100, 180)
turtle.end_fill()
turtle.hideturtle()
turtle.done()
The approach of #Superior is perfectly valid (+1), but I'd code it differently:
from turtle import Screen, Turtle
screen = Screen()
t1 = Turtle()
t1.begin_fill()
t1.circle(200, 180)
t1.end_fill()
t2 = Turtle()
t2.color(screen.bgcolor())
t2.begin_fill()
for _ in range(2):
t2.circle(100, 180)
t2.right(180)
t2.end_fill()
screen.exitonclick()
The above can also be done just as easy with a single turtle. And finally, for completeness, we could draw this figure via stamping, which we can do with one or more turtles:
from turtle import Screen, Turtle
screen = Screen()
t1 = Turtle()
t1.hideturtle()
t1.shape('circle')
t1.penup()
t1.shapesize(20)
t1.stamp()
t1.color(screen.bgcolor())
t1.shapesize(stretch_len=10)
t2 = t1.clone()
t2.shapesize(stretch_wid=10)
for sign in (-1, 1):
t2.sety(sign * 100)
t2.stamp()
t1.shape('square')
t1.backward(100)
t1.stamp()
screen.exitonclick()
Generally, I wouldn't clone() turtles unless there's some feature set on the original turtle that you want to preserve in the clone. In your code, you clone a brand new turtle which has no benefit over creating a new turtle instance. In the stamping example above, cloning is used to preserve the color and stretch-width of the original turtle before the original turtle itself changes.

Recursive rainbow-colored circles around each other

I'm trying to change the color and # of circles shown on the screen. So far, I've figured out how to make all of them different colors in a recursive pattern, but I need help finding out how to add more. Attached is what I have versus what I need to achieve.
my code
import turtle
import colorsys
def draw_circle(x,y,r,color):
turtle.seth(0)
turtle.up()
turtle.goto(x,y-r)
turtle.down()
turtle.fillcolor(color)
turtle.begin_fill()
turtle.circle(r)
turtle.end_fill()
def draw_recursive_circles(x,y,r,color,n):
if n == 0:
return
draw_circle(x,y,r,color)
colors = ['red','orange','yellow','green','blue','purple']
i = 0
for angle in range(30,360,60):
turtle.up()
turtle.goto(x,y)
turtle.seth(angle)
turtle.fd(r*2)
draw_recursive_circles(turtle.xcor(),turtle.ycor(),r/3,colors[i],n-1)
i += 1
turtle.tracer(0)
turtle.hideturtle()
turtle.speed(0)
draw_recursive_circles(0,0,100,'red',5)
turtle.update()
What I need to achieve
What I have so far
You import colorsys but never use it -- this is a clue that you're supposed to generate colors based on angles and not a fixed list of colors. The reason for the import is that turtle's RGB-based colors are the wrong model for our needs, so we want a more appropriate model, like HSV (where we only really care about H/hue), and have it convert those values to RGB.
The number of satellites is determined by your range call:
for angle in range(30,360,60):
Which for this drawing should be more like:
for angle in range(0, 360, 30):
As there are twelve satellites and 360 / 30 is 12. Finally, we need to do proper accounting such that whenever we change a position or heading, in order to do recursive drawing, we need to restore the original values on exit. Below is my simplified example solution to this problem:
from turtle import Screen, Turtle
from colorsys import hsv_to_rgb
def draw_circle(radius):
y = turtle.ycor() # save position & heading
heading = turtle.heading()
turtle.fillcolor(hsv_to_rgb(heading / 360, 1.0, 1.0))
turtle.sety(y - radius)
turtle.setheading(0)
turtle.begin_fill()
turtle.circle(radius)
turtle.end_fill()
turtle.sety(y) # restore position & heading
turtle.setheading(heading)
def draw_recursive_circles(radius, n):
if n == 0:
return
draw_circle(radius)
if n > 1:
heading = turtle.heading() # save heading
for angle in range(0, 360, 30):
turtle.setheading(angle)
turtle.forward(radius * 2)
draw_recursive_circles(radius / 5, n - 1)
turtle.backward(radius * 2)
turtle.setheading(heading) # restore heading
screen = Screen()
screen.tracer(False)
turtle = Turtle(visible=False)
turtle.penup()
draw_recursive_circles(150, 4)
screen.update()
screen.tracer(True)
screen.exitonclick()
I've intentionally kept the pen up to simplifiy my example so only filled portions of the circles are shown. Putting back the surrounding outlines I leave as an exercise for you.
The center circle is not the right color. Fixing this is a simple matter of setting the turtle's heading prior to the initial call to draw_recursive_circles()

randomly positioned circle clicking game

So I'm still very new to python and trying to learn through making small projects.
The game I'm making is meant to test your mouse accuracy by creating a bunch of random circles which the player is meant to click in a given amount of time. At the end of the game, it should tell the player their score, and how many misclicks they had.
I've been using turtle to try and do this, but I'm stuck:
import turtle
import random
t = turtle.Pen()
win = turtle.Screen()
win.bgcolor("lightgreen")
win.title("clicky")
def mycircle(red, green, blue):
t.color(red, green, blue)
t.begin_fill()
x = random.randint(10,50)
t.circle(x)
t.end_fill()
t.up()
y = random.randint(0,360)
t.seth(y)
if t.xcor() < -300 or t.xcor() > 300:
t.goto(0, 0)
elif t.ycor() < -300 or t.ycor() > 300:
t.goto(0, 0)
z = random.randint(0,100)
t.forward(z)
t.down()
for i in range(0, 20):
a = random.randint(0,100)/100.0
b = random.randint(0,100)/100.0
c = random.randint(0,100)/100.0
mycircle(a, b, c)
The main issues I've been trying to figure out are:
How can I make the circles spawn further from each other? They overlap
quite often and I want that to be avoided.
How can I make the circles spawn instantly rather than having to be
drawn?
How can I make the circles spawn further from each other?
We can keep track of circles already created and make sure their centers are at least a diameter away from each other. Your current circle placement logic is too complicated along with being faulty. Let's try to simplify it and make sure circles are drawn completely within the window.
How can I make the circles spawn instantly rather than having to be
drawn?
We could stamp them rather than draw them. However, since you are drawing so few circles, we can make every circle a turtle. This makes determining if you clicked on a circle, and removing that circle, simpler. I've added code, for you to expand on, that removes any circle that you click on:
from turtle import Turtle, Screen
from random import random, randint
CURSOR_SIZE = 20
def my_circle(color):
radius = randint(10, 50)
circle = Turtle('circle', visible=False)
circle.shapesize(radius / CURSOR_SIZE)
circle.color(color)
circle.penup()
while True:
nx = randint(2 * radius - width // 2, width // 2 - radius * 2)
ny = randint(2 * radius - height // 2, height // 2 - radius * 2)
circle.goto(nx, ny)
for other_radius, other_circle in circles:
if circle.distance(other_circle) < 2 * max(radius, other_radius):
break # too close, try again
else: # no break
break
circle.showturtle()
circle.onclick(lambda x, y, t=circle: t.hideturtle()) # expand this into a complete function
return radius, circle
screen = Screen()
screen.bgcolor("lightgreen")
screen.title("clicky")
width, height = screen.window_width(), screen.window_height()
circles = []
for _ in range(0, 20):
rgb = (random(), random(), random())
circles.append(my_circle(rgb))
screen.mainloop()
One issue you need to work out is making sure your circle color isn't too similar to (or the same as) your background color, otherwise you'll be hunting an invisible circle. Also, we might be able to speed up the circle drawing process even more, if needed.

Turtle Graphics "if touching colour"

I am trying to make a program in Turtle that draws a Christmas Tree and then some baubles, which I want to be placed randomly on the tree. However because a Christmas Tree is an irregular shape I am not able to place the baubles by randomly choosing x and y co-ordinates. Is there a way to randomly place the baubles on the tree?
I was considering an "turtle.pendown()" and then "if turtle.pen touching "green"" but I am not sure how to code this.
Any help would be greatly appreciated.
One simple, graphic, approach is to:
Find a Python module that has a routine for performing the "point
in polygon"
inclusion
test
Use turtle's begin_poly(), end_poly(), and get_poly() to capture the
vertices that your code generates when drawing the tree
Randomly generate ornaments within the bounding box of the tree but
also apply the crossing number test to see if their centers are on
the tree
Here's an example implementation using an (exceptionally) abstract tree and ornaments:
from turtle import Turtle, Screen
from random import randrange, choice
from point_in_polygon import cn_PnPoly
screen = Screen()
WINDOW_WIDTH, WINDOW_HEIGHT = screen.window_width(), screen.window_height()
COLORS = ["red", "yellow", "gold", "blue", "white", "pink"]
def draw_abstract_tree(turtle):
width = WINDOW_WIDTH//4
turtle.penup()
turtle.goto(0, -WINDOW_HEIGHT//4)
turtle.pendown()
for _ in range(8):
turtle.forward(width)
turtle.left(150)
turtle.forward(1.156 * width)
turtle.right(150)
width *= 0.9
turtle.left(210)
for _ in range(8):
turtle.forward(1.156 * width)
turtle.left(150)
turtle.forward(width)
turtle.right(150)
width /= 0.9
turtle.goto(0, -WINDOW_HEIGHT//4)
turtle.setheading(0)
def decorate_tree(turtle, polygon):
turtle.penup()
for _ in range(1000):
x = randrange(-WINDOW_WIDTH/4, WINDOW_WIDTH/4)
y = randrange(-WINDOW_HEIGHT/4, WINDOW_HEIGHT)
diameter = randrange(1, 12)
if cn_PnPoly((x, y), polygon):
turtle.goto(x, y)
turtle.color(choice(COLORS))
turtle.dot(diameter)
yertle = Turtle(visible=False)
yertle.speed("fastest")
yertle.color("darkgreen")
yertle.begin_poly()
draw_abstract_tree(yertle)
yertle.end_poly()
polygon = yertle.get_poly()
yertle.begin_fill()
draw_abstract_tree(yertle)
yertle.end_fill()
decorate_tree(yertle, polygon)
screen.exitonclick()
OUTPUT
I think turtle doesn't have method to check color.
But turtle uses Canvas from tkinter which have function find_overlaping(rectangle) to check if some objects overlaps this rectangle. Maybe it could works. Maybe you could check if there is tree in some small rectange in random place.
turtle.getcanvas()
tkinter: Canvas.find_overlapping()

Categories