How to move multiple turtles at the same time?
Asked Answered
F

3

5

I have an assignment which is asked to set two turtles in a race track (same size but separate track). I am able to make them move, but the second one moves only when the first one moved a half of the track. I'm not sure how to make the turtles move at the same time. Here is my code:

import turtle
import random
import time

wn = turtle.Screen()
wn.bgcolor("lightgreen")

t = turtle.Turtle()
t.shape('turtle')
t.color('red')

t2 = turtle.Turtle()
t2.shape('turtle')
t2.color('blue')

#user input function

p = float(input('please insert the perimeter:'))

#set the track
def drawTrack(p,r):
    shortside = (p/2.0)/(r+1)
    longside = r*shortside
    turtle.setup((shortside*2)+60, longside +40)
    t.penup()
    t2.penup()
    t.setposition(-shortside-10, -longside/2)
    t2.setposition(10, -longside/2)   
    for i in range (2):
        #first track
        t.speed(1)
        t.pendown()
        t.forward(shortside)
        t.left(90)
        t.forward(longside)
        t.left(90)
        
        #second track
        t2.speed(1)
        t2.pendown()
        t2.forward(shortside)
        t2.left(90)
        t2.forward(longside)
        t2.left(90) 

drawTrack(p,2)

wn.exitonclick()
Firstfoot answered 14/10, 2016 at 19:9 Comment(3)
Unfortunately, python turtle works by doing a step by step so there is no way for you to move 2 turtles at a time. You also can't use threading to do this.Heavyweight
Does this answer your question? Python Multiple Turtles Moving (seemingly) SimultaneouslyCompartment
@JonathanDrukker It's very much possible to do this. See the answers here and other threads.Compartment
P
5

I make the turtles each move a tiny increment repetitively so they only ever are at max 1 pixel apart:

# Setup 
import turtle

screen = turtle.Screen()
screen.bgcolor("white")
screen.title("Turtle Movement")
screen.setup(width=500, height=500)

# Object 1 set up
obj1 = turtle.Turtle()
obj1.color("red") # Sets obj1's colour
obj1.speed(0) # The drawing speed will go as fast as it can if it is set to zero
obj1.penup() # Don't want it to draw a line as I goes to 0, -50
obj1.goto(0, -50) # goes to 0, -50
obj1.pendown() # Now we want it to draw

# Object 2 set up
obj2 = turtle.Turtle()
obj2.color("blue") # sets obj2's colour
obj2.speed(0) # The drawing speed will go as fast as it can if it is set to zero
obj2.penup() # Don't want it to draw a line as I goes to 0, -50
obj2.goto(0, 50) # goes to 0, 50
obj2.pendown() # Now we want it to draw

# Movement of objects
while True: # infinite loop
    obj1.forward(1) # Moves obj1 forwards by 1
    obj2.forward(1) # Moves obj2 forwards by 1

Note: The smaller the 'Movement amount' is, e.g forward(Movement amount is in here), the smoother the movement gets, but you will lose speed. I encourage you to tweak the values and experiment!

Pallaton answered 25/2, 2020 at 8:36 Comment(0)
C
4

There are a couple of ways you can go about this.

One approach to use the screen.ontimer() event (see the turtle documentation). This approach is nice as you can adjust the turtles to actual clock time and this can run within the turtle event loop so that other events can also take place (like exitonclick()).

The approach I used below is to break up turtle motion into tiny steps inside a Python generator which yields after every bit of motion. This allows us to alternate between the turtles. The race takes place before the turtle event loop so exitonclick() is invalid until the race is over.

To provide a differential in speed, I've used the turtle drawing speed as part of the motion calculation so if you say turtle1.speed("fast") it will move fast compared to a turtle2.speed("slow"). There are other ways to go about this using random and/or varying speeds.

I've changed the prompt for the perimeter to be a dialog box and made various style tweaks:

from turtle import Turtle, Screen

screen = Screen()
screen.bgcolor("lightgreen")

turtle1 = Turtle(shape='turtle')
turtle1.color('red')
turtle1.speed("slow")  # = 3
turtle1.penup()

turtle2 = Turtle(shape='turtle')
turtle2.color('blue')
turtle2.speed(4)  # "slow" (3) < 4 < "normal" (6)
turtle2.penup()

# user input function

perimeter = screen.numinput("Track Perimeter", "Please enter the perimeter:", default=2000, minval=500, maxval=3000)

def full_track_crawl(turtle, shortside, longside):
    speed = turtle.speed()
    turtle.pendown()

    for j in range (2):
        for i in range(0, int(shortside), speed):
            turtle.forward(speed)
            yield(0)
        turtle.left(90)
        for i in range(0, int(longside), speed):
            turtle.forward(speed)
            yield(0)
        turtle.left(90)

    turtle.penup()

# set the track
def drawTrack(perimeter, ratio):
    shortside = (perimeter / 2.0) / (ratio + 1)
    longside = ratio * shortside

    screen.setup(shortside * 2 + 60, longside + 40)

    turtle1.setposition(-shortside - 10, -longside / 2)
    turtle2.setposition(10, -longside / 2)   

    generator1 = full_track_crawl(turtle1, shortside, longside)
    generator2 = full_track_crawl(turtle2, shortside, longside)

    while (next(generator1, 1) + next(generator2, 1) < 2):
        pass

drawTrack(perimeter, 2)

screen.exitonclick()

Happy racing!

Carthusian answered 14/10, 2016 at 22:21 Comment(3)
yield (0), while (next(generator1, 1) + next(generator2, 1) < 2): pass. what #1 in the bracket means?Firstfoot
@CodeLearner, there's a bit of trickery going on here. The generators either yield 0 or run out of results. The optional second argument to next(..., 1) is what we want returned if the generator has run out of results. So, until the sum of the two next() statements equals 2, one of the generators is still yielding results. I.e. at least one of the turtles is still running the race, so keep invoking the generators.Carthusian
Thanks for the replying. I have searched what yield and generator means but Im not sure about them. next(..., 1), next(...,1 )<2. What do #1 and #2 mean? Can i replace them with any different #?Firstfoot
C
0

Moving turtles in small increments in a round-robin manner as described elsewhere in this thread is a good solution for many cases, but a more flexible and general solution is to disable turtle's update loop with tracer(0), then use ontimer and update to roll your own.

Here's a proof of concept generalized to n turtles you can adapt to your use case:

from random import randint
from turtle import Screen, Turtle


def tick():
    for t in turtles:
        t.forward(1)

        if randint(0, 1):
            t.left(randint(-5, 5))

    screen.update()
    screen.ontimer(tick, 1000 // 30)


screen = Screen()
screen.tracer(0)
turtles = [Turtle() for _ in range(20)]

for t in turtles:
    t.setheading(randint(0, 360))

tick()
screen.exitonclick()

See also: Python Multiple Turtles Moving (seemingly) Simultaneously

Compartment answered 14/2 at 19:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.