3

I'm trying to make two turtles move together instead of one moving subsequently after the other. For example:

a = turtle.Turtle()
b = turtle.Turtle()

a.forward(100)
b.forward(100)

But this only makes them move one after the other. Is there a way to make them move together simultaneously?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
rasabi
  • 56
  • 1
  • 6
  • How is turtle class defined? Could you provide the code? – Banghua Zhao Nov 08 '18 at 00:08
  • In SO you should not add [ANSWERED] or similar in the title of the question, you must mark as correct the answer that is the solution to your problem, if no answer will help you then we invite you to publish your solution and mark it as correct. – eyllanesc Nov 15 '18 at 22:32

2 Answers2

4

Is there a way to make them move together simultaneously?

The best we can hope to do is make them appear to move simultaneously. Below are three increasingly complex approaches to this problem. But first, let's establish our baseline code, two turtles heading at each other and stopping when they meet at the origin:

from turtle import Screen, Turtle

screen = Screen()

a = Turtle('square', visible=False)
a.speed('slow')
a.color('red')
a.penup()
a.setx(-300)
a.setheading(0)
a.pendown()
a.showturtle()

b = Turtle('circle', visible=False)
b.speed('slow')
b.color('green')
b.penup()
b.setx(300)
b.setheading(180)
b.pendown()
b.showturtle()

### Subsequent variations start here ###

a.forward(300)
b.forward(300)

### Subsequent variations end here ###

screen.mainloop()

The above doesn't do what we want as one turtle moves and then the other. For our first variation, we simply chop up the motion into smaller units and alternate:

###

for _ in range(300):
    a.forward(1)
    b.forward(1)

###

Our next variation uses timer events to control the motion of the two turtles:

###

def move(turtle):
    turtle.forward(1)

    if turtle.distance(0, 0) > 1 :
        screen.ontimer(lambda t=turtle: move(t), 50)

move(a)
move(b)

###

Our final variation uses threading to independently control the two turtles. Each turtle is a thread and there's a third, main thread that handles all the graphics operations for the turtle threads. This is needed as turtle operates atop tkinter which has issues handling graphics from secondary threads:

###

from threading import Thread, active_count
from queue import Queue

QUEUE_SIZE = 1

def process_queue():
    while not actions.empty():
        action, *arguments = actions.get()
        action(*arguments)

    if active_count() > 1:
        screen.ontimer(process_queue, 100)

actions = Queue(QUEUE_SIZE)  # a thread-safe data structure

def move(turtle):
    while turtle.distance(0, 0) > 1:
        actions.put((turtle.forward, 1))

Thread(target=move, args=[a], daemon=True).start()
Thread(target=move, args=[b], daemon=True).start()

process_queue()

###
cdlane
  • 40,441
  • 5
  • 32
  • 81
  • Thanks! Since python is a pretty basic (but strong) language, I'm sticking to making them appear to be moving "simultaneously". – rasabi Nov 15 '18 at 22:17
1

You can't do this as a guaranteed part of the Turtle interface. You could try doing these in parallel processes, but there's no guarantee of simultaneous movement. Some Python run-time systems handle parallel processes with sequential time-slicing of a single process. You may also find that one move completes while the other process is initializing. If you want to try tighter management, build two processes that hold on a process lock; have the main program release both locks in a critical section ... and you might get something close to the desired functionality.

If you're trying to do something this graphically mature, you might consider a larger graphics package, such as PyGame.

Prune
  • 76,765
  • 14
  • 60
  • 81