print("Finished Loading!")
print("x's turn")
xturn = True
if xturn == True:
usl.onclick(x1)
usc.onclick(yo)
usr.onclick(x3)
csr.onclick(x4)
csl.onclick(x5)
cs.onclick(x6)
dsl.onclick(x7)
dsc.onclick(x8)
dsr.onclick(x9)
else:
print("o's Turn")
usl.onclick(o1)
I am creating A Tic-tac-toe game for a class and have the playing mechanics finished(When someone clicks a box makes an x/o) but I am now struggling with turns. The excerpt above is what I would mostly likely need to motify. I was wondering if anyone knew how you can use events in turtle graphics to change a varible or if anyone knows a good system for alternating turns that would be Great!
I took this question as a challenge to write as little turtle code as possible to implement a working tic-tac-toe interface. The code below alternates between X's & O's turns and won't let a square be reused. It doesn't attempt to score the game nor itself play the game, it's just a minimal interface to build your smarts onto:
from turtle import Turtle, Screen
UNIT_SIZE = 80
HALF_GRID_SIZE = 3 * UNIT_SIZE / 2
FONT_SIZE = 48 # adjust to taste
x_turn = True
def choosen(turtle):
global x_turn
turtle.onclick(None) # taken so no longer responds
x, y = turtle.position()
magic_marker.goto(x, y - FONT_SIZE / 2)
magic_marker.write("X" if x_turn else "O", font=("Arial", FONT_SIZE, "bold"), align="center")
x_turn = not x_turn
magic_marker = Turtle(visible=False)
magic_marker.speed("fastest")
magic_marker.penup()
for row in range(-UNIT_SIZE, UNIT_SIZE + 1, UNIT_SIZE):
if row >= 0:
magic_marker.goto(-HALF_GRID_SIZE, row - UNIT_SIZE / 2)
magic_marker.pendown()
magic_marker.setx(HALF_GRID_SIZE)
magic_marker.penup()
for column in range(-UNIT_SIZE, UNIT_SIZE + 1, UNIT_SIZE):
if column >= 0:
magic_marker.goto(column - UNIT_SIZE / 2, -HALF_GRID_SIZE)
magic_marker.pendown()
magic_marker.sety(HALF_GRID_SIZE)
magic_marker.penup()
turtle = Turtle(shape="square", visible=False)
turtle.shapesize(0.8 * (UNIT_SIZE / 20)) # 0.8 = a safety margin
turtle.color("white")
turtle.penup()
turtle.goto(column, row)
turtle.showturtle() # still white on white but not clickable if not visible
turtle.onclick(lambda x, y, t = turtle: choosen(t))
screen = Screen()
screen.mainloop()
It uses click events and turns control over to the main loop so other events can also be active.
Related
I'm creating a simple game with Python using turtle package.
My aim is to have some balloons on the screen running from right to left and then when one of them is clicked to make it disappear.
What I have going wrong is that when I click one balloon, all of them are disappeared!
Here is my code
1- Main
from turtle import Screen
from balloon import Balloon
import time
screen = Screen()
screen.title('Balloons Nightmare')
screen.setup(width=600, height=600)
screen.bgpic(picname='sky-clouds.gif')
screen.tracer(0)
balloons_manager = Balloon()
current_x = 0
current_y = 0
screen.listen()
screen.onclick(fun=balloons_manager.explode_balloon, btn=1)
game_is_on = True
while game_is_on:
time.sleep(0.1)
screen.update()
balloons_manager.create_balloon()
balloons_manager.move_balloon()
screen.exitonclick()
2- balloon module
import random
from turtle import Turtle
COLORS = ["red", "yellow", "green", "blue", "black"]
MOVEMENT_SPEED = 2
class Balloon:
def __init__(self):
self.all_balloons = []
self.balloon_speed = MOVEMENT_SPEED
self.x = 0
self.y = 0
self.hidden = None
def create_balloon(self):
random_choice = random.randint(1, 9)
if random_choice == 1:
new_balloon = Turtle("circle")
new_balloon.penup()
new_balloon.turtlesize(stretch_wid=2, stretch_len=0.75)
new_balloon.color(random.choice(COLORS))
random_y_cor = random.randint(-50, 280)
new_balloon.goto(320, random_y_cor)
self.hidden = new_balloon.isvisible()
self.all_balloons.append(new_balloon)
def move_balloon(self):
for balloon in self.all_balloons:
balloon.backward(self.balloon_speed)
def explode_balloon(self, x, y):
for balloon in range(len(self.all_balloons)):
print(self.all_balloons[balloon].position())
self.all_balloons[balloon].hideturtle()
# for balloon in self.all_balloons:
# balloon.hideturtle()
I tried so many changes but nothing helped me, example of what I tried so far
getting the current x,y coordinates of the balloon so that on click to hide only the one with these coordinates but didn't work for me or I did something wrong with it
Any hints will be appreciated.
Thank you
I fixed the issue by adding an inner function in create_balloon function
def create_balloon(self):
random_choice = random.randint(1, 9)
if random_choice == 1:
new_balloon = Turtle("circle")
new_balloon.penup()
new_balloon.turtlesize(stretch_wid=2, stretch_len=0.75)
new_balloon.color(random.choice(COLORS))
random_y_cor = random.randint(-50, 280)
new_balloon.goto(320, random_y_cor)
def hide_the_balloon(x, y):
return new_balloon.hideturtle()
new_balloon.onclick(hide_the_balloon)
self.all_balloons.append(new_balloon)
Here's your code triggered by the click handler:
def explode_balloon(self, x, y):
for balloon in range(len(self.all_balloons)):
print(self.all_balloons[balloon].position())
self.all_balloons[balloon].hideturtle()
This loops over all balloons and hides them unconditionally.
You probably want to use an if in there to only conditionally trigger the hiding behavior. Compare the x and y coordinates of the click against the current balloon in the loop. Only hide the balloon if the distance is less than a certain amount (say, the radius of the balloon).
Another approach is to use turtle.onclick to add a handler function that will be trigged when the turtle is clicked.
Related:
How to see if a mouse click is on a turtle in python
Python find closest turtle via mouse click
Why is my Python Turtle program slowing down drastically the longer it runs? (since you're never removing turtles and constantly adding new ones, this is a good thread to take a look at)
I have got a problem with how to end my turtle python game. The code seems to function if I place the turtles at the starting/ending point from the beginning of the code, but it does not register when the turtle reaches the endpoint in gameplay. From what I know I think my maths for the end function is right. I am new and appreciate the help. I am currently offline though.
CODE:
import time
import turtle
from turtle import *
wn = turtle.Screen()
name=textinput("Question", "what is your name")
#display
pencolor("white")
penup()
goto(0,170)
write("hello " +name,align='center',font=('Comic Sans', 20))
#wn = turtle.screen() if the code doesn't work
#diffrent turtles here
t1 = turtle.Turtle()
t2 = turtle.Turtle()
t3 = turtle.Turtle()
#starting psoition
turtle.penup()
t1.penup()
turtle.goto(-1, -230)
t1.goto(-1, -170)
#starting line postion
def f():
fd(10)
def b():
bk(10)
def l():
left(10)
def r():
right(10)
#testing
def fo():
t1.fd(10)
def ba():
t1.bk(10)
def le():
t1.left(10)
def ri():
t1.right(10)
#turtle coordinates
first=turtle.ycor()
second=turtle.xcor()
third=t1.ycor()
fourth=t1.xcor()
#when to end the game
if (turtle.ycor()>= (-160)) and (turtle.ycor()<= (-240)):
if (turtle.xcor()>= (0)) and (turtle.xcor()<= (11)):
print("Finally working")
#replaced with write who the winner is later
if (t1.ycor()>= (-160)) and (t1.ycor()<= (-240)):
if (t1.xcor()>= (0)) and (t1.xcor()<= (11)):
print("Finally")
# onkey creates the key board = turtle.onkey("function, key") You have to keep pressing keys for it to move
turtle.onkey(f, "w")
turtle.onkey(b, "s")
turtle.onkey(l, "a")
turtle.onkey(r, "d")
wn.onkey(fo, "Up")
wn.onkey(ba, "Down")
wn.onkey(le, "Left")
wn.onkey(ri, "Right")
listen()
#WINDOW SETUP
window = Screen()
window.setup(800, 800)
window.title("Turtle game")
turtle.bgcolor("forestgreen")
t3.color("black")
t3.speed(0)
t3.penup()
t3.setpos(-140, 250)
t3.write("THE TURTLE RACE", font=("Comic Sans", 30, "bold"))
t3.penup()
#turtle ask name
#add images here
#turtle controls
# def creates a function. : means opperation f means move turtle foward. fd push turtle forward
# onkey creates the key board = turtle.onkey("function, key") You have to keep pressing keys for it to move
t2.speed(0)
t2.color("grey")
t2.pensize(100)
t2.penup()
t2.goto(-200, -200)
t2.left(90)
t2.pendown()
t2.forward(300)
t2.right(90)
t2.forward(500)
t2.right(90)
t2.forward(300)
t2.right(90)
t2.forward(500)
turtle.penup()
Firstly, your maths is not quite right - a coordinate can never be both <= -240 and >= -160. It should be t.ycor() >= -240 and t.ycor() <= -160, or more briefly, -240 <= t.ycor() <= -160.
Secondly, the condition as it stands is only checked once, when the code is first run. Instead, you need to get the program to check it regularly. You can do this by adding a general onkeypress event handler which is checked every time any key is pressed.
def check_status():
for player, t in enumerate([turtle, t1]):
if 0 <= t.xcor() <= 11 and -240 <= t.ycor() <= -160:
print(f"Player {player} is at the endpoint")
...
wn.onkeypress(check_status)
listen()
I am trying to draw a checker board using the Turtle library and am running into an error where the board window does not open. It was working at the beginning of my session about 30 minutes ago but, I changed some stuff and want to know why it changed.
Here is my code:
##This program draws a checkboard using the turtle library
import turtle
#below initiates the turtle pen and screen
penMain = turtle.Turtle()
turtleMain = turtle.Screen()
def turtleBoard():
for x in range(4):
penMain.forward(30)
penMain.left(90)
penMain.forward(30)
turtleMain.setup(600, 600)
penMain.speed(50)
for a in range(8):
penMain.up()
penMain.setpos(0, 30 * a)
penMain.down()
for x in range(8):
if (a + x)% 2 == 0:
squareColor = 'black'
else:
squareColor = 'white'
penMain.fillcolor(squareColor)
penMain.begin_fill()
turtleBoard()
penMain.end_fill()
I believe this code works besides my one error! Thank you all for your help in advance!
I can't say what changes you made to get your current code, but this code seems to be working:
##This program draws a checkboard using the turtle library
import turtle
#below initiates the turtle pen and screen
penMain = turtle.Turtle()
turtleMain = turtle.Screen()
def turtleBoard():
penMain.forward(30)
turtleMain.setup(600, 600)
penMain.speed(50)
for a in range(8):
for x in range(8):
penMain.up()
penMain.setpos(30 * x, 30 * a)
penMain.down()
penMain.begin_fill()
for xx in range(4):
penMain.forward(30)
penMain.left(90)
if a%2 == x%2:
squareColor = 'black'
else:
squareColor = 'white'
penMain.fillcolor(squareColor)
penMain.end_fill()
turtleBoard()
turtle.done()
Now that we've seen that your code can be made to work, let's consider stamping instead of drawing to make it work more simply and more quickly:
from turtle import Screen, Turtle
SQUARES_PER_EDGE = 8
SQUARE_SIZE = 30 # in pixels
OFFSET = SQUARE_SIZE * (SQUARES_PER_EDGE / 2) - SQUARE_SIZE/2 # center the board
CURSOR_SIZE = 20
def turtleBoard():
turtle.shape('square')
turtle.shapesize(SQUARE_SIZE / CURSOR_SIZE)
turtle.penup()
for y in range(SQUARES_PER_EDGE):
for x in range(SQUARES_PER_EDGE):
turtle.goto(x * SQUARE_SIZE - OFFSET, y * SQUARE_SIZE - OFFSET)
turtle.fillcolor('black' if y % 2 == x % 2 else 'white')
turtle.stamp()
screen = Screen()
screen.setup(600, 600)
turtle = Turtle()
turtle.speed('fastest') # because I have no patience
turtleBoard()
screen.exitonclick()
I lined up the indent of the bottom 4 lines with the last 'else' statement and it worked. Thank you guys!
I'm doing an assignment where I have to write a small game. When a turtle collides with a dot (bug) on the screen, it will add one point to the score value in the top left and teleport the bug to another random spot. I'm having trouble getting the score to update when they collide.
I tried to put the score update within the game loop but that did not work as it kept telling me that the value is not defined. I tried solving that with a global value, but that didn't do anything:
import turtle
import math
import random
#Set up the constants for the game.
WINDOW_HEIGHT = 300
WINDOW_WIDTH = 300
FORWARD_STEP = 10 #how much does the turtle move forward
TURN_STEP = 30 #how much does the turtle turn (in degrees)
SHRINK_FACTOR = 0.95 #how much does the turtle shrink when it moves
DEATH_WIDTH = 0.05 #the size at which you stop the game because the user lost
COLLISION_THRESHOLD = 10;#we say that two turtles collided if they are this much away
#from each other
#Define functions
def game_setup():
'''set up the window for the game, a bug and the player turtle '''
#create the screen
wn = turtle.Screen()
wn.screensize(WINDOW_HEIGHT,WINDOW_WIDTH)
wn.bgcolor("light green")
#Create player turtle
player = turtle.Turtle()
player.color("blue")
player.shape("turtle")
player.penup()
player.setpos (random.randrange(1,301), random.randrange(1,301))
#create a bug
bug1 = turtle.Turtle()
bug1.color("black")
bug1.shape("circle")
bug1.shapesize(stretch_wid=0.2, stretch_len=0.2)
bug1.penup()
bug1.speed(0) #the bug is not moving
bug1.setposition(-200, 200)
#create score turtle
score_keeper = turtle.Turtle()
score_keeper.hideturtle()
score_keeper.penup()
score_keeper.setposition (-400,360)
score = 0
scorestring = "Score: %s" %score
score_keeper.write(scorestring, False, align="left", font=("Arial",14, "normal"))
return (wn,player,bug1,score_keeper)
def is_collision (player, bug1):
distance = (math.sqrt((player.xcor()-bug1.xcor())**2 + (player.ycor() - bug1.ycor())**2))
if distance < COLLISION_THRESHOLD:
return True
else:
return False
def main():
#set up the window, player turtle and the bug
(wn,player,bug1,score_keeper) = game_setup()
#make the arrow keys move the player turtle
bindKeyboard(player)
#Set this veriableto True inside the loop below if you want the game to end.
game_over = False
player_width = get_width(player)
#This is the main game loop - while the game is not over and the turtle is large enough print the width of the turtle
#on the screen.
while not game_over and player_width > DEATH_WIDTH:
#your collision detection should go here
if is_collision (player, bug1):
bug1.setpos (random.randrange(1,301), random.randrange(1,301))
player.shapesize(stretch_wid=1, stretch_len=1)
player_width = get_width(player)
player.showturtle()
print(player_width)
print("Done")
wn.exitonclick()
main()
This is most of the code. All I want it to do is when the is_collision() function happens, it adds 1 to the value of score and the score_keeper turtle then prints that value in the window.
Im havign trouble getting the score to update when they collide.
I've done a stripped down rework of your code below (substuting for methods you left out) to show how to update the score on the screen. Code like this shouldn't have an explicit main loop as it's all event driven and should instead call turtle's main event loop:
from turtle import Screen, Turtle
from random import randrange
from functools import partial
# Set up the constants for the game.
WINDOW_WIDTH, WINDOW_HEIGHT = 500, 500
FORWARD_STEP = 10 # how much does the turtle move forward
TURN_STEP = 30 # how much does the turtle turn (in degrees)
COLLISION_THRESHOLD = 10 # we say that two turtles collided if they are this much away from each other
CURSOR_SIZE = 20
FONT = ('Arial', 14, 'normal')
# Define functions
def is_collision(player, bug):
return player.distance(bug) < COLLISION_THRESHOLD
def random_position(turtle):
scale, _, _ = turtle.shapesize()
radius = CURSOR_SIZE * scale
return randrange(radius - WINDOW_WIDTH/2, WINDOW_WIDTH/2 - radius), randrange(radius - WINDOW_HEIGHT/2, WINDOW_HEIGHT/2 - radius)
def forward():
global score
player.forward(FORWARD_STEP)
if is_collision(player, bug):
bug.setposition(random_position(bug))
score += 1
score_keeper.clear()
score_keeper.write(f"Score: {score}", font=FONT)
# Set up the window for the game, a bug and the player turtle.
# Create the screen
screen = Screen()
screen.setup(WINDOW_WIDTH, WINDOW_HEIGHT)
screen.bgcolor('light green')
# Create score turtle
score_keeper = Turtle(visible=False)
score_keeper.penup()
score_keeper.setposition(-230, 230)
score = 0
score_keeper.write(f"Score: {score}", font=FONT)
# Create a bug
bug = Turtle('circle', visible=False)
bug.shapesize(4 / CURSOR_SIZE)
bug.penup()
bug.setposition(random_position(bug))
bug.showturtle()
# Create player turtle
player = Turtle('turtle', visible=False)
player.color('blue')
player.speed('fastest')
player.penup()
player.setposition(random_position(player))
player.showturtle()
# make the arrow keys move the player turtle
screen.onkey(partial(player.left, TURN_STEP), 'Left')
screen.onkey(partial(player.right, TURN_STEP), 'Right')
screen.onkey(forward, 'Up')
screen.listen()
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()