In Python, how can I execute two Turtle commands simultaneously? - python

As in having two Turtles moving at once. For example, I import two turtles, then try to have both of them move forward alongside each other. How can I do this?

bob = turtle.Turtle()
john = turtle.Turtle()
def move_turtles(ammount):
for i in range(ammount // 10):
bob.forward(10)
john.forward(10)
move_turtles(100)
There's no way to move them at the same time, although you can use something like that. It moves the turtles by 10 points each, so it gives the impression that they are moving together, but they are actually moving separately by little ammounts. It repeats the operation (ammount //10) times, and moves 10 in each iteration, so if you were to give 50 as an input, it would move 5 times 10 points, resulting in 50. You can then customize the function to move by a little a turtle so they don't overlap and so on.

You can move multiple turtles independently at the same time using timer events -- you can even have them move at different rates, both in time and space:
import turtle
turtle.setworldcoordinates(0, -100, 100, 100)
bob = turtle.Turtle(shape="turtle")
bob.penup()
bob.sety(20)
john = turtle.Turtle(shape="turtle")
john.penup()
john.sety(-20)
def move_bob():
bob.forward(1)
if bob.xcor() < 90:
turtle.ontimer(move_bob, 75)
def move_john():
john.forward(2)
if john.xcor() < 90:
turtle.ontimer(move_john, 100)
move_bob()
move_john()
turtle.exitonclick()
Other folks also use threads to achieve this but timer events are built into the turtle module.

There is a way to control frames using the Turtle module - you need to change the screen attributes.
screen = turtle.Screen()
screen.tracer(0)
This method makes all turtle movement invisible until you run screen.update(), and then every turtle will be visually updated at the same time on the screen. In your case, you can write screen.update() after the movement of both turtles, and they will appear to move at the same time.

Related

How do I prevent the barriers and the doors overlapping?

# Imported Modules for Randomization and Turtle
import turtle as trtl
import random as rand
from random import *
# Painter Configuration and Screen Configuration
painter = trtl.Turtle()
distanceForward = 20
painter.pensize(5)
painter.speed(10000000)
painter.screen.setup(1000, 1000)
walls = 32
#This for loop is essential--> it creates 32 lines, and based on the conditions below, adds walls, barriers, and exits
for i in range(walls):
#Register the beginning position of the Turtle.
xBeginCor = painter.xcor()
yBeginCor = painter.ycor()
#Variables for randomization of Exits and Doors
initialWallDistance = randint(1, distanceForward-5)
doorDistance = randint(1,distanceForward-18)
#Program for the Walls and their Randomization
#Prevents the last 4 lines having barriers protruding
#We feel this method of randomizing the wall distance was really innovative and that it works pretty well
if i < walls - 4:
painter.penup()
painter.forward(initialWallDistance)
painter.left(90)
painter.pendown()
painter.forward(30)
painter.backward(30)
painter.right(90)
painter.penup()
#Preventing overlapping for the Walls and Doors. This does not work perfectly, and sometimes the doors are too small as a result of this
if doorDistance == range(0, 21):
doorDistance + 20
painter.forward(distanceForward - initialWallDistance)
else:
painter.forward(distanceForward - initialWallDistance)
#Creates the randomization of the doors. This works really well, as it makes the turtle go a random distance forward from the beginning of the line, and create a door
painter.goto(xBeginCor, yBeginCor)
painter.pendown()
painter.forward(doorDistance)
painter.penup()
painter.forward(20)
painter.pendown()
painter.forward(distanceForward-doorDistance)
#Turn the turtle to create the next line
painter.right(90)
#Change the length of the line so the next one is a longer distance
distanceForward += 15
#Keeps window open
wn = trtl.Screen()
wn.mainloop()
I need help stopping these doors and barriers happening on top of each other. I have also attached an image describing what I mean.
This code here is what I am working on to prevent the two from going on top of each other:
if doorDistance == range(0, 21):
doorDistance + 20
painter.forward(distanceForward - initialWallDistance)
else:
painter.forward(distanceForward - initialWallDistance)
Nothing is working. At this point I am completely confused, and I have no idea what I am doing. Any explanations/help would be appreciated
Note: I am a beginner, so I will not be able to use any complex techniques
This code doesn't work Python-wise:
if doorDistance == range(0, 21):
doorDistance + 20
It should be something like:
if 0 <= doorDistance <= 20:
doorDistance += 20
But that's not going to fix your problem. Your logic to avoid door and barrier overlap doesn't work. As you spiral from the center, you put up barriers pointing to the next layer, and leave door gaps. But the overlaps happen on the next spiral when all memory of where those barriers are located is lost.
My rework of your code into Python (still broken):
# Imported Modules for Randomization and Turtle
from turtle import Screen, Turtle
from random import randint
WALLS = 32
# Painter Configuration and Screen Configuration
screen = Screen()
screen.setup(1000, 1000)
painter = Turtle()
painter.hideturtle()
painter.pensize(5)
painter.speed('fastest')
distanceForward = 20
# This for loop is essential--> it creates 32 lines, and based on the conditions below, adds walls, barriers, and exits
for wall in range(WALLS):
# Register the beginning position of the Turtle.
beginCor = painter.position()
# Variables for randomization of Exits and Doors
initialWallDistance = randint(1, distanceForward-5)
doorDistance = randint(1, distanceForward-18)
# Program for the Walls and their Randomization
# Prevents the last 4 lines having barriers protruding
# We feel this method of randomizing the wall distance was really innovative and that it works pretty well
if wall < WALLS - 4:
painter.penup()
painter.forward(initialWallDistance)
painter.left(90)
painter.pendown()
painter.forward(30)
painter.penup()
painter.backward(30)
painter.right(90)
# Preventing overlapping for the Walls and Doors. This does not work perfectly, and sometimes the doors are too small as a result of this
if 0 <= doorDistance <= 20:
doorDistance += 20
painter.forward(distanceForward - initialWallDistance)
# Creates the randomization of the doors. This works really well, as it makes the turtle go a random distance forward from the beginning of the line, and create a door
painter.goto(beginCor)
painter.pendown()
painter.forward(doorDistance)
painter.penup()
painter.forward(20)
painter.pendown()
painter.forward(distanceForward - doorDistance)
# Turn the turtle to create the next line
painter.right(90)
# Change the length of the line so the next one is a longer distance
distanceForward += 15
# Keeps window open
screen.mainloop()
Your logic seems like it could produce unsolvable mazes, like your example one.

Create random-path by randomly choosing from lists of stored turtle-graphics functions

I have stored turtle-graphics functions in list and am using random functions to call it to create a random-path, however code does not work.
Can someone please have a look on this and provide suggestion.
from turtle import Turtle
from turtle import Screen
import random
pen = Turtle()
pen.pensize(8)
pen.speed(10)
window = Screen()
window.colormode(255)
moves=[pen.forward(30),pen.backward(30)]
turns=[pen.right(90),pen.left(90)]
is_true = True
while is_true:
pen.color(random.randint(0,255),random.randint(0,255),random.randint(0,255))
random.choice(turns)
random.choice(moves)
window.exitonclick()
I would say the issue here is that you are using functions as data when you could simply use data as data. That is, giving forward() a negative distance is the same as backward(). Giving left() a negative angle is the same as right(). So we can simply do:
from turtle import Screen, Turtle
from random import random, choice
DISTANCES = [30, -30]
ANGLES = [90, -90]
def move():
turtle.color(random(), random(), random())
turtle.left(choice(ANGLES))
turtle.forward(choice(DISTANCES))
screen.ontimer(move, 10)
screen = Screen()
turtle = Turtle()
turtle.pensize(8)
turtle.speed('fastest')
move()
screen.exitonclick()
I also dealt the next issue, your implicit while True:. The way you structured your code, the exitonclick() is never reached and doesn't work. Now it works as we've kept both the drawing and exitonclick() in the event loop.
You execute the methods only when you define the two list. Change the relevant part of the code like this
moves=[pen.forward, pen.backward]
turns=[pen.right, pen.left]
while True:
pen.color(random.randint(0,255), random.randint(0,255), random.randint(0,255))
random.choice(turns)(90)
random.choice(moves)(30)
The first problem is that you don't list function calls in the lists moves and turns but the results of the calls. The second problem is that you don't call functions after the random.choice calls. What you are actually getting from this is the flickering effect of the visible pen tip that endlessly changes the color.
On way to fix it is already shown in buran's answer. Another way that keeps the turn and move arguments out of the loop is the following, here lambda : transforms the function calls into anonymous functions to which references are stored in moves and turns:
Another option is to extract the actual moves and turns into functions
from turtle import Turtle
from turtle import Screen
import random
pen = Turtle()
pen.pensize(8)
pen.speed(10)
window = Screen()
window.colormode(255)
moves=[lambda : pen.forward(30), lambda : pen.backward(30)]
turns=[lambda : pen.right(90), lambda : pen.left(90)]
for _ in range(100):
pen.color(random.randint(0,255),random.randint(0,255),random.randint(0,255))
random.choice(turns)()
random.choice(moves)()
window.exitonclick()
I decided to only draw 10 lines, so the turtle will most likely remain on screen. For a better way to get rid of the while True loop (including explanation) see cdlane's answer!

Turtle control the rate of refresh using sleep does not refresh properly

I'm controlling turtle animation by using tracer(0, 0) and stamping the turtle cursor itself.
However, it refreshes strangely, it's suppose to be 1 second between any animation (with sleep) but it seems to sometimes hang for two second, then a couple of second later zaps into position:
The code is:
import turtle
import time
screen = turtle.Screen()
screen.listen()
screen.tracer(0, 0)
pen = turtle.Turtle()
pen.speed('fastest')
pen.shape("circle")
pen.penup()
pen.hideturtle()
next = [0, 8]
time.sleep(10)
while True:
pen.goto(next[0]*20, next[1]*20)
screen.clear()
pen.stamp()
screen.update()
time.sleep(1)
next = [next[0], next[1]-1]
Seems like simply sleeping is not a good idea. I seem to also have trouble getting it to work with threading. How do I it works with a predefined time like 1 second in this case?
Depending on the efficiency of your computer, time.sleep(1) would have different durations.
Instead of relying on the efficiency of your computer to adjust the sleep duration, you can use the perf_counter method to detect how long your program has been running from one part to another.
Basically, once you run the program, the time tracking starts. Where ever you call time.perf_counter(), it will return the amount of time for the start of the program to reach the place you called time.perf_counter().
import turtle
import time
screen = turtle.Screen()
screen.listen()
screen.tracer(0, 0)
pen = turtle.Turtle()
pen.speed('fastest')
pen.shape("circle")
pen.penup()
pen.hideturtle()
nxt = [0, 8]
time.sleep(10)
while True:
start = time.perf_counter() # Find the total performance time from the start of the program to here
pen.goto(nxt[0]*20, nxt[1]*20)
screen.clear()
pen.stamp()
screen.update()
while time.perf_counter() - start < 1: # Keep looping until the amount of performance time from right under "while True" to this line reaches 1 second
pass
nxt = [nxt[0], nxt[1]-1]
Using sleep and while True: loops in an event-driven world like turtle is a bad idea. We can instead use a timer event to produce the effect you describe. While we're at it, we're going to toss stamping as that's not adding anything, and neither are clear() and listen():
from turtle import Screen, Turtle
CURSOR_SIZE = 20
def drop():
screen.update()
turtle.sety(turtle.ycor() - CURSOR_SIZE)
screen.ontimer(drop, 1000) # 1 second
screen = Screen()
screen.tracer(False)
turtle = Turtle()
turtle.shape('circle')
turtle.penup()
turtle.sety(160)
screen.ontimer(drop, 10_000) # 10 seconds
screen.mainloop()
Of course, you need to add some logic to avoid restarting the timer event if/when the circle falls off the bottom of the screen. By using while True: loops, and sleep() you potentially block events, like user keystrokes or window closing, from reaching turtle.
One minor inaccuracy here is the drop() method takes some minimal time to run -- to factor that out, if needed/desired, you need to adjust the timing based on clock time or something like time.perf_counter() as #AnnZen demonstrates, but not in a pass loop!
EDIT:

Having trouble timing turtle movement

I wrote the following code, which creates expanding squares at two random locations. I would like to write a function f(squares, seconds) so that if the user inputs f(5,10), a new square would begin forming every 10 seconds, until 5 had formed.
I can't seem to find anything which lets me start a new square while one is still forming. I can either make two form at the same time, as in the code below, or have one be completed, and then for another to start forming. Help?
import sys
sys.setExecutionLimit(1000000)
import turtle
import random
wn = turtle.Screen()
#Creates alex the turtle
alex = turtle.Turtle()
alex.color('blue')
alex.pensize(3)
alex.ht()
alex.penup()
#creates bob the turtle
bob = turtle.Turtle()
bob.color('blue')
bob.pensize(3)
bob.ht()
bob.penup()
#Sets variables so that alex starts in a random location
a=random.randrange(360)
b=random.randrange(360)
x=random.randrange(50,150)
y=random.randrange(50,150)
#Sets variables so that bob starts in a random location
l=random.randrange(360)
m=random.randrange(360)
n=random.randrange(50,150)
o=random.randrange(50,150)
#Moves alex to his random starting location
alex.speed(100)
alex.left(a)
alex.forward(x)
alex.left(b)
alex.forward(y)
alex.pendown()
#Moves bob to his random starting location
bob.speed(100)
bob.left(l)
bob.forward(n)
bob.left(m)
bob.forward(o)
bob.pendown()
#Draws the 2 squares
for i in range(1,500):
alex.forward(i)
alex.left(90)
bob.forward(i)
bob.left(90)
The functionality you want requires independent execution threads. You need to work with the multi-threading package and tutorial
You will want logic such as this:
import time
import threading
def draw_square():
# Draw a square in a random place
length = random.randrange(360)
width = random.randrange(360)
x_pos = random.randrange(50,150)
y_pos = random.randrange(50,150)
# Continue with your square-drawing logic;
# you already know how to do this.
while True:
threading.thread(draw_square)
time.sleep(10)

How to use multiprocessing or threading with tkinter

I am trying to make a simple space invaders game and a problem I have run into is getting things t happen at the same time. I have binded the shooting action to the canvas of the game so that when you click a function is called. I would like it so that this function can be called multiple times at once so that multiple "lasers/bullets" can be seen on the screen at any one time. At the minute when you click and a "laser/bullet" is already on screen, the previous one disappears and a new one appears. CODE:
class Game1():
def __init__(self, xcoord1=380, ycoord1=550, xcoord2=400, ycoord2=570):
self.Master = Master
self.Master.geometry("800x600+300+150")
Game1Canvas = Canvas(self.Master, bg="black", height=600, width=800)
Game1Canvas.place(x=0, y=0)
self.Canvas = Game1Canvas
self.Canvas.bind("<Button-1>", self.Shoot)
self.Ship = self.Canvas.create_rectangle(self.xcoord1, self.ycoord1, self.xcoord2, self.ycoord2, fill = "red")
def Shoot(self):
self.LaserLocation = 0
for self.LaserLocation in range(0 , 112):
Master.after(1, self.Canvas.create_rectangle(self.xcoord1, self.ycoord1 - (self.LaserLocation * 5), self.xcoord2 - 5, self.ycoord2 - (self.LaserLocation * 5), fill = "pink", tag=str(CurrentTag)))
Master.update()
self.Canvas.delete(str(CurrentTag))
This is a much more "dumbed" down version of the code at the minute because I've been trying a bunch of different ways to get this working and it's a mess. I am aware of the multiprocessing and threading imports and I have tried them both but am unable to get them working for my code. If someone could reply back with a solution I would be very grateful. Cheers.
You don't need to use multithreading or multiprocessing. You also don't need (nor want) to be drawing new rectangles every millisecond, or multiple times per millisecond.
The solution is to have your Shoot function merely create a single rectangle, and add it to a list. Then, using a simple animation mechanism, iterate over the list and move each bullet up one or two pixels. You do this by creating a function that calls itself every 20-30 ms.
The solution looks something like this:
def Shoot(self):
laser = self.Canvas.create_rectangle(...)
self.lasers.append(laser)
def do_animation(self):
# make a copy of the list of lasers to iterate
# over, so we can remove items from the original
# list when they go off screen
lasers = self.lasers[:]
for laser in lasers:
# get current coordinates of this laser
(x0,y0,x1,y1) = self.canvas.bbox(laser)
if x1 < self.canvas.winfo_height():
# if it is not off screen, move it up
self.canvas.move(laser, 0, -10)
else:
# if it IS off screen, delete it
self.canvas.delete(laser)
self.lasers.remove(laser)
self.after(30, self.do_animation)
The above will move the lasers every 30 milliseconds (about 33 fps).

Categories

Resources