How can I detect the collision of multiple turtle objects? - python

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()

Related

Is there a way to draw multiple circles with Turtle based on user input?

I want to create something that looks like this: many circles of the same size next to each other
However, I want the number of circles to be determined by user input. I can't seem to find any information on how I might go about this.
Here's what i have so far but it does not accomplish my goals.
import turtle
print("How many circles?")
circnum = input()
#Summoning the turtle
t = turtle.Turtle()
#circling the circle
for i in circnum:
r = 25
t.circle(r)
Many thanks!
You need to make circnum a number so that you can make a range to iterate over, and you need to move the turtle in between circles so you aren't just drawing the same circle on top of itself over and over.
import turtle
print("How many circles?")
circnum = int(input())
#Summoning the turtle
t = turtle.Turtle()
#circling the circle
for _ in range(circnum):
t.circle(25)
t.forward(5)
I agree with #Samwise's suggestions (+1) but if you're using standard Python 3 turtle, and not some older version or subset, I say get rid of input() and go full turtle on it:
from turtle import Screen, Turtle
RADIUS = 25
DISTANCE = 10
screen = Screen()
number_circles = screen.numinput("A Circle in a Spiral", "How many circles?", default=10, minval=1, maxval=30)
if number_circles:
# Summoning the turtle
turtle = Turtle()
turtle.speed('fast') # because I have little patience
# Circling the circle
for _ in range(int(number_circles)): # numinput() returns a float
turtle.circle(RADIUS)
turtle.forward(DISTANCE)
screen.exitonclick()
else:
# user hit 'Cancel' in the number input dialog
screen.bye()

Turtle drawing a hexagon and hexagon grid

current code
#import the turtle modules
import turtle
#Start a work Screen
ws=turtle.Screen()
#Define a Turtle Instance
geekyTurtle=turtle.Turtle()
#executing loop 6 times for 6 sides
for i in range(6):
#Move forward by 90 units
geekyTurtle.forward(90)
#Turn left the turtle by 300 degrees
geekyTurtle.left(300)
My goal is to make a hexagon grid pattern and I am failing to do it properly. My first issue is if you run the code you get a hexagon but the top is flat, I can't get it to get the pointy corners to get on top. Second I tried to make the grid and it failed and I am not sure why I am unable to copy the same hexagon and clone it next to the other. I will or should have a file of the image that I am going for below.
The output I am getting:
The output I am trying to get:
Before going into loop, turn 30 degrees.
geekyTurtle.right(30)
In order to have its clone beside, just put the turtle to the new place and draw the shape again:
for i in range(6):
geekyTurtle.forward(90)
geekyTurtle.left(300)
geekyTurtle.up()
geekyTurtle.goto(90 * 3 ** .5, 0)
geekyTurtle.down()
for i in range(6):
geekyTurtle.forward(90)
geekyTurtle.left(300)
Put it in a loop to have it for more than two times
You can use the idea of .up() and .goto(x, y) and .down() to draw grids.
It seems like this is a problem that recursion could simplify in a fractal-like way. Each side of the initial hexagon is itself a hexagon, and so forth, filling the available space:
from turtle import Screen, Turtle
SIDE = 75 # pixels
def hexagon(side, depth):
if depth > 0:
for _ in range(6):
turtle.forward(side)
turtle.right(60)
hexagon(side, depth - 1)
turtle.left(120)
screen = Screen()
screen.tracer(False) # because I have no patience
turtle = Turtle()
turtle.penup()
turtle.width(2)
turtle.sety(-SIDE) # center hexagons on window
turtle.pendown()
turtle.left(30) # optional, orient hexagons
hexagon(SIDE, depth=6) # depth depends on coverage area
turtle.hideturtle()
screen.tracer(True)
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.

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()

Python - Make One Turtle Object Always Above Another

I would like to create a program where one Turtle object always stays above all of the other Turtle objects. I don't know if this is possible, but any help would be apprecated.
This is my code:
from turtle import *
while True:
tri = Turtle()
turtle = Turtle()
tri.pu()
tri.pencolor("white")
tri.color("black")
tri.shape("turtle")
tri.bk(400)
turtle = Turtle()
turtle.pu()
turtle.pencolor("white")
turtle.shape("square")
turtle.color("white")
turtle.pu()
turtle.speed(0)
tri.speed(0)
turtle.shapesize(100,100,00)
setheading(towards(turtle))
while tri.distance(turtle) > 10:
turtle.ondrag(turtle.goto)
tri.setheading(tri.towards(turtle))
tri.fd(5)
clearscreen()
Why not just do all the drawing for the "bottom" turtle first? Then do the drawing for the "top" turtle? This should make the top turtle always visible.
My Observed Rules of Turtle Layering:
Multiple Turtles moving to same location: last to arrive is on top.
Same thing drawn by multiple turtles: there are no rules!
To illustrate my second point, consider this code:
from turtle import Turtle, Screen
a = Turtle(shape="square")
a.color("red")
a.width(6)
b = Turtle(shape="circle")
b.color("green")
b.width(3)
b.goto(-300, 0)
b.dot()
a.goto(-300, 0)
a.dot()
a.goto(300, 0)
b.goto(300, 0)
screen = Screen()
screen.exitonclick()
Run it and observe the result. On my system, the final goto() draws a long green line over the red one but the green line disappears as soon as it has finished drawing. Comment out the two calls to dot() and observe again. Now the green line remains over the red one. Now change the calls from dot() to stamp() or circle(5) instead. Observe and formulate your own rule...
Now back to your example, which is badly flawed (you're actually manipulating three turtles, not two!) Here's my simplification:
from turtle import Turtle, Screen
tri = Turtle(shape="turtle")
tri.color("black")
tri.pu()
turtle = Turtle(shape="square")
turtle.shapesize(4)
turtle.color("pink")
turtle.pu()
def drag_handler(x, y):
turtle.ondrag(None)
turtle.goto(x, y)
turtle.ondrag(drag_handler)
turtle.ondrag(drag_handler)
tri.bk(400)
while tri.distance(turtle) > 10:
tri.setheading(tri.towards(turtle))
tri.fd(5)
screen = Screen()
screen.mainloop()
You can tease tri by dragging the pink square until tri catches up with it. Ultimately, tri will land on top as long as the square isn't moving when tri catches it. If you drag the square over tri, then it will temporarily cover him as it is the "last to arrive".

Categories