How can i break a for-loop with an key event? - python

I want to break a for-loop in Python 3.7.3 with a key event. I am trying to make a small game in the turtle graphics.
import turtle
from turtle import *
block1 = turtle.Turtle()
def space1():
block1.hideturtle()
listen()
onkey(space1, "space")
for b in range(1, 200):
block1.backward(537.5)
block1.forward(537.5)
For now I hide the turtle when I press "space", but I want to break the for-loop with the key event.

Make another callback:
...
exit = false
def space1():
global exit
exit = True
block1.hideturtle()
listen()
onkey(space1, "space")
for b in range(1, 200):
if exit:
break
block1.backward(537.5)
block1.forward(537.5)
This is just an example and altough you should not use global variables and you should encapsulate all this behaviour, it will be enough.

Related

Why do I need the time.sleep(x) function in this code for it to work?

from turtle import Screen, Turtle
import time
import snake
MOVE_DISTANCE = 20
UP = 90
DOWN = 270
LEFT = 180
RIGHT = 0
class InitialSnake:
def __init__(self):
self.number_x = 0
self.snake_first = []
self.create_snake()
self.actual_snake = self.snake_first[0]
def create_snake(self):
for number in range(3):
snake = Turtle(shape="square")
snake.penup()
snake.color("white")
snake.goto(self.number_x, 0)
self.number_x -= 20
self.snake_first.append(snake)
def move(self):
for segments_num in range(len(self.snake_first) - 1, 0, -1):
self.snake_first[segments_num].goto(self.snake_first[segments_num - 1].xcor(),
self.snake_first[segments_num - 1].ycor())
self.snake_first[0].forward(MOVE_DISTANCE)
def up(self):
if self.actual_snake.heading() != DOWN:
self.snake_first[0].setheading(UP)
def down(self):
if self.actual_snake.heading() != UP:
self.snake_first[0].setheading(DOWN)
def left(self):
if self.actual_snake.heading() != RIGHT:
self.snake_first[0].setheading(LEFT)
def right(self):
if self.actual_snake.heading() != LEFT:
self.snake_first[0].setheading(RIGHT)
my_screen = Screen()
my_screen.setup(width=600, height=600)
my_screen.bgcolor("black")
my_screen.title("Snake Game")
my_screen.tracer(0)
snake_1 = snake.InitialSnake()
#snake_here.create_snake()
game_is_on = True
my_screen.listen()
my_screen.onkey(snake_1.up, "Up")
my_screen.onkey(snake_1.down,"Down")
my_screen.onkey(snake_1.left,"Left")
my_screen.onkey(snake_1.right,"Right")
while game_is_on:
my_screen.update()
time.sleep(0.1)
snake_1.move()
my_screen.exitonclick()
I did not really understand the concept of the tracer and the update and how this is linked to the sleep function. I understand that when the tracer is called and turned off, it will not refresh the screen until you call the update() function. But shouldn't it still work without time.sleep(0.1) since this is just creating a short delay before the next functions get called. Can someone help me please to understand this? Thanks in advance (:
If you use print() to see snake position
while game_is_on:
my_screen.update()
snake_1.move()
print(snake_1.snake_first[0].ycor(), snake_1.snake_first[0].xcor())
then you will see it moves very, very fast and it left screen - and you simply can't see it.
On my very old computer after few milliseconds its positon was (0, 125700). Probably on the newest computer it would be much bigger value.
So this code use sleep() to slow down snake and to move it with the same speed on all computers.
BTW:
I would use turtle method to repeate code
def game_loop():
snake_1.move()
my_screen.update()
my_screen.ontimer(game_loop, 100) # repeate after 100ms (0.1s)
# start loop
game_loop()
because sleep may blocks some code and ontimer was created for non-blocking delay.

How to stop turtle movement when two turtles are in proximity of each other?

I'm making a game when the user has to move the car to dodge obstacles, Everything working except I can't get the game to and end. My goal is to end the game when the block hits the turtle.
The issue is I can't get the game to detect the turtles to detect their proximity and the exit or quit command doesn't work. Also I don't think the code pasted properly so adjust the indents if it doesn't work.
import turtle as trtl
import random as rand
import sys
#initialize turtles
turtle = trtl.Turtle(shape = "turtle")
block = trtl.Turtle(shape = "square")
drawer = trtl.Turtle()
blockList = []
wn = trtl.Screen()
#game configuration
turtle.pu()
turtle.goto(0,-150)
drawer.pu()
drawer.goto(0,-160)
drawer.pd()
drawer.pensize(10)
drawer.pencolor("blue")
drawer.forward(180)
drawer.left(90)
drawer.forward(300)
drawer.left(90)
drawer.forward(350)
drawer.left(90)
drawer.forward(300)
drawer.left(90)
drawer.forward(180)
drawer.hideturtle()
def turtleRight():
turtle.setheading(0)
turtle.pu()
turtle.forward(15)
def turtleLeft():
turtle.setheading(180)
turtle.pu()
turtle.forward(15)
#actions
wn.onkeypress(turtleRight, 'Right')
wn.onkeypress(turtleLeft, 'Left')
wn.listen()
for i in range(5):
app = block
blockList.append(app)
#functions
def draw_blocks(index):
blockList[index].penup()
blockList[index].shape("square")
wn.tracer(False)
blockList[index].setx(rand.randint(-150,150))
blockList[index].sety(rand.randint(0,125))
blockList[index].showturtle()
wn.tracer(True)
wn.update()
def drop_block(index):
blockList[index].penup()
blockList[index].clear()
blockList[index].speed(2)
blockList[index].sety(-150)
blockList[index].hideturtle()
draw_blocks(index)
xDistance = abs(turtle.xcor()-block.xcor())
yDistance = abs(turtle.ycor()-block.ycor())
if (xDistance < 20 and yDistance < 20):
sys.exit()
else:
drop_block(i)
for i in range(5):
drop_block(i)
wn.mainloop()
trtl.done()
Can anyone help..!?
The problem is that you're re-drawing the block and thus resetting its position before you're testing the distance.
Alter the function like so:
def drop_block(index):
blockList[index].penup()
blockList[index].clear()
blockList[index].speed(2)
blockList[index].sety(-150)
blockList[index].hideturtle()
xDistance = abs(turtle.xcor()-block.xcor())
yDistance = abs(turtle.ycor()-block.ycor())
print(xDistance, yDistance)
if (xDistance < 20 and yDistance < 20):
sys.exit()
else: # redraw blocks _after_ calculating distance
draw_blocks(index)
drop_block(i)

Python onkey not stopping loop

def startgame():
start.state = False
print start.state
def restart():
end.state = False
start.state = True
print game.state, end.state
s.listen()
s.onkey(startgame, "Return")
s.onkey(restart, 'r')
# This Loop stops when you hit Enter
while start.state:
start.enter()
s.reset()
# I tried repeating it here but it doesn't work
while end.state:
end.enter()
s.reset()
game.state = 'playing'
Both loops are nested in a main while loop but the second one is nested in another while loop (If that helps) so it would look something like this
while True:
while start.state:
start.flash()
s.reset()
while True:
# main game code here
while end.state:
end.flash()
s.reset()
game.state = 'playing'
break
I simply want it to show the end screen and have 'Press r to play again' flash on screen until the player hits r and then it should restart the game and go back to the start screen. The end.state variable wont update during the while loop, but start.state does during its while loop.
Your drawn out (in time) while loops have no place in an event-driven environment like turtle. In general, we need to control everything with events, specifically timer events. Let's rewrite your code as a state machine. (Since I don't have your object classes, entities like start.state become globals like start_state in my example code.)
from turtle import Screen, Turtle
start_state = True
end_state = False
def start_game():
global start_state
start_state = False
def end_game():
global end_state
if not start_state:
end_state = True
def restart_game():
global end_state, start_state
end_state = False
start_state = True
def flash(text):
turtle.clear()
turtle.write(text, align="center", font=('Arial', 24, 'bold'))
color = turtle.pencolor()
turtle.pencolor(turtle.fillcolor())
turtle.fillcolor(color)
def game_states():
if start_state:
flash("Start")
elif end_state:
flash("End")
else:
flash("Playing")
screen.ontimer(game_states, 500)
screen = Screen()
turtle = Turtle()
turtle.hideturtle()
turtle.fillcolor(screen.bgcolor())
screen.onkey(start_game, 'Return')
screen.onkey(restart_game, 'r')
screen.onkey(end_game, 'e')
screen.listen()
game_states()
screen.mainloop()
The state machine rules are:
Start via <Return> -> Playing
Playing via <e> -> End and via <r> -> Start
End via <r> -> Start

Use onkey() to do multiple functions with Python turtle

I'm trying to write a basic turtle drawing game/program and I've been using onkey(function, "key") to have the user input keystrokes. Well I wanted the user to be able to change the width of the pen by either hitting the up key to increase the width by one, or the down key to decrease the width by one. I know I need some kind of loop, but I don't really know where to implement it.
Here's a simple example that will make the turtle walk in a continuous circle while you press up and down arrows to change the pen width:
from turtle import Turtle, Screen
def larger():
size = turtle.pensize()
if size < 10:
turtle.pensize(size + 1)
def smaller():
size = turtle.pensize()
if size > 1:
turtle.pensize(size - 1)
def move():
turtle.circle(150, extent=3)
screen.ontimer(move, 100)
turtle = Turtle()
screen = Screen()
screen.onkey(larger, "Up")
screen.onkey(smaller, "Down")
screen.listen()
move()
screen.mainloop()
Make sure you click on the window first to make it the key listener.
I think you can't, but you can call the function insde the function you bind to the key:
from turtle import *
def function1():
do_that = "do that"
print(do_that)
def function2():
do_this = "do this"
print(do_this)
function1()
onkey(function2, "space")
do this
do that
It worked for me ;)

How do I make this program wait for a screen click before starting?

I am attempting to finish a program for a class, and I am doing fairly well. It is a simple turtle-graphics game in python, where you attempt to avoid poison dots and get navigate to a square. However, my program starts immediately, before the user clicks on the screen. How can I fix this? Thanks!
My code:
# This game involves avoiding red poison blobs while attempting to navigate to
# a square. If you hit the blob, you begin to speed up, making it more difficult
# not to hit more. Additionally, you lose a point. If you reach the square you
# get a point.
import turtle
import math
import random
# screen
wn = turtle.Screen()
wn.bgcolor("black")
wn.tracer(3)
# Draw border
pen1 = turtle.Turtle()
pen1.color("white")
pen1.penup()
pen1.setposition(-275,-275)
pen1.pendown()
pen1.pensize(5)
for side in range(4):
pen1.forward(550)
pen1.left(90)
pen1.hideturtle()
# player
player = turtle.Turtle()
player.color("dark green")
player.shape("turtle")
player.penup()
# poisonBlob
maxpoisonBlob = 15
poisonBlob = []
for a in range(maxpoisonBlob):
poisonBlob.append(turtle.Turtle())
poisonBlob[a].color("dark red")
poisonBlob[a].shape("circle")
poisonBlob[a].shapesize(4, 4, 4)
poisonBlob[a].penup()
poisonBlob[a].speed(0)
poisonBlob[a].setposition(random.randint(-255, 255), random.randint(-255, 255))
maxfood = 1
food = []
for a in range(maxfood):
food.append(turtle.Turtle())
food[a].color("light blue")
food[a].shape("square")
food[a].penup()
food[a].speed(0)
food[a].setposition(random.randint(-240, 240), random.randint(-240, 240))
# speed variable
speed = 6.5
def turnleft():
player.left(30)
def turnright():
player.right(30)
def increasespeed():
global speed
speed += 1
def touchPoison(t1, t2):
d = math.sqrt(math.pow(t1.xcor()-t2.xcor(),2) + math.pow(t1.ycor()-t2.ycor(),2))
if d < 50:
return True
else:
return False
def touchfood(z1, z2):
d = math.sqrt(math.pow(z1.xcor()-z2.xcor(),2) + math.pow(z1.ycor()-z2.ycor(),2))
if d < 20:
return True
else:
return False
turtle.listen()
turtle.onkey(turnleft, "Left")
turtle.onkey(turnright, "Right")
def main():
print("Help your turtle navigate red poison blobs while attempting to navigate to the\n"
"food! If you hit the poison, you begin to speed up, making it more difficult\n"
"not to hit more. Additionally, you lose a point. If you reach the square you\n"
"get a point. To navigate, click the screen, and then use the right and left\n"
"arrow keys. Quickly, your turtle is running away!")
score = 0
while True:
player.forward(speed)
# turtle boundary
if player.xcor() > 260 or player.xcor() < -260:
player.right(180)
# turtle boundary
if player.ycor() > 260 or player.ycor() < -260:
player.right(180)
# move poison
for a in range(maxpoisonBlob):
poisonBlob[a].forward(3)
# Poison boundaries
if poisonBlob[a].xcor() > 220 or poisonBlob[a].xcor() < -220:
poisonBlob[a].right(180)
# Poision boundaries
if poisonBlob[a].ycor() > 220 or poisonBlob[a].ycor() < -220:
poisonBlob[a].right(180)
# Poison touching
if touchPoison(player, poisonBlob[a]):
increasespeed()
poisonBlob[a].setposition(random.randint(-230, 230), random.randint(-230, 230))
poisonBlob[a].right(random.randint(0,360))
score -= 1
#Draw score
pen1.undo()
pen1.penup()
pen1.hideturtle()
pen1.setposition(-260, 280)
scorestring = "Score: %s" %score
pen1.write(scorestring, font=("Calibri", 12))
for a in range(maxfood):
#Positive Point Checking
if touchfood(player, food[a]):
food[a].setposition(random.randint(-230, 230), random.randint(-230, 230))
score += 1
#Draw Score
pen1.undo()
pen1.penup()
pen1.hideturtle()
pen1.setposition(-260, 280)
scorestring = "Score: %s" %score
pen1.write(scorestring, font=("Calibri", 12))
if __name__ == "__main__":
main()
Generally we try not to directly answer homework questions, but I will give you some guidance.
I'm not familiar with the library you're using, but the basic idea is that you need to wait until the user clicks the screen before continuing. My understanding is that you want to do so after the print() function in main, so that the user has to read the message and then click to continue.
A quick search turned up this link: Turtle in python- Trying to get the turtle to move to the mouse click position and print its coordinates
This goes into detail of the onscreenclick() event. Judging by your onkey() methods you are already familiar with binding to events, so you should be able to use this knowledge to bind to the onscreenclick() event and then you just need to break your code up a bit so that it doesn't execute your game until the onscreenclick() event has happened.
If that isn't clear enough, help me understand where your confusion is and I'll assist further. Thanks!
You could begin by defining a function where you call your "if" statement and then use turtle.onscreenclick() by inserting your newly defined function in it like so:
def Game(x, y):
if __name__ == "__main__":
main()
turtle.onscreenclick(Game)
Make sure to insert all this at the end!
Now, adding the (x, y) in the parenthesis after the function name in the function definition assigns the spot the user clicks on in the graphics window its corresponding (x, y) coordinates, which in turn activates the function due to the action of clicking on the screen. I did this successfully, so I can assure you it works! I hope this helps! :)

Categories