I have been developing a fairly simple turtle program which essentially just draws snowflakes of a random size, shape, and colour. The amount is based on user input. What I would like to do with the program is add an input section at the end that asks the user whether they would like to end the program or restart it. If they choose restart, the program should start over again from the beginning. Otherwise it should follow the exit procedure I made for it. I have seen methods including WHILE Loops and other similar concepts, I just can't figure out how to apply it to my code. Is there a simple way to do this?
Here is the code:
import time
import sys
import turtle
import random
n = int(input("How many snowflakes do you want?: "))
screen = turtle.Screen()
screen.bgcolor("black")
speedy = turtle.Turtle()
speedy.speed(0)
sfcolor = ["yellow","gold","orange","red","violet","magenta","purple","navy","blue","skyblue","cyan","turquoise","lightgreen","green","darkgreen","white"]
def snowflake(size):
speedy.penup()
speedy.forward(10 * size)
speedy.left(45)
speedy.pendown()
speedy.color(random.choice(sfcolor))
for i in range(8):
branch(size)
speedy.left(45)
def branch(size):
for i in range(3):
for i in range(3):
speedy.forward(10.0 * size / 3)
speedy.back(10.0 * size / 3)
speedy.right(45)
speedy.left(90)
speedy.back(10.0 * size / 3)
speedy.left(45)
speedy.right(90)
speedy.forward(10.0 * size)
for i in range(n):
x = random.randint(-200, 200)
y = random.randint(-200, 200)
sfsize = random.randint(1, 4)
speedy.penup()
speedy.goto(x, y)
speedy.pendown()
snowflake(sfsize)
print("The turtle window will close in 10 seconds. Thanks for using the program!")
print("Goodbye!")
time.sleep(10)
turtle.Screen().bye()
sys.exit()
Solved! Thanks to the advice from the user https://stackoverflow.com/users/6243352/ggorlen
This is my complete code now that it works.
import time
import sys
import turtle
import random
restart = True
while restart == True:
n = int(input("How many snowflakes do you want?: "))
screen = turtle.Screen()
screen.bgcolor("black")
speedy = turtle.Turtle()
speedy.speed(0)
sfcolor = ["yellow","gold","orange","red","violet","magenta","purple","navy","blue","skyblue","cyan","turquoise","lightgreen","green","darkgreen","white"]
def snowflake(size):
speedy.penup()
speedy.forward(10 * size)
speedy.left(45)
speedy.pendown()
speedy.color(random.choice(sfcolor))
for i in range(8):
branch(size)
speedy.left(45)
def branch(size):
for i in range(3):
for i in range(3):
speedy.forward(10.0 * size / 3)
speedy.back(10.0 * size / 3)
speedy.right(45)
speedy.left(90)
speedy.back(10.0 * size / 3)
speedy.left(45)
speedy.right(90)
speedy.forward(10.0 * size)
for i in range(n):
x = random.randint(-200, 200)
y = random.randint(-200, 200)
sfsize = random.randint(1, 4)
speedy.penup()
speedy.goto(x, y)
speedy.pendown()
snowflake(sfsize)
print("The turtle window will close in 10 seconds. Thanks for using the program!")
time.sleep(10)
turtle.Screen().bye()
restart = False
restart = input("Do you want to restart the program? Yes or No: ")
restart = restart.upper()
if restart == "YES":
restart = True
print("Restarting...")
elif restart == "NO":
restart = False
print("Thank you for using the program. Goodbye!")
time.sleep(10)
turtle.Screen().bye()
sys.exit()
else:
print("Error.")
Related
So, I have written a code that creates snowflakes using turtle. Essentially it asks the user how many snowflakes to generate. It then opens a turtle window and draws the snowflakes in a random place, size and colour. The random place is important for this question. Essentially, when it draws the snowflakes, is there a way to stop the snowflakes from being drawn in the (approx.) same area so that they don't overlap?
Normally yes, this would be simple but due to its random nature, I have no clue how to do this.
Here is the code:
import time
import sys
import turtle
import random
restart = True
print("This program creates snowflakes. Enjoy!")
while restart == True:
n = int(input("How many snowflakes do you want?: "))
screen = turtle.Screen()
screen.bgcolor("black")
speedy = turtle.Turtle()
speedy.speed(0)
sfcolor = ["yellow","gold","orange","red","violet","magenta","purple","navy","blue","skyblue","cyan","turquoise","lightgreen","green","darkgreen","white","BlueViolet","DeepSkyBlue","snow2","ForestGreen", "gainsboro", "GhostWhite", "goldenrod"]
def snowflake(size):
speedy.penup()
speedy.forward(10 * size)
speedy.left(45)
speedy.pendown()
speedy.color(random.choice(sfcolor))
for i in range(8):
branch(size)
speedy.left(45)
def branch(size):
for i in range(3):
for i in range(3):
speedy.forward(10.0 * size / 3)
speedy.back(10.0 * size / 3)
speedy.right(45)
speedy.left(90)
speedy.back(10.0 * size / 3)
speedy.left(45)
speedy.right(90)
speedy.forward(10.0 * size)
for i in range(n):
x = random.randint(-375, 375)
y = random.randint(-375, 375)
sfsize = random.randint(1, 4)
speedy.penup()
speedy.goto(x, y)
speedy.pendown()
snowflake(sfsize)
print(i+1," Snowflake(s)")
restart = False
print("Thanks for using the program! You will have the option to resart it shortly.")
time.sleep(3)
restart = input("Do you want to run the program again? Yes or No: ")
restart = restart.upper()
if restart == "YES":
turtle.Screen().bye()
restart = True
print("Restarting...")
elif restart == "NO":
restart = False
print("Thank you for using the program. Goodbye!")
time.sleep(3)
turtle.Screen().bye()
sys.exit()
else:
print("\nError. Program Resetting...")
turtle.Screen().bye()
print("This program creates snowflakes. Enjoy!")
restart = True
Similar to #mx0's suggestion (+1), rather than a square, we define a circle that encompasses the snowflake and for each successful placement, keep a list of existing positions and radii. We also use the radius to avoid drawing partial snowflakes near the edge of our window:
from turtle import Screen, Turtle
from random import randint, choice
WIDTH, HEIGHT = 480, 320 # small for testing
SF_COLORS = [
'yellow', 'gold', 'orange', 'red', 'violet',
'magenta', 'purple', 'navy', 'blue', 'skyblue',
'cyan', 'turquoise', 'lightgreen', 'green', 'darkgreen',
'white', 'BlueViolet', 'DeepSkyBlue', 'snow2', 'ForestGreen',
'gainsboro', 'GhostWhite', 'goldenrod',
]
def snowflake(size):
radius = 15 * size # circle roughly encompassing snowflake
position = randint(radius - WIDTH/2, WIDTH/2 - radius), randint(radius - HEIGHT/2, HEIGHT/2 - radius)
speedy.goto(position)
trys = 0
while any(speedy.distance(other_position) < (radius + other_radius) for other_position, other_radius in snowflakes):
position = randint(radius - WIDTH/2, WIDTH/2 - radius), randint(radius - HEIGHT/2, HEIGHT/2 - radius)
speedy.goto(position)
trys += 1
if trys > 100:
return False # can't fit this snowflake, signal caller to try a different `size`
snowflakes.append((position, radius))
speedy.color(choice(SF_COLORS))
speedy.penup()
speedy.forward(10 * size)
speedy.left(45)
speedy.pendown()
for _ in range(8):
branch(size)
speedy.left(45)
speedy.penup()
return True
def branch(size):
length = 10.0 * size / 3
for _ in range(3):
for _ in range(3):
speedy.forward(length)
speedy.backward(length)
speedy.right(45)
speedy.left(90)
speedy.backward(length)
speedy.left(45)
speedy.right(90)
speedy.forward(length * 3)
print("This program creates snowflakes. Enjoy!")
n = int(input("How many snowflakes do you want?: "))
screen = Screen()
screen.setup(WIDTH, HEIGHT)
screen.bgcolor('black')
speedy = Turtle()
speedy.speed('fastest')
snowflakes = []
flakes = 0
while flakes < n:
sfsize = randint(1, 4)
if snowflake(sfsize):
flakes += 1
speedy.hideturtle()
screen.exitonclick()
However, fitting snowflakes like this creates an issue. The user might request more snowflakes than can fit in a given size window. The code above partially addresses this by returning failure and letting the caller figure out what to do. Here, we simply try another snowflake size. Smarter code would reduce the random size range based on failure, and quit trying altogether when a size 1 snowflake fails!
I've removed the restart logic to simplify my example and because I'm not convinced it works.
So I decided to model a differential equation in python turtle, but if the turtle is moving fast it starts to "cut", with the circle repeatedly being cutoff at points
I tried to make it smoother by using tracer(0, 0) and updating it only when I need to but that still doesn't work
from turtle import *
import time
from threading import Thread
import math
setup(500, 500)
bgcolor("black")
b = Turtle()
s = b.getscreen()
tracer(0, 0)
b.color("sky blue")
b.shape("circle")
b.up()
b.goto(0, -100)
s.update()
globals()['lastt'] = 0
globals()['auto'] = 0
globals()['angle'] = 0
globals()['speed'] = 0
globals()['accel'] = 0
globals()['air'] = .5
#Starting speed
def measurespeed():
while True:
time.sleep(.01)
while globals()['auto'] == 0:
past = globals()['angle']
time.sleep(.01)
globals()['speed'] = (angle - past)/.01
dspeed = Thread(target=measurespeed)
dspeed.start()
#Auto
def equation():
while True:
time.sleep(.01)
while globals()['auto'] == 1:
globals()['accel'] = -5 * math.sin(angle) - (air * speed)
globals()['speed'] += accel * .01
globals()['angle'] += speed * .01
time.sleep(.01)
b.goto(100 * math.cos(angle - (math.pi / 2)), 100 * math.sin(angle - (math.pi / 2)))
s.update()
move = Thread(target=equation)
move.start()
#Dragging
def drag(x, y):
if x == 0:
if y >= 0:
b.goto(0, 100)
else:
b.goto(0, -100)
elif x > 0:
globals()['angle'] = math.atan(y/x) + (math.pi / 2)
else:
globals()['angle'] = math.atan(y/x) + (3 * math.pi / 2)
b.goto(100 * math.cos(angle - (math.pi / 2)), 100 * math.sin(angle - (math.pi / 2)))
s.update()
def static(x, y):
globals()['auto'] = 0
globals()['speed'] = 0
def resume(x, y):
globals()['auto'] = 1
b.ondrag(drag)
b.onclick(static)
b.onrelease(resume)
s.mainloop()
Sorry for the lack of comments, this was supposed to be a quick experiment
I took a crack at this: I turned off the drag handler while inside the drag handler as this can cause problems including faux recursion stack overflow.
Other changes not directly affecting function include using standard Python global notation and other style stuff like parentheses reduction:
from turtle import Screen, Turtle
from threading import Thread
from time import sleep
import math
AIR = 0.5
auto = False
angle = 0
speed = 0
accel = 0
# Starting speed
def measurespeed():
global speed
while True:
sleep(0.01)
while not auto:
past = angle
sleep(0.01)
speed = (angle - past) / 0.01
# Auto
def equation():
global accel, angle, speed
while True:
sleep(0.01)
while auto:
accel = -5 * math.sin(angle) - AIR * speed
speed += accel * 0.01
angle += speed * 0.01
sleep(0.01)
turtle.goto(100 * math.cos(angle - math.pi/2), 100 * math.sin(angle - math.pi/2))
screen.update()
# Dragging
def drag(x, y):
global angle
turtle.ondrag(None) # disable handler inside handler
if x == 0:
if y >= 0:
turtle.goto(0, 100)
else:
turtle.goto(0, -100)
elif x > 0:
angle = math.atan(y / x) + math.pi/2
else:
angle = math.atan(y / x) + 3 * math.pi/2
turtle.goto(100 * math.cos(angle - math.pi/2), 100 * math.sin(angle - math.pi/2))
screen.update()
turtle.ondrag(drag) # reenable handler
def static(x, y):
global auto, speed
auto = False
speed = 0
def resume(x, y):
global auto
auto = True
screen = Screen()
screen.setup(500, 500)
screen.bgcolor('black')
screen.tracer(False)
turtle = Turtle()
turtle.shape('circle')
turtle.color("sky blue")
turtle.penup()
turtle.sety(-100)
screen.update()
dspeed = Thread(target=measurespeed)
dspeed.start()
move = Thread(target=equation)
move.start()
turtle.ondrag(drag)
turtle.onclick(static)
turtle.onrelease(resume)
screen.mainloop()
Another issue to consider is that traditionally you could only do turtle graphic operations like goto() from the main thread. However, this seems to work better in the latest Python and latest tkinter. If you're still having issues, make sure to upgrade to current software. If that doesn't help, search for some of the turtle and threading questions on SO.
In the context of a drag, the click and release events appear to work fine, but on their own they don't work the way you'd expect -- both are invoked on each down and each up of the button.
I'm trying to make a game where you click a bunch of random circles and get a score, however I also want it to deduct from your score when you miss the circle. I've been trying to use screen.onclick() but instead of deducting the score on a misclick it seems to deduct the score every second for no reason. What am I doing wrong?
import turtle
from random import random, randint
import time
CURSOR_SIZE = 20
score=0
def addscore():
global score
score += 1
def deletescore():
global score
score -= 1
def my_circle(color):
radius = (15)
circle = turtle.Turtle('circle', visible=False)
circle.shapesize(radius / CURSOR_SIZE)
circle.color(color)
circle.penup()
while True:
nx = randint(2 * radius - width // 2, width // 2 - radius * 2)
ny = randint(2 * radius - height // 2, height // 2 - radius * 2)
circle.goto(nx, ny)
for other_radius, other_circle in circles:
if circle.distance(other_circle) < 2 * max(radius, other_radius):
break
else:
break
circle.showturtle()
circle.onclick(lambda x,y,t=circle: (circle.hideturtle(), addscore()))
screen.onclick(deletescore())
return radius, circle
username=str(input("Set your username: "))
screen = turtle.Screen()
screen.bgcolor("lightgreen")
screen.title("Speed Clicker")
width, height = screen.window_width(), screen.window_height()
circles = []
gameLength = 30
difficulty = 20
startTime = time.time()
while True:
time.sleep(1/difficulty)
rgb = (random(), random(), random())
timeTaken = time.time() - startTime
circles.append(my_circle(rgb))
screen.title('SCORE: {}, TIME LEFT: {}'.format(score,int(round(gameLength - timeTaken,0))))
if time.time() - startTime > gameLength:
break
screen.title('GG! FINAL SCORE: {}'.format(score))
screen.mainloop()
The problem is this line:
screen.onclick(deletescore())
It's in the wrong place (only needs to be called once, not in a loop) and the argument is incorrect, it should be passing the function not calling it:
screen.onclick(deletescore)
The fix is multifold: first, move the modified statement to just before your while True: statement. Then fix the definition of deletescore() to take x and y arguments that we won't use but are necessary to be a click handler. (Or wrap it in a lambda like the call to addscore())
However, a click on a turtle can also be passed through as a click on the screen. To counter this, we can add 2 in addscore() instead of 1 since deletescore() will get called as well. That should be enough to get things working.
However, we really should eliminate the while True: loop and sleep() call which have no place in an event-driven program. Instead we use ontimer() to keep things moving and not potentially block other events. The reformulated code would be more like:
from turtle import Turtle, Screen
from random import random, randint
from time import time
CURSOR_SIZE = 20
def addscore():
global score
score += 2 # add 2 as this will also count as a -1 screen click!
def deletescore():
global score
score -= 1
def my_circle(color):
circle = Turtle('circle', visible=False)
circle.shapesize(radius / CURSOR_SIZE)
circle.color(color)
circle.penup()
while True:
nx = randint(2 * radius - width // 2, width // 2 - radius * 2)
ny = randint(2 * radius - height // 2, height // 2 - radius * 2)
circle.goto(nx, ny)
for other_radius, other_circle in circles:
if circle.distance(other_circle) < 2 * max(radius, other_radius):
break
else:
break
circle.onclick(lambda x, y: (circle.hideturtle(), addscore()))
circle.showturtle()
return radius, circle
def play():
rgb = (random(), random(), random())
timeTaken = time() - startTime
circles.append(my_circle(rgb))
screen.title('SCORE: {}, TIME LEFT: {}'.format(score, int(round(gameLength - timeTaken, 0))))
if time() - startTime > gameLength:
screen.title('FINAL SCORE: {}'.format(score))
screen.onclick(None)
screen.clear()
else:
screen.ontimer(play, 1000 // difficulty)
screen = Screen()
screen.bgcolor("lightgreen")
screen.title("Speed Clicker")
width, height = screen.window_width(), screen.window_height()
score = 0
circles = []
radius = 15
difficulty = 20
gameLength = 30
screen.onclick(lambda x, y: deletescore())
startTime = time()
play()
screen.mainloop()
When clicking within the random squares, it says "You clicked!" only when it draws the next square, not when you initially click.
import turtle
import random
import time
t1 = turtle.Turtle()
t2 = turtle.Turtle()
s = turtle.Screen()
def turtle_set_up():
t1.hideturtle()
t2.hideturtle()
t2.penup()
s.tracer(1, 0)
def draw():
for i in range(2):
t1.fd(400)
t1.lt(90)
t1.fd(400)
t1.lt(90)
def drawbox():
t2.pendown()
for i in range(2):
t2.fd(50)
t2.lt(90)
t2.fd(50)
t2.lt(90)
t2.penup()
def box1():
X = random.randint(0, 350)
Y = random.randint(0, 350)
Xleft = X
Xright = X + 50
Ytop = Y + 50
Ybottom = Y
t2.goto(X, Y)
drawbox()
Here is where it checks if your mouse click is within the box drawn. If it is, it erases the old box for a new one to be drawn every 3 seconds.
I don't understand why it waits to say "You clicked!" until the 3 seconds are up for the loop instead of saying it when you initially click.
I cant place a break command after the print statement that prints "You clicked!" because it's outside the loop.
between here:
t_end = time.time() + 60 * 0.05
while time.time() < t_end:
def get_mouse_click_coor(x, y):
X_click = x
Y_click = y
if Xright > X_click > Xleft and Ytop > Y_click > Ybottom:
print('You clicked!')
s.onclick(get_mouse_click_coor)
t2.clear()
and here:
def starter():
turtle_set_up()
draw()
while 1:
box1()
starter()
Applications with interactive GUI (Graphical User Interface) are event-based, which means that they perform their actions when some events happen. For such applications, if you create a waiting loop for a given amount of time (as does your code) the whole application will be blocked for this amount of time. That's why the print is only executed after the 3s delay.
All GUI libraries include a scheme to activate some timer events. For the turtle API, there is a on_timer(func, delay) method that calls a function func after some delay (expressed in milliseconds). The idea is to repeatedly call your drawbox function every 3000ms. So, your code will be based on two main callback functions: get_mouse_click called on click events, and drawbox called on timer events. Here is the modified code, I propose:
import turtle
import random
t1 = turtle.Turtle()
t2 = turtle.Turtle()
s = turtle.Screen()
def turtle_set_up():
t1.hideturtle()
t2.hideturtle()
s.tracer(1, 0)
s.onclick(get_mouse_click) # define the 'click' event callback
def draw():
for i in range(2):
t1.fd(400)
t1.lt(90)
t1.fd(400)
t1.lt(90)
def drawbox():
global box # store box coordinates as global variable
X = random.randint(0, 350) # so that they are available for testing
Y = random.randint(0, 350) # in the mouse click callback
box = (X, Y, X+50, Y+50)
t2.clear() # clear previous box before drawing new one
t2.penup()
t2.goto(X, Y)
t2.pendown()
for i in range(2):
t2.fd(50)
t2.lt(90)
t2.fd(50)
t2.lt(90)
s.ontimer(drawbox, 3000) # define timer event callback
def get_mouse_click(x, y):
if box[0] <= x <= box[2] and box[1] <= y <= box[3]:
print('You clicked!')
def starter():
turtle_set_up()
draw()
drawbox()
starter()
Computers have to run in sequence so it can only process one line at a time, so, unless i'm mistaken, your program gets 'caught' on the timer then runs through the program and back to the start.
you could develop a while loop with a nested if statement that gets datetime from the device, if turtles have datetime
I believe you can simplify this problem. Primarily by making a turtle be the inner box rather than drawing the inner box. This simplifies drawing, erasing and event handling. Avoid invoking the tracer() method until you have a working program as it only complicates debugging. We can also stamp rather than draw the border.
If we simply want to be able to click on the inner box and have it randomly move to a new location:
from turtle import Turtle, Screen
from random import randint
BORDER_SIZE = 400
BOX_SIZE = 50
CURSOR_SIZE = 20
def move_box():
x = randint(BOX_SIZE/2 - BORDER_SIZE/2, BORDER_SIZE/2 - BOX_SIZE/2)
y = randint(BOX_SIZE/2 - BORDER_SIZE/2, BORDER_SIZE/2 - BOX_SIZE/2)
turtle.goto(x, y)
def on_mouse_click(x, y):
print("You clicked!")
move_box()
screen = Screen()
turtle = Turtle('square', visible=False)
turtle.shapesize(BORDER_SIZE / CURSOR_SIZE)
turtle.color('black', 'white')
turtle.stamp()
turtle.shapesize(BOX_SIZE / CURSOR_SIZE)
turtle.onclick(on_mouse_click)
turtle.penup()
turtle.showturtle()
move_box()
screen.mainloop()
If we want to make the program more game-like and require the user to click on the inner box within 3 seconds of each move, or lose the game, then we can introduce the ontimer() event as #sciroccorics suggests:
from turtle import Turtle, Screen
from random import randint
BORDER_SIZE = 400
BOX_SIZE = 50
CURSOR_SIZE = 20
CLICK_TIMEOUT = 3000 # milliseconds
def move_box():
x = randint(BOX_SIZE/2 - BORDER_SIZE/2, BORDER_SIZE/2 - BOX_SIZE/2)
y = randint(BOX_SIZE/2 - BORDER_SIZE/2, BORDER_SIZE/2 - BOX_SIZE/2)
turtle.goto(x, y)
screen.ontimer(non_mouse_click, CLICK_TIMEOUT)
def on_mouse_click(x, y):
global semaphore
print("You clicked!")
semaphore += 1
move_box()
def non_mouse_click():
global semaphore
semaphore -= 1
if semaphore < 1:
turtle.onclick(None)
turtle.color('black')
print("Game Over!")
screen = Screen()
semaphore = 1
turtle = Turtle('square', visible=False)
turtle.shapesize(BORDER_SIZE / CURSOR_SIZE)
turtle.color('black', 'white')
turtle.stamp()
turtle.shapesize(BOX_SIZE / CURSOR_SIZE)
turtle.onclick(on_mouse_click)
turtle.penup()
turtle.showturtle()
move_box()
screen.mainloop()
I am trying to setup a program in which the user decides how many turtles to generate and then they have a race after. My current solution is to just get an int input from the user and execute the code below (the code keeps repeating with larger numbers). I have tried putting in a loop but I am running into troubles since they all need to preform random movements in the end. Any help?
if turtNum >= 1:
turt1 = Turtle()
turt1.color(turtColour[0])
turt1.shape('turtle')
turt1.penup()
turt1.goto(0, -10)
turt1.pendown()
if turtNum >= 2:
turt2Name = input('Enter a name for the second turtle: ')
turt2 = Turtle()
turt2.color(turtColour[1])
turt2.shape('turtle')
turt2.penup()
turt2.goto(0, -25)
turt2.pendown()
This is the code I tried but got this error "list indices must be integers or slices, not str"
turtName = []
maxLengthList = turtNum
while len(turtName) < maxLengthList:
name = input('Enter the names for the turtles: ')
turtName.append(name)
for i in turtName:
turtName[i] = Turtle()
turtName[i].color(turtColour[0])
turtName[i].shape('turtle')
turtName[i].penup()
turtName[i].goto(0, -10)
turtName[i].pendown()
You can't dangle the concept of turtle racing without expecting us to get excited about seeing it happen. Below is a rough implementation that addresses the issues you had about entering the number of turtles, entering individual turtle information, storing it all and random motion:
from turtle import Turtle, Screen
from itertools import cycle
from random import randrange
MAX_TURTLES = 20
LANE_WIDTH = 25
FONT_SIZE = 18
FONT = ("Arial", FONT_SIZE, "normal")
COLORS = cycle(['red', 'green', 'blue', 'cyan', 'black', 'yellow'])
FINISH_LINE = 350
START_LINE = -200
NAME_LINE = START_LINE - 150
DELAY = 100 # milliseconds
MAX_STEP = 10
turtles = dict()
def race():
for name, turtle in turtles.items(): # should shuffle turtles
turtle.forward(randrange(MAX_STEP + 1))
if turtle.xcor() > FINISH_LINE:
return # the race is over
screen.ontimer(race, DELAY)
magic_marker = Turtle(visible=False)
magic_marker.penup()
turtNum = 0
while not 1 <= turtNum <= MAX_TURTLES:
turtNum = int(input('Enter the number of turtles: '))
for i in range(turtNum):
name = input('Enter a name for the turtle #{}: '.format(i + 1))
turtle = Turtle(shape="turtle")
turtle.color(next(COLORS))
y_offset = LANE_WIDTH * i - LANE_WIDTH * turtNum // 2
magic_marker.color(turtle.pencolor())
magic_marker.goto(NAME_LINE, y_offset - FONT_SIZE / 2)
magic_marker.write(name, font=FONT)
turtle.penup()
turtle.goto(START_LINE, y_offset)
turtle.pendown()
turtles[name] = turtle
magic_marker.color('red')
magic_marker.goto(FINISH_LINE, -FINISH_LINE)
magic_marker.pendown()
magic_marker.goto(FINISH_LINE, FINISH_LINE)
magic_marker.penup()
screen = Screen()
screen.ontimer(race, DELAY)
screen.exitonclick()