How to add a countdown timer to my turtle program? - python

I'm trying to make a game using turtle where the player has to click as many randomly distributed circles in a set amount of time, but I'm stumped on how to create a timer inside the game. Whenever I try to set the timer, it counts down before the game starts or counts down after 1 circle spawns. I want to have it count down as the game starts, and have the game end once the timer ends.
I'm also trying to create a score counter, but am I using circle.onclick wrong?
This is what I've been trying to do:
from turtle import Turtle, Screen
from random import random, randint
import time
CURSOR_SIZE = 20
def score():
num=0
print("Score: ",num)
def my_circle(color):
radius = randint(10, 50)
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.showturtle()
circle.onclick(lambda x, y, t=circle: t.hideturtle())
circle.onclick(num=num+1)
return radius, circle
screen = Screen()
screen.bgcolor("lightgreen")
screen.title("Speed Clicker")
width, height = screen.window_width(), screen.window_height()
circles = []
for _ in range(0, 20):
for i in range(40):
print(str(40-i)+" seconds remain")
time.sleep(1)
rgb = (random(), random(), random())
circles.append(my_circle(rgb))
screen.mainloop()
The last thing I'm curious about is if it's possible to have the timer and score printed ON the game, rather than on the Python Shell.

I've made a few alterations and this seems to work. I have put the score and the time left in the title bar of the game. Thanks
import turtle
from random import random, randint
import time
CURSOR_SIZE = 20
num=0
def increase_score():
global num
num += 1
def my_circle(color):
radius = randint(10, 50)
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(), increase_score()))
return radius, circle
screen = turtle.Screen()
screen.bgcolor("lightgreen")
screen.title("Speed Clicker")
width, height = screen.window_width(), screen.window_height()
circles = []
gameLength = 5
# Higher number means faster blocks
# 1-10
difficulty = 10
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(num,int(round(gameLength - timeTaken,0))))
if time.time() - startTime > gameLength:
break
screen.title('FINISHED! FINAL SCORE: {}'.format(num))
screen.mainloop()

Related

How can i print random color circles in turtle?

the below code is one part of my code. I would like to print random colored circles. But it does not work. Can someone please help me to correct this code? circles shouldnt be overalapped!!
ERROR is bad color sequence: (164, 13, 120)
from random import randint
from svg_turtle import SvgTurtle
import turtle
from turtle import Turtle
import json
def fiber_circle(fiber, width, height):
fiber_r=25
fiber_num = 100
cursor_size = 20
fiber.hideturtle()
fiber.screen.bgcolor("white")
r = randint(0, 255)
g = randint(0, 255)
b = randint(0, 255)
fiber.color(r,g,b)
fiber.screen.colormode(255)
fiber.shape("circle")
fiber.shapesize(fiber_r / cursor_size)
fiber.speed("fastest")
fiber.penup()
fibers = []
for _ in range(fiber_num):
fiberr = fiber.clone()
fiberr.setposition(
randint(-width / 2, width / 2),
randint(-height / 2, height / 2),
)
Your program, if it ran, would simply change the turtle cursor into different colors, but in one location. If you want to draw randomly colored circles all over your window, you can try something like the following which uses dot() to draw them:
from turtle import Screen, Turtle
from random import randint
FIBER_RADIUS = 25
FIBER_NUMBER = 100
CURSOR_SIZE = 20
def fiber_circle(fiber):
r = randint(0, 255)
g = randint(0, 255)
b = randint(0, 255)
x = randint(FIBER_RADIUS - width//2, width//2 - FIBER_RADIUS)
y = randint(FIBER_RADIUS - height//2, height//2 - FIBER_RADIUS)
fiber.color(r, g, b)
fiber.goto(x, y)
fiber.dot(FIBER_RADIUS * 2) # dot() takes a diameter
screen = Screen()
screen.colormode(255)
width, height = screen.window_width(), screen.window_height()
fiber = Turtle()
fiber.hideturtle()
fiber.speed("fastest")
fiber.penup()
for _ in range(FIBER_NUMBER):
fiber_circle(fiber)
screen.exitonclick()
Alternatively, you can use your original approach of shaping, sizing and coloring the turtle itself, but then use stamp() to leave behind a circle while moving the turtle randomly around the screen:
...
def fiber_circle(fiber):
r = randint(0, 255)
g = randint(0, 255)
b = randint(0, 255)
x = randint(FIBER_RADIUS - width//2, width//2 - FIBER_RADIUS)
y = randint(FIBER_RADIUS - height//2, height//2 - FIBER_RADIUS)
fiber.color(r, g, b)
fiber.goto(x, y)
fiber.stamp()
screen = Screen()
screen.colormode(255)
width, height = screen.window_width(), screen.window_height()
fiber = Turtle()
fiber.hideturtle()
fiber.shape('circle')
fiber.shapesize(FIBER_RADIUS * 2 / CURSOR_SIZE) # CURSOR_SIZE is effectively a diameter
fiber.speed('fastest')
fiber.penup()
for _ in range(FIBER_NUMBER):
fiber_circle(fiber)
...

In the turtle module, how to find the maximum values of coordinates?

import turtle as t
from random import randint, random
def draw_star(points, size, col, x, y):
t.penup()
t.goto(x, y)
t.pendown()
angle = 180 - (180 / points)
t.color(col)
t.begin_fill()
for i in range(points):
t.forward(size)
t.right(angle)
t.end_fill()
# Main code
while True:
ranPts = randint(2, 5) * 2 + 1
ranSize = randint(10, 50)
ranCol = (random(), random(), random())
ranX = randint(-350, 300)
ranY = randint(-250, 250)
draw_star(ranPts, ranSize, ranCol, ranX, ranY)
Question:
How could I know the maximum values of coordinates of my screen? So I can have a better idea on how to set the values of ranX and ranY?
Thanks.
You could use t.setworldcoordinates(llx, lly, urx, ury)
The parameters:
llx = x of lower left corner
lly = y of lower left corner
urx= x of upper right corner
ury = y of upper right corner
You can create a function and find the values of coordinates yourself by clicking on the screen like this:
# turtle library
import turtle
#This to make turtle object
tess=turtle.Turtle()
# self defined function to print coordinate
def buttonclick(x,y):
print("You clicked at this coordinate({0},{1})".format(x,y))
#onscreen function to send coordinate
turtle.onscreenclick(buttonclick,1)
turtle.listen() # listen to incoming connections
turtle.speed(10) # set the speed
turtle.done() # hold the screen
This will print everytime you click on the screen and print the coordinates out.
The screensize() function returns the canvas width and the canvas height as a tuple.
You can use this to find the max coordinates of the canvas.
screenSize = t.screensize() #returns (width, height)
# Main code
while True:
ranPts = randint(2, 5) * 2 + 1
ranSize = randint(10, 50)
ranCol = (random(), random(), random())
ranX = randint(50-screenSize[0], screenSize[0] - 100)
ranY = randint(50-screenSize[1], screenSize[1] - 100)
draw_star(ranPts, ranSize, ranCol, ranX, ranY)
I found out this is what I need: t.window_width() and t.window_height().

My text in my clock python is not aligning properly

My text in my turtle module is not aligning properly, it is aligned up and to the left. I want it to align exactly where the turtle is. Can anyone help? I tried setting the xcor and ycor of the turtle up and to the left by 5 units and that did not work. Any help would be greatly appreciated.
Code:
import time
from datetime import datetime,date
import turtle
t = turtle.Pen()
while True:
turtle.tracer(0, 0)
hour_hand = float(datetime.today().hour)
minute_hand = float(datetime.today().minute)
second_hand = float(datetime.today().second)
# Draw circle
t.hideturtle()
t.circle(150)
t.left(90)
t.up()
t.forward(150)
t.down()
# Draw hands
t.right(float(float(minute_hand) * 6))
t.forward(100)
t.backward(100)
t.left(float(float(minute_hand) * 6))
t.right(int(float(hour_hand) * 30 + float(minute_hand) / 60 * 30))
t.forward(50)
t.backward(50)
t.left(int(float(hour_hand) * 30 + float(minute_hand) / 60 * 30))
t.right(second_hand * 6)
t.forward(125)
t.backward(125)
t.left(second_hand * 6)
# Draw ticks
for x in range(0, 12):
t.up()
t.forward(130)
t.down()
t.forward(20)
t.backward(20)
t.up()
t.backward(130)
t.down()
t.right(30)
for y in range(0, 60):
t.up()
t.forward(140)
t.down()
t.forward(10)
t.backward(10)
t.up()
t.backward(140)
t.down()
t.right(6)
t.up()
# Draw numbers
t.right(32.5)
for z in range(1, 12):
t.forward(130)
t.sety(t.ycor() - 5)
t.setx(t.xcor() - 5)
t.write(z, align = 'center', font = ('Times New Roman', 16))
t.sety(t.ycor() + 5)
t.setx(t.xcor() + 5)
t.backward(130)
t.right(30)
t.forward(130)
t.write(12, align = 'center', font = ('Times New Roman', 16))
turtle.update()
t.hideturtle()
time.sleep(0.85)
t.reset()
I don't really want to use tkinter, it is too complicated.
A simpler, though potentially less accurate, way to do this completely within turtle:
FONT_SIZE = 16
FONT = ('Times New Roman', FONT_SIZE)
t.color('red')
t.dot(2) # show target of where we want to center text, for debugging
t.color('black')
t.sety(t.ycor() - FONT_SIZE/2)
t.write(12, align='center', font=FONT)
Now let's address your program as a whole. The primary issues I see is that it flickers and is more complicated than necessary. The first thing to do is to switch turtle into Logo mode, which makes positive angles clockwise and makes 0 degrees at the top (not unlike a clock!).
Then we split the dial drawing onto it's own turtle to be drawn once an we put the hands on their own turtle to be erased and redraw over and over. We all toss the while True: and sleep(), which have no place in an event-driven world like turtle, and use a turtle timer event instead:
from datetime import datetime
from turtle import Screen, Turtle
OUTER_RADIUS = 150
LARGE_TICK = 20
SMALL_TICK = 10
FONT_SIZE = 16
FONT = ('Times New Roman', FONT_SIZE)
def draw_dial():
dial = Turtle()
dial.hideturtle()
dial.dot()
dial.up()
dial.forward(OUTER_RADIUS)
dial.right(90)
dial.down()
dial.circle(-OUTER_RADIUS)
dial.up()
dial.left(90)
dial.backward(OUTER_RADIUS)
for mark in range(60):
distance = LARGE_TICK if mark % 5 == 0 else SMALL_TICK
dial.forward(OUTER_RADIUS)
dial.down()
dial.backward(distance)
dial.up()
dial.backward(OUTER_RADIUS - distance)
dial.right(6)
dial.sety(-FONT_SIZE/2)
dial.setheading(30) # starting at 1 o'clock
for z in range(1, 13):
dial.forward(OUTER_RADIUS - (LARGE_TICK + FONT_SIZE/2))
dial.write(z, align='center', font=FONT)
dial.backward(OUTER_RADIUS - (LARGE_TICK + FONT_SIZE/2))
dial.right(30)
def tick():
hour_hand = datetime.today().hour
minute_hand = datetime.today().minute
second_hand = datetime.today().second
hands.reset()
hands.hideturtle() # redo as undone by reset()
hands.right(hour_hand * 30 + minute_hand / 60 * 30)
hands.forward(1/3 * OUTER_RADIUS)
hands.backward(1/3 * OUTER_RADIUS)
hands.left(hour_hand * 30 + minute_hand / 60 * 30)
hands.right(minute_hand * 6)
hands.forward(2/3 * OUTER_RADIUS)
hands.backward(2/3 * OUTER_RADIUS)
hands.left(minute_hand * 6)
hands.right(second_hand * 6)
hands.forward(OUTER_RADIUS - (LARGE_TICK + FONT_SIZE))
hands.backward(OUTER_RADIUS - (LARGE_TICK + FONT_SIZE))
hands.left(second_hand * 6)
screen.update()
screen.ontimer(tick, 1000)
screen = Screen()
screen.mode('logo') # make 0 degrees straight up, positive angles clockwise (like a clock!)
screen.tracer(False)
draw_dial()
hands = Turtle()
tick()
screen.mainloop()
I don't really want to use tkinter, it is too complicated.
I don't think it's too complicated and it is the most straight-forward solution to center the text vertically.
Use the following code to get the height of text in your font:
from tkinter import font
t = turtle.Pen()
font_config = font.Font(font=('Times New Roman', 16))
font_height = font_config.metrics("linespace")
Then draw your text using font_height/2 as the vertical offset:
t.sety(t.ycor() - font_height/2)
t.write(z, align="center", font=font_config)
t.sety(t.ycor() + font_height/2)

How can I change the size of my python turtle window?

I am currently trying to draw a Mandelbrot set in python with turtle. However, my problem has nothing to do with the Mandelbrot. I can't change the size of my turtle window. How can I do that?
I tried to initialize a screen and set the screen size with the screensize method. Nothing changes if I do this.
This is my code for drawing the set. I pasted the whole code because I don't know what I did wrong that the screen size doesn't change.
from turtle import *
height = 360
width = 360
screen = Screen()
screen.screensize(width, height)
tu = Turtle()
tu.hideturtle()
tu.speed(0)
tu.penup()
def decreasePoint(n, start1, stop1, start2, stop2):
return ((n - start1) / (stop1 - start1)) * (stop2 - start2) + start2
for x in range(width):
for y in range(height):
a = decreasePoint(x, 0, width, -2, 2)
b = decreasePoint(y, 0, height, -2, 2)
ca = a
cb = b
n = 0
z = 0
while n < 100:
aa = a * a - b * b
bb = 2 * a * b
a = aa + ca
b = bb + cb
n += 1
if abs(a + b) > 16:
break
bright = 'pink'
if (n == 100):
bright = 'black'
tu.goto(x , y)
tu.pendown()
tu.dot(4, bright)
tu.penup()
done()
Instead of:
screen.screensize(width, height)
do:
screen.setup(width, height)
The screensize() method sets the amount of area the turtle can roam, but doesn't change the screen size (despite the name), just the scrollable area. Also, to simplify your code, speed it up and produce a more detailed result, I suggest the following rework:
from turtle import Screen, Turtle
WIDTH, HEIGHT = 360, 360
screen = Screen()
screen.setup(WIDTH + 4, HEIGHT + 8) # fudge factors due to window borders & title bar
screen.setworldcoordinates(0, 0, WIDTH, HEIGHT)
turtle = Turtle()
turtle.hideturtle()
turtle.penup()
def scalePoint(n, start1, stop1, start2, stop2):
return (n - start1) * (stop2 - start2) / (stop1 - start1) + start2
screen.tracer(False)
for x in range(WIDTH):
real = scalePoint(x, 0, WIDTH, -2, 2)
for y in range(HEIGHT):
imaginary = scalePoint(y, 0, HEIGHT, -2, 2)
c = complex(real, imaginary)
z = 0j
color = 'pink'
for _ in range(100):
if abs(z) >= 16.0:
break
z = z * z + c
else:
color = 'black'
turtle.goto(x, y)
turtle.dot(1, color)
screen.update()
screen.tracer(True)
screen.exitonclick()

python canvas.find_overlapping appears to have inverted y-axis

I am trying to find the color of the canvas under a Python turtle. I use canvas.find_overlapping but it is only successful when I negate the ycor, implying that the y-axis is inverted in the canvas object, compared to what is shown. Is there a problem with my code or is the y-axis inverted?
import turtle
wn = turtle.Screen()
maze_drawer = turtle.Turtle()
maze_drawer.color("purple")
maze_drawer.speed("fastest")
path_width = 15
def get_pixel_color(x, y):
c = turtle.Screen().getcanvas()
# -y should not work??
items = c.find_overlapping(x, -y, x, -y)
if len(items) > 0:
return c.itemcget(items[0], "fill") # get 0 object (canvas)
# draw simplified maze
wall_len = 0
for i in range(10):
maze_drawer.left(90)
wall_len += path_width
maze_drawer.forward(wall_len)
# navigate maze from center
maze_runner = turtle.Turtle()
maze_runner.color("green")
maze_runner.penup()
maze_runner.goto(-path_width, -path_width)
# test in y dir: maze_runner.setheading(90)
clear = True
while(clear):
maze_runner.forward(1)
color_at_turtle = get_pixel_color(maze_runner.xcor(), maze_runner.ycor())
if (color_at_turtle == "purple"):
clear = False
wn.exitonclick()
Neat use of tkinter pixel detection within turtle! If the inverted Y coordinate is bothersome, you can flip it from turtle's perspective:
from turtle import Screen, Turtle
screen = Screen()
width, height = screen.window_width() / 2, screen.window_height() / 2
screen.setworldcoordinates(-width, height, width, -height) # flip Y coordinate
Then your code doesn't have to think about negating Y as long as you know you're drawing upside down:
from turtle import Screen, Turtle
PATH_WIDTH = 15
def get_pixel_color(x, y):
canvas = screen.getcanvas()
items = canvas.find_overlapping(x, y, x, y)
if items:
return canvas.itemcget(items[0], "fill") # get 0 object (canvas)
return None
screen = Screen()
width, height = screen.window_width() / 2, screen.window_height() / 2
screen.setworldcoordinates(-width, height, width, -height)
maze_drawer = Turtle(visible=False)
maze_drawer.color("purple")
maze_drawer.speed("fastest")
# draw simplified maze
wall_len = 0
for _ in range(20):
maze_drawer.left(90)
wall_len += PATH_WIDTH
maze_drawer.forward(wall_len)
# navigate maze from center
maze_runner = Turtle()
maze_runner.color("dark green", "green")
maze_runner.penup()
maze_runner.goto(-PATH_WIDTH, -PATH_WIDTH)
def run_maze():
maze_runner.forward(1)
x, y = maze_runner.position()
color_at_turtle = get_pixel_color(x, y)
if color_at_turtle == "purple":
maze_runner.backward(PATH_WIDTH - 1)
maze_runner.left(90)
x, y = maze_runner.position()
if -width < x < width and -height < y < height:
screen.ontimer(run_maze, 10)
run_maze()
screen.exitonclick()

Categories