user2006082
user2006082

Reputation: 135

How to Make Turtles Move Together?

Here is my code (Python beginner, please bear any of the unprofessional code), and basically what I want is to let the two turtles move on the same circle together (as you may guess, my task is about simulating a spaceship chasing the ISS). In my code, first turtle will move around the circle, then the second turtle:

from turtle import *
rocket=Turtle()
ISS=Turtle()
counter=1
title("ISS")
screensize(750,750)
ISS.hideturtle()
rocket.hideturtle()
ISS.penup()
ISS.left(90)
ISS.fd(250)
ISS.left(90)
ISS.showturtle()
ISS.pendown()
rocket.penup()
rocket.fd(250)
rocket.left(90)
rocket.showturtle()
rocket.pendown()
while counter==1:
    ISS.speed(1)
    rocket.speed(2)
    ISS.circle(250)
    rocket.circle(250)

My teacher has told me that "threading" will work for this, but I don't quite understand that. It would be much appreciated if someone can help me out with this ;)

Upvotes: 0

Views: 2373

Answers (2)

cdlane
cdlane

Reputation: 41872

In an event-driven world like turtle, you shouldn't do this:

counter = 1
...
while counter==1:

This has the potential of locking out events. Also, there's no way to quit this program cleanly. Instead of moving different distances in a while loop, let's move a constant distance but with different delays on in an ontimer() event (should work with Python 2 or 3):

from turtle import Turtle, Screen

RADIUS = 250
ISS_DELAY = 100  # milliseconds
ROCKET_DELAY = 75  # milliseconds
CURSOR_SIZE = 20

def move_ISS():
    ISS.circle(RADIUS, 1)

    screen.ontimer(move_ISS, ISS_DELAY)

def move_rocket():
    rocket.circle(RADIUS, 1)

    # rocket slows to ISS' speed once docked
    delay = ISS_DELAY if rocket.distance(ISS) <= CURSOR_SIZE else ROCKET_DELAY

    screen.ontimer(move_rocket, delay)

screen = Screen()
screen.title("ISS")
screen.setup(750, 750)

ISS = Turtle("square", visible=False)
ISS.speed('fastest')
ISS.penup()
ISS.left(180)
ISS.sety(RADIUS)
ISS.showturtle()
ISS.pendown()

rocket = Turtle("triangle", visible=False)
rocket.speed('fastest')
rocket.penup()
rocket.left(90)
rocket.setx(RADIUS)
rocket.showturtle()
rocket.pendown()

move_ISS()
move_rocket()

screen.exitonclick()

You can click anywhere on the window at any time to exit the program cleanly with respect to the event handler. And I threw in some logic to make the space ship stick with the ISS once they dock rather than fly past.

This also can be done with threads, but all the graphical operations have to be channeled through the main thread to avoid Tkinter errors (a Python3-only solution):

from threading import Thread, active_count
from turtle import Turtle, Screen
from queue import Queue  # use for thread-safe communications
from time import sleep

RADIUS = 250
ISS_DISTANCE = 3
ROCKET_DISTANCE = 4
CURSOR_SIZE = 20
QUEUE_SIZE = 1

def move_ISS(turtle):
    while True:
        actions.put((turtle.circle, RADIUS, ISS_DISTANCE))
        sleep(0.1)

def move_rocket(turtle):
    while True:
        # rocket slows to ISS' speed once docked
        distance = ISS_DISTANCE if rocket.distance(ISS) <= CURSOR_SIZE else ROCKET_DISTANCE

        actions.put((turtle.circle, RADIUS, distance))
        sleep(0.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)

screen = Screen()
screen.title("ISS")
screen.setup(750, 750)

ISS = Turtle("square", visible=False)
ISS.speed('fastest')
ISS.penup()
ISS.left(180)
ISS.sety(RADIUS)
ISS.showturtle()
ISS.pendown()

ISS_thread = Thread(target=move_ISS, args=[ISS], daemon=True)

rocket = Turtle("triangle", visible=False)
rocket.speed('fastest')
rocket.penup()
rocket.left(90)
rocket.setx(RADIUS)
rocket.showturtle()
rocket.pendown()

rocket_thread = Thread(target=move_rocket, args=[rocket], daemon=True)

ISS_thread.start()
rocket_thread.start()

process_queue()

screen.exitonclick()

Upvotes: 0

jgritty
jgritty

Reputation: 11915

There is a turtle limitation that doesn't allow it to work multithreaded.

Although, you don't have to move the turtle around the whole circle, you can just move it part way. Also, I think you've misunderstood what speed does. It's just the speed that the turtle draws at.

from turtle import *

def move(thing, distance):
    thing.circle(250, distance)

def main():
    rocket = Turtle()
    ISS = Turtle()
    rocket.speed(10)
    ISS.speed(10)
    counter = 1
    title("ISS")
    screensize(750, 750)
    ISS.hideturtle()
    rocket.hideturtle()
    ISS.penup()
    ISS.left(90)
    ISS.fd(250)
    ISS.left(90)
    ISS.showturtle()
    ISS.pendown()
    rocket.penup()
    rocket.fd(250)
    rocket.left(90)
    rocket.showturtle()
    rocket.pendown()

    while counter == 1:
        move(ISS, 3)
        move(rocket, 4)

if __name__ == '__main__':
    main()

I took out the repeated step of moving the thing, either the ISS or the rocket, and made that a function. I upped the speed of the drawing to 10, because I thought it just looked smoother. The ISS now moves only like 3/4 as far as the rocket on each step.

Upvotes: 1

Related Questions