Randomly choose positions of turtle circles without overlap - python

If I used randint() to choose the positions of turtle circles, how do I make sure they don't overlap. Here is my code for one circle. The screen has to be 500 x 500 pixels and the circles have to be 35 pixels:
asteroid.pencolor('grey')
asteroid.fillcolor('grey')
asteroid.begin_fill()
asteroid.hideturtle()
asteroid.penup()
asteroid.speed(15)
asteroid.setposition(randint(-400,400), randint(-400,400))
asteroid.pendown()
asteroid.circle(35)
asteroid.end_fill()

At first glance, your claim that "The screen has to be 500,500" and your use of the value -400/400 in the following:
setposition(randint(-400,400), randint(-400,400))
seem to be in conflict as a 500 x 500 screen implies randint(-250, 250)
Moving on, you can achieve your goal by testing each new asteroid against the locations of the others using something like any and turtle.distance():
from turtle import Screen, Turtle
from random import randint
WIDTH, HEIGHT = 500, 500
ASTEROID_RADIUS = 35
NUMBER_ASTEROIDS = 50
CURSOR_SIZE = 20
screen = Screen()
screen.setup(WIDTH, HEIGHT)
asteroid_prototype = Turtle()
asteroid_prototype.hideturtle()
asteroid_prototype.color('grey')
asteroid_prototype.shape('circle')
asteroid_prototype.shapesize(ASTEROID_RADIUS / CURSOR_SIZE)
asteroid_prototype.speed('fastest') # because 15 isn't a valid argument
asteroid_prototype.penup()
asteroids = []
for _ in range(NUMBER_ASTEROIDS):
asteroid = asteroid_prototype.clone()
asteroid.setposition( \
randint(ASTEROID_RADIUS - WIDTH/2, WIDTH/2 - ASTEROID_RADIUS), \
randint(ASTEROID_RADIUS - HEIGHT/2, HEIGHT/2 - ASTEROID_RADIUS) \
)
while any(map((lambda a: lambda b: a.distance(b) < ASTEROID_RADIUS)(asteroid), asteroids)):
asteroid.setposition( \
randint(ASTEROID_RADIUS - WIDTH/2, WIDTH/2 - ASTEROID_RADIUS), \
randint(ASTEROID_RADIUS - HEIGHT/2, HEIGHT/2 - ASTEROID_RADIUS) \
)
asteroid.showturtle()
asteroids.append(asteroid)
screen.exitonclick()

Related

Why is the turtle offset?

I am trying to create a program to move the turtle to where the mouse is.
Right now I am doing:
import turtle
t = turtle.Turtle()
canvas = turtle.getcanvas()
width = canvas.winfo_width()
height = canvas.winfo_height()
midpointX = width / 2
midpointY = height / 2
t.speed(0)
while True:
mouseX, mouseY = canvas.winfo_pointerxy()
turtleX = mouseX - midpointX
turtleY = (mouseY - midpointY) * -1
t.goto(turtleX, turtleY)
The turtle is offset when I run it in PyCharm or through the command line, but not when I run it on replit.
I am using Windows 11 if that helps.
This is the extent to which the turtle goes if my mouse is on the edge of the screen:
This happens for 2 reasons:
canvas.winfo_pointerxy() gives you the mouse coordinates relative to your screen, not the canvas
When you resize the window, width and height don't change because they're just stored values from the beginning
You can fix this by moving midpointX and midpointY inside the loop and using canvas.winfo_rootx(), which gives you the position on the screen:
import turtle
t = turtle.Turtle()
canvas = turtle.getcanvas()
t.speed(0)
while True:
mouseX, mouseY = canvas.winfo_pointerxy()
midpointX = canvas.winfo_width() / 2
midpointY = canvas.winfo_height() / 2
turtleX = mouseX - midpointX - canvas.winfo_rootx()
turtleY = -mouseY + midpointY + canvas.winfo_rooty()
t.goto(turtleX, turtleY)

How to convert window coords into turtle coords (Python Turtle)

I am trying to create a program to move the turtle to where the mouse is.
I am doing:
import turtle
t = turtle.Turtle()
canvas = turtle.getcanvas()
while True:
mouseX, mouseY = canvas.winfo_pointerxy()
t.goto(mouseX, mouseY)
but the turtle keeps moving off the screen.
I read from this question that canvas.winfo_pointerxy() returns 'window coordinates' (0, 0 at the top left of the window) and that I need to convert them to 'turtle coordinates' (0, 0 at the center of the window) but I don't know how to do that.
First, you need to find the size of the canvas. for this example, I used a set width and height, but for your purposes, you may find it easier to find the size instead of entering it.
width = 500
height = 300
t = turtle.Turtle()
canvas = turtle.getcanvas()
turtle.screensize(canvwidth=width, canvheight=height)
you can use this code to find the width and height
width = canvas.winfo_width()
height = canvas.winfo_height()
When you measure the mouse's position, however, you'll need to do this calculation to get the right value.
mouseX = canvas.winfo_pointerx() - width/2
mouseY = (canvas.winfo_pointery()*-1) + height/2
t.goto(mouseX, mouseY)
You have to use the dimensions of the window to calculate the center of the window. You just need to subtract the midpoint from the mouse coordinates (since the 0,0 for that is the top-left of the screen) to get it.
You need to add the following:
width = canvas.winfo_width()
height = canvas.winfo_height()
midpoint_x = width / 2
midpoint_y = height / 2
turtleX = mouseX - midpoint_x
turtleY = mouseY - midpoint_y
You end up with something like this:
import turtle
t = turtle.Turtle()
canvas = turtle.getcanvas()
width = canvas.winfo_width()
height = canvas.winfo_height()
midpoint_x = width / 2
midpoint_y = height / 2
while True:
mouseX, mouseY = canvas.winfo_pointerxy()
turtleX = mouseX - midpoint_x
turtleY = mouseY - midpoint_y
t.goto(turtleX, turtleY)

How to make the object bounce back with turtle

I want to make the yellow block jump on the line when there are other shapes. Also, how could I make the screen to close when the yellow block touches the other blocks.
I have though about using the time module and put time.sleep for 1 seconds after blockself.goto(0,30) then make the block go down to (0,0). However, when I did that the whole screen frozed for 1 second. This means that the time.sleep was not aimed for the blockself(yellow block) it self. Is there any way to fix this.
from turtle import Screen, Turtle
import random
WIDTH, HEIGHT = 800, 400
CURSOR_SIZE = 20
BASELINE = -CURSOR_SIZE/2
def move_block(block):
block.x -= CURSOR_SIZE/2
block.setx(block.x)
if block.x <= CURSOR_SIZE/2 - WIDTH/2:
block.x += WIDTH + CURSOR_SIZE
block.setx(block.x)
screen.update()
screen.ontimer(lambda: move_block(block), 40) # delay in milliseconds
screen = Screen()
screen.title("Jump over!")
screen.setup(WIDTH, HEIGHT)
screen.tracer(False)
marker = Turtle()
marker.hideturtle()
marker.penup()
marker.goto(WIDTH/2, BASELINE)
marker.pendown()
marker.goto(-WIDTH/2, BASELINE)
#three blocks
shape1=["square","triangle","circle"]
block_1 = Turtle(shape=(random.choice(shape1)))
block_1.up()
block_1.color('black')
block_1.x = WIDTH/2 + CURSOR_SIZE # user defined property
block_1.setx(block_1.x)
shape2=["square","triangle","circle"]
block_2 = Turtle(shape=(random.choice(shape2)))
block_2.up()
block_2.color('black')
block_2.x = block_1.x + 300
block_2.setx(block_2.x)
shape3=["square","triangle","circle"]
block_3 = Turtle(shape=(random.choice(shape3)))
block_3.up()
block_3.color('black')
block_3.x = block_2.x + 300
block_3.setx(block_3.x)
move_block(block_1)
move_block(block_2)
move_block(block_3)
# self
blockself= Turtle(shape="square")
blockself.color('yellow')
blockself.setx(0)
blockself.direction="Stop"
def goup():
blockself.up()
blockself.goto(0,30)
screen.listen ()
screen.onkey ( goup , "w" )
screen.mainloop()
We can make the yellow block vertical jump independent of the other motion using the same mechanism I gave you for the horizontal block motion, ontimer(). The jump() method below is invoked by the keyboard event, moving the yellow block up and down as a series of timer events:
from turtle import Screen, Turtle
from random import choice
SHAPES = ['square', 'triangle', 'circle']
WIDTH, HEIGHT = 800, 400
CURSOR_SIZE = 20
BASELINE = -CURSOR_SIZE/2
def move_block(block):
block.x -= CURSOR_SIZE/2
block.setx(block.x)
if block.x <= CURSOR_SIZE/2 - WIDTH/2:
block.x += WIDTH + CURSOR_SIZE
block.setx(block.x)
screen.update()
if block.distance(block_self) < CURSOR_SIZE:
screen.bye()
else:
screen.ontimer(lambda: move_block(block), 65) # delay in milliseconds
def jump(direction):
screen.onkey(None, 'w') # disable jumping while jumping
y = block_self.ycor()
if direction > 0 and y >= 40:
direction = -1
elif direction < 0 and y <= 0:
block_self.sety(0)
direction = 0
if direction:
block_self.sety(y + direction)
screen.ontimer(lambda: jump(direction), 25)
else:
screen.onkey(lambda: jump(1), 'w') # jump over, reenable jumping
screen.update()
screen = Screen()
screen.title("Jump over!")
screen.setup(WIDTH, HEIGHT)
screen.tracer(False)
marker = Turtle()
marker.hideturtle()
marker.penup()
marker.goto(WIDTH/2, BASELINE)
marker.pendown()
marker.goto(-WIDTH/2, BASELINE)
block_1 = Turtle(shape=choice(SHAPES))
block_1.penup()
block_1.color('black')
block_1.x = WIDTH/2 + CURSOR_SIZE # user defined property
block_1.setx(block_1.x)
block_2 = Turtle(shape=choice(SHAPES))
block_2.penup()
block_2.color('black')
block_2.x = block_1.x + 266
block_2.setx(block_2.x)
block_3 = Turtle(shape=choice(SHAPES))
block_3.penup()
block_3.color('black')
block_3.x = block_2.x + 266
block_3.setx(block_3.x)
block_self = Turtle(shape='square')
block_self.penup()
block_self.color('yellow')
move_block(block_1)
move_block(block_2)
move_block(block_3)
screen.onkey(lambda: jump(1), 'w')
screen.listen()
screen.mainloop()
Also, how could I make the screen to close when the yellow block
touches the other blocks.
It seems a severe response to user miscalculation but I put it in the code above by adding the following conditional to the move_block() function:
if block.distance(block_self) < CURSOR_SIZE:
screen.bye()
Finally, as you're discovering yourself, time.sleep() has no business being used in an event-driven world like turtle.

Why does turtle open an even smaller screen when the canvas is small?

I'm trying to draw on a small 200x200 screen using turtle, however the drawing doesn't pop up as full size, it opens a smaller window and I have to scroll up/down, left/right (just a bit) to see the whole drawing. I don't have this problem with larger windows. How do I prevent this?
import turtle
import random
height, width = 200, 200
screen = turtle.Screen()
screen.setup(width, height)
screen.setworldcoordinates(0, 0, width, height)
t = turtle.Turtle()
t.speed(1)
for _ in range(5):
t.penup()
t.goto(random.randint(20, width-20), random.randint(0, height-40))
t.pendown()
t.circle(20)
edit: screenshot, I want the actual size window instead of the scrolls
You could resize the window to 420×420.
If you don't want to resize your window, I suggest modifying the values for the keys "canvwidth" and "canvheight" keys in the turtle._CFG dictionary:
import turtle
import random
height, width = 200, 200
screen = turtle.Screen()
screen.setup(width, height)
screen.setworldcoordinates(0, 0, width, height)
turtle._CFG.update({"canvwidth": width-20, "canvheight": height-20}) # Removing the scroll bars
t = turtle.Turtle()
t.speed(1)
for _ in range(5):
t.penup()
t.goto(random.randint(20, width-20), random.randint(0, height-40))
t.pendown()
t.circle(20)
screen.exitonclick()
Using small windows in turtle is a can of worms. If #TheOneMusic's simple solution (+1) is good enough for your purposes, go for it! On my system, your setworldcoordinates() call gets rid of the scroll bars, so I don't even see the issue. So, another approximate solution might be to upgrade to current Python and tkinter.
However, neither is an exact solution. If we add code to draw a 200 x 200 box around our drawing area:
t.penup()
t.color('red')
t.goto(0, 0) # because of setworldcoordinates()
t.pendown()
for _ in range(4):
t.forward(200)
t.left(90)
We get the box skewed:
To solve this problem more precisely, involves uglier code:
from turtle import Screen, Turtle
from random import randint
TRUE_WIDTH, TRUE_HEIGHT = 200, 200
CURSOR_SIZE = 20 # for drawing frame around edge
RADIUS = 20
CHROME = 14 # magic number possibly derivable from tkinter
width, height = TRUE_WIDTH + CHROME, TRUE_HEIGHT + CHROME # needs to be slightly larger than 200 target
offset_x = CHROME / -2 + 2
offset_y = CHROME / 2 - 2
screen = Screen()
screen.setup(width, height)
screen.screensize(width/2, height/2) # backing store needs to be smaller than window
screen.setworldcoordinates(0, 0, TRUE_WIDTH, TRUE_HEIGHT)
# Draw red frame around edge to "prove" drawing area
frame = Turtle(shape='square', visible=False)
frame.shapesize(TRUE_HEIGHT / CURSOR_SIZE, TRUE_WIDTH / CURSOR_SIZE) # 200 x 200 frame
frame.color('red', 'white')
frame.penup()
frame.goto(TRUE_WIDTH/2 + offset_x, TRUE_HEIGHT/2 + offset_y)
frame.stamp()
turtle = Turtle()
turtle.speed('fastest') # because I have no patience
for _ in range(5):
turtle.penup()
turtle.goto(randint(RADIUS, TRUE_WIDTH - RADIUS) + offset_x, randint(0, TRUE_HEIGHT - RADIUS*2) + offset_y)
turtle.pendown()
turtle.circle(RADIUS)
screen.exitonclick()
But this sort of detail work could easily be undone by a future release of turtle and/or tkinter. If you can live with turtle's default window, life gets easier.

Python Turtle, change visible part

On the canvas,I draw two dots,right one is at (100,0),left one is at (-1000,0).After initializing the program,the orginal screen location(visible part) is near the right dot,just like pic1 show
pic 1:[1]: https://i.stack.imgur.com/KtPRN.png
And now I wanna move the the screen(visible part) to the left dot using coordinate so that i can see it(pic2).What should I do?
pic 2:https://i.stack.imgur.com/Rtfrv.png
def drawDot(x):
penup()
goto(x, 0)
pendown()
dot('pink')
write(x)
b = -1000 #left dot(-1000,0)
a = 100 #right dot(100,0)
speed(0)
delay(0)
tracer(0, 0)
hideturtle()
screensize(500,500)
color('red')
bgcolor('black')
drawDot(a)
drawDot(b)
done()
I believe the following does what you describe. When the window opens, it's centered on (0, 0) and point a is visible off to the right and point b isn't visible at all. When you click on the window, it scrolls so that the window is centered on point b:
from turtle import Screen, Turtle
WINDOW_WIDTH, WINDOW_HEIGHT = 500, 500
CANVAS_WIDTH, CANVAS_HEIGHT = 3000, 1000
def drawDot(x):
turtle.penup()
turtle.setx(x)
turtle.dot('pink')
turtle.write(x)
def scrollToDot(x, y): # unused arguments
canvas = screen.getcanvas()
# tkinter has a different coordinate system
# we have to describe left edge of scrolled
# window as percentage in its coordinates:
screen_center = CANVAS_WIDTH / 2
dot_center = screen_center + b
left_edge = dot_center - screen.window_width() / 2
canvas.xview_moveto(left_edge / CANVAS_WIDTH) # percentage
a = 100 # right dot(100, 0)
b = -1000 # left dot(-1000, 0)
screen = Screen()
screen.setup(WINDOW_WIDTH, WINDOW_HEIGHT) # What we see
screen.screensize(CANVAS_WIDTH, CANVAS_HEIGHT) # What there is
screen.bgcolor('black')
turtle = Turtle()
turtle.hideturtle()
turtle.speed('fastest')
turtle.color('red')
drawDot(a)
drawDot(b)
screen.onclick(scrollToDot)
screen.mainloop()
To do this, we have to access the tkinter Canvas underpinnings of turtle. However, the Canvas coordinate system is different than turtle's, so we have to make an adjustment as noted in the code comments.

Categories

Resources