code executing in odd order with turtle graphics - python

i am currently trying to make a game like snake in python using turtle graphics and have encountered a game breaking bug when you turn the turtle using a and d keys if you are in line with any previous turn. it appears to be executing code out of order but i have no real idea whats happening.
the whole code is below
import turtle
import random
import math
x = 400
y = 400
global speed
global points
points = 0
speed = 2
posList = ['']
# turns your character left
def left():
global speed
global posList
key = True
char.fd(speed)
char.left(90)
char.fd(speed)
# turns your character left
def right():
global speed
global posList
key = True
char.fd(speed)
char.right(90)
char.fd(speed)
# adds one box to the point counter
def point():
global points
points += 1
wall.speed(0)
wall.pendown()
wall.forward(50)
wall.seth(90)
wall.forward(10)
wall.seth(180)
wall.forward(50)
wall.seth(270)
wall.forward(10)
wall.penup()
wall.seth(90)
wall.forward(12)
wall.seth(0)
dot.setx(random.randint(-200,200))
dot.sety(random.randint(-200,200))
print(points)
# checks if curren posisition is anywhere you have ever been
def checkBracktrack(pos, poslist):
found = False
for thing in posList:
if thing == pos:
found=True
return found
# creates the box that the game occurs in
turtle.colormode(255)
screen = turtle.Screen()
dot = turtle.Turtle()
dot.penup()
dot.speed(0)
dot.shape('turtle')
dot.setx(random.randint(-200,200))
dot.sety(random.randint(-200,200))
wall = turtle.Turtle()
wall.speed(0)
wall.penup()
wall.goto(x/2,y/2)
wall.pendown()
wall.seth(180)
wall.forward(400)
wall.seth(270)
wall.forward(400)
wall.seth(0)
wall.forward(400)
wall.seth(90)
wall.forward(400)
wall.seth(270)
wall.forward(400)
wall.seth(0)
wall.penup()
wall.forward(100)
char = turtle.Turtle()
x = 0
y = 0
# updates the position of the player turtle
while True:
screen.onkey(left,"a")
screen.onkey(right,"d")
char.hideturtle()
char.forward(speed)
char.speed(0)
turtle.listen(xdummy=None, ydummy=None)
print(char.pos())
print(posList[(len(posList)-1)])
# checks if current position is the same as any position it has ever been in !this is the bit that is having problems!
if checkBracktrack(char.pos(),posList):
speed = 0
break
# checks if it is close enough to a point marker to
if char.ycor() in range(dot.ycor()-10,dot.ycor()+10) and char.xcor() in range(dot.xcor()-10,dot.xcor()+10):
point()
# checks if in the box
if char.ycor() not in range(-200,200) or char.xcor() not in range(-200,200):
speed = 0
# adds current location to the list
posList.append(char.pos())
char.fd(speed)
print('you travelled',len(posList),'pixels')
print('collided with yourself')
print(char.pos())
print(posList)
name = input('quit')
screen.mainloop()

There are many little issues with your code: you need to reread about when to use global; your checkBracktrack() function takes poslist as an argument but operates on the global posList instead (case typo); your pixels travelled distance calculation is incorrect because of extra fd() calls and a speed greater than 1; your proximity test can be greatly simplified using turtle's .distance() method; your code to display points on the game board doesn't work at all; you call onkey() over and over again in a loop when you only need to call it once for each key; your checkBracktrack() function has an unnecessary loop.
The biggest issue I have with the code is the while True: which shouldn't happen in event-based code. I've rewritten, and simplified your code below, addressing the above issues as well as others:
from turtle import Turtle, Screen
from random import randint
FONT = ('Arial', 24, 'normal')
WIDTH, HEIGHT = 400, 400
SPEED = 1
def left():
""" turns your character left """
char.left(90)
def right():
""" turns your character right """
char.right(90)
def point():
""" adds one box to the point counter """
global points
points += 1
wall.undo()
wall.write(points, font=FONT)
dot.setpos(randint(-WIDTH/2, WIDTH/2), randint(-HEIGHT/2, HEIGHT/2))
def checkBracktrack(pos, poslist):
""" checks if current posiition is anywhere you have ever been """
return pos in poslist
def move_char():
""" updates the position of the player turtle """
over = False
char.forward(SPEED)
# checks if current position is the same as any position it has ever been at
if checkBracktrack(char.pos(), posList):
over = True
# checks if in the box
elif not (-200 <= char.ycor() <= 200 and -200 <= char.xcor() <= 200):
over = True
if over:
print('you travelled', len(posList), 'pixels')
return
# adds current location to the list
posList.append(char.pos())
# checks if it is close enough to a point marker
if char.distance(dot) < 20:
point()
screen.ontimer(move_char, 10)
points = 0
posList = []
# creates the box in which the game occurs
screen = Screen()
screen.onkey(left, "a")
screen.onkey(right, "d")
screen.listen()
dot = Turtle('turtle')
dot.speed('fastest')
dot.penup()
dot.setpos(randint(-WIDTH/2, WIDTH/2), randint(-HEIGHT/2, HEIGHT/2))
wall = Turtle(visible=False)
wall.speed('fastest')
wall.penup()
wall.goto(WIDTH/2, HEIGHT/2)
wall.pendown()
for _ in range(4):
wall.right(90)
wall.forward(400)
wall.penup()
wall.forward(100)
wall.write("0", font=FONT)
char = Turtle(visible=False)
char.speed('fastest')
move_char()
screen.mainloop()
My belief is that the problem that prompted your original question got fixed in the process of reworking the code.

Related

How can I delay the onkey event until the function has finished executing

Basically I'm making a space invaders type game in python and for some reason, it's still allowing the user to press the spacebar no matter what I do. Therefore, I would like to see what kind of ideas you guys have since I have no idea what to do to make this work.
Also I have tried creating a delay variable so that if the delay variable is true then it will execute the onkey event but unfortunately it didn't work.
I have also tried making the both the function and the onkey sleep via the time library but also didn't work
import turtle
# Making the window and its proporties
wn = turtle.Screen()
wn.title("Galaxy Wars")
wn.bgcolor("black")
# adding the images to the shape function so that it will recongize them as a shape
turtle.register_shape("Spaceship.gif")
turtle.register_shape("invader2.gif")
turtle.register_shape("Laser beam.gif")
# creating the fighter jet and then puting it down
fighter = turtle.Turtle()
fighter.shape("Spaceship.gif")
fighter.penup()
fighter.setposition(0,-270)
fighter.setheading(90)
# create the laser
laser = turtle.Turtle()
laser.speed(0)
laser.setheading(90)
laser.hideturtle()
laser.shape("Laser beam.gif")
laser.penup()
# how far the fighter teleports each time a key is pressed
fighttp = 20
# delay
delay = "F"
# creating functions that either adds or substracts the current position
def up():
y = fighter.ycor()
y += fighttp
if y > -130:
y = -130
fighter.sety(y)
def left():
x = fighter.xcor()
x -= fighttp
if x < -370:
x = -370
fighter.setx(x)
def down():
y = fighter.ycor()
y -= fighttp
if y < -300:
y = -300
fighter.sety(y)
def right():
x = fighter.xcor()
x += fighttp
if x > 360:
x = 360
fighter.setx(x)
# give the player the laser beam to perform the pew pew on the bad guys just like the original game
def shoot():
if delay == "F":
delay == "T"
laser.speed(0)
laser.setposition(fighter.xcor(), fighter.ycor() + 20)
laser.showturtle()
laser.speed(3)
laser.forward(500)
laser.hideturtle()
delay == "F"
# turtle listens for the keys and then it moves according to the function and key pressed
turtle.listen()
turtle.onkey(left, "a")
turtle.onkey(right, "d")
turtle.onkey(up, "w")
turtle.onkey(down, "s")
if delay == "F":
turtle.onkey(shoot, "space")
wn.mainloop()
Skip the delay variable. Instead we can turn on and off the key event within the key event handler:
def shoot():
turtle.onkey(None, "space") # disable handler inside handler
laser.speed(0)
laser.setposition(fighter.xcor(), fighter.ycor() + 20)
laser.showturtle()
laser.speed(3)
laser.forward(500)
laser.hideturtle()
turtle.onkey(shoot, "space")
Below is a rework of your code with various style changes and optimizations:
from turtle import Screen, Turtle
# Making the window and its properties
screen = Screen()
screen.title("Galaxy Wars")
screen.bgcolor('black')
# Adding images to the shape function so that it will recognize them as a shape
screen.register_shape("Spaceship.gif")
screen.register_shape("Laser beam.gif")
# Create the fighter jet and then put it down
fighter = Turtle()
fighter.shape("Spaceship.gif")
fighter.penup()
fighter.sety(-270)
fighter.setheading(90)
# Create the laser
laser = Turtle()
laser.hideturtle()
laser.shape("Laser beam.gif")
laser.setheading(90)
laser.penup()
# How far the fighter teleports each time a key is pressed
fighttp = 20
# creating functions that either adds or subtracts the current position
def up():
y = fighter.ycor() + fighttp
if y > -130:
y = -130
fighter.sety(y)
def left():
x = fighter.xcor() - fighttp
if x < -370:
x = -370
fighter.setx(x)
def down():
y = fighter.ycor() - fighttp
if y < -300:
y = -300
fighter.sety(y)
def right():
x = fighter.xcor() + fighttp
if x > 360:
x = 360
fighter.setx(x)
# Give player laser beam to perform the pew pew on the bad guys just like the original game
def shoot():
screen.onkey(None, 'space') # disable handler inside handler
laser.speed('fastest')
laser.setposition(fighter.xcor(), fighter.ycor() + 20)
laser.showturtle()
laser.speed('slow')
laser.forward(500)
laser.hideturtle()
screen.onkey(shoot, 'space')
# Listen for keys and move according to the function and key pressed
screen.onkey(left, 'a')
screen.onkey(right, 'd')
screen.onkey(up, 'w')
screen.onkey(down, 's')
screen.onkey(shoot, 'space')
screen.listen()
screen.mainloop()

Why all balloons are hidden and not only the one I click?

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)

Jump and Move sideways

I can't figure out how to move sideways and jump at the same time, only one or the other.
I have tried asyncio and multithreading/multiprocessing and couldn't get it to work. I am all out of ideas and can't find anymore online. I also have another issue where I can jump and if I reach the apex of the jump and hold a or d I can move side to side floating.
class Player():
def __init__(self,health,boosts,height,width):
self.health = health
self.booosts = boosts
self.height = height
self.width = width
def jump():
global gravtest, press
press.remove("'w'")
gravtest = False
y = player[0].ycor()
for i in range (1, 10):
player[0].sety(y+(i*5))
time.sleep(0.05)
#while player[0]
gravtest = True
# def powers(self, boosts):
import turtle as t
import time, random
from pynput.keyboard import Key, Listener
t.ht()
press = []
gravtest = True
wn = t.Screen()
wn.title("Jump Man")
wn.bgcolor("white")
wn.screensize(250, 250)
wn.setup(width=1.0, height=1.0)
player = [t.Turtle(), Player(100, [], 25, 25)]
player[0].speed(0)
player[0].shapesize(0.5)
player[0].shape("square")
player[0].color("black")
player[0].up()
player[0].goto(0, 0)
floor = t.Turtle()
floor.speed(0)
floor.shape("square")
floor.shapesize(100)
floor.color("black")
floor.up()
floor.goto(0, -1150)
def gravity():
global gravtest
if gravtest == True:
grav = 0
while player[0].distance(floor) > 1007:
y = player[0].ycor()
player[0].sety(y + grav)
if grav > -5:
grav -= 1
player[0].sety(y + grav)
gravtest = False
if player[0].distance(floor) < 1045:
player[0].sety(-145)
def show(key):
global press
if not(format(key) in press):
press.append(format(key))
print(key)
def rem(key):
global press
if format(key) in press:
press.remove(format(key))
def move():
global press
while "'a'" in press:
player[0].setx(player[0].xcor()-2)
while "'d'" in press:
player[0].setx(player[0].xcor()+2)
if press == '\'s\'':
print()
gravity()
if "'w'" in press:
jump()
with Listener(on_press = show, on_release = rem) as listener:
while 1:
move()
Your problem with moving and jumping is that you have separate loops for each that try to handle one kind of movement of the movement in one place. That won't work properly if other stuff (like jumping while moving, or moving while falling under gravity) are supposed to be possible.
Instead, you need to have just one main loop. On each run of that loop, you should do one frame worth of movement of whatever kinds is appropriate (e.g. moving horizontally, falling or jumping). This may require some bookkeeping to keep track of how long the vertical acceleration from a jump lasts.
I'd make the main loop something like this:
on_the_ground = True
jumping = False
while True:
horizontal_movement() # this is always possible if the buttons are pressed
if on_the_ground or jumping:
jump() # can start or continue a jump if a button is pressed
else:
gravity() # fall, if you're not grounded or accelerating in a jump
handle_collisions_etc()
time.sleep(1/FRAMERATE)
Note, I've made some assumptions about the game logic you want. If you don't want to be able to move horizontally while in the air (as you can in many platformer games), you'll need to change this a little bit.
The jump function will need to keep track of how long you've been jumping for, since you probably want the player to be limited in how high they can go. I'm not exactly sure I understand the logic of your current code, so I'll leave it up to you to figure out exactly what to track.
A final suggestion is to move some of the global variables into the Player class. Even the turtle object you're using to draw things could be stored as an attribute on the instance you create.

Python - Turtle coordinates not working properly in snake style game

I am having some game breaking issues with a simple game that is similar to snake.
As you can see in the picture the snake is overlapping itself but has not collided like it is supposed to.
The collision with self does work some of the time as you can see but it is not reliable.
I have tried everything I can think of to fix this.
from turtle import Turtle, Screen
from random import randint
FONT = ('Arial', 24, 'normal')
WIDTH, HEIGHT = 400, 400
SPEED = 1
points = 0
posList = []
def left():
## turns your character left
char.left(90)
def right():
## turns your character right
char.right(90)
def point():
## adds one box to the point counter
global points
points += 1
wall.undo()
wall.write(str(points) + ' score', font=FONT)
dot.setpos(randint(-WIDTH/2, WIDTH/2), randint(-HEIGHT/2, HEIGHT/2))
dot.seth(randint(0,360))
def checkBracktrack(pos, poslist):
## checks if current posiition is anywhere you have ever been
return pos in poslist
def moveChar():
## updates the position of the player turtle
over = False
change = False
char.forward(SPEED)
# checks if current position is the same as any position it has ever been at
if checkBracktrack(char.pos(), posList):
over = True
# checks if in the box
elif not (-200 <= char.ycor() <= 200 and -200 <= char.xcor() <= 200):
over = True
if over:
print('you travelled', len(posList), 'pixels')
return
tempPos = char.pos()
# adds current location to the list
posList.append(tempPos)
# checks if it is close enough to a point marker
if char.distance(dot) < 20:
point()
# calls the moveChar function again
screen.ontimer(moveChar, 1)
# creates the box in which the game occurs
screen = Screen()
screen.onkey(left, "a")
screen.onkey(right, "d")
screen.listen()
dot = Turtle('turtle')
dot.speed('fastest')
dot.penup()
dot.setpos(randint(-WIDTH/2, WIDTH/2), randint(-HEIGHT/2, HEIGHT/2))
wall = Turtle(visible=False)
score = Turtle(visible=False)
wall.speed('fastest')
wall.penup()
wall.goto(WIDTH/2, HEIGHT/2)
wall.pendown()
for _ in range(4):
wall.right(90)
wall.forward(400)
try:
with open('score.txt','r') as file:
highScore = int(file.read())
except:
with open('score.txt','w') as file:
file.write('0')
highScore = 0
wall.penup()
wall.forward(50)
wall.write("0" + ' score', font=FONT)
score.penup()
score.setpos(wall.pos())
score.seth(270)
score.fd(30)
score.write(str(highScore) + ' high score', font=FONT)
score.right(180)
score.fd(30)
char = Turtle(visible=False)
char.speed('fastest')
moveChar()
screen.mainloop()
if points > highScore:
with open('score.txt','w') as file:
file.write(str(points))
print('new high score of ' + str(score) + ' saved')
print(posList)
I can't reproduce your error but let's try something. Since the turtle coordinate system is floating point, let's assume that small errors are creeping into the fractional portion of your positions that are preventing this test from working:
return pos in poslist
So, let's change that line to:
return (int(pos[0]), int(pos[1])) in poslist
And change the matching line in moveChar():
# adds current location to the list
posList.append((int(tempPos[0]), int(tempPos[1])))
Now we're only storing and testing the integer portion of the position so any fractional component will be ignored. Try this and let us know what happens.

How to speed up python's 'turtle' function and stop it freezing at the end

I have written a turtle program in python, but there are two problems.
It goes way too slow for larger numbers, I was wonder how I can speed up turtle.
It freezes after it finishes and when clicked on, says 'not responding'
This is my code so far:
import turtle
#Takes user input to decide how many squares are needed
f=int(input("How many squares do you want?"))
c=int(input("What colour would you like? red = 1, blue = 2 and green =3"))
n=int(input("What background colour would you like? red = 1, blue = 2 and green =3"))
i=1
x=65
#Draws the desired number of squares.
while i < f:
i=i+1
x=x*1.05
print ("minimise this window ASAP")
if c==1:
turtle.pencolor("red")
elif c==2:
turtle.pencolor("blue")
elif c==3:
turtle.pencolor("green")
else:
turtle.pencolor("black")
if n==1:
turtle.fillcolor("red")
elif n==2:
turtle.fillcolor("blue")
elif n==3:
turtle.fillcolor("green")
else:
turtle.fillcolor("white")
turtle.bk(x)
turtle.rt(90)
turtle.bk(x)
turtle.rt(90)
turtle.bk(x)
turtle.rt(90)
turtle.bk(x)
turtle.rt(90)
turtle.up()
turtle.rt(9)
turtle.down()
By the way: I am on version 3.2!
Set turtle.speed("fastest").
Use the turtle.mainloop() functionality to do work without screen refreshes.
Disable screen refreshing with turtle.tracer(0, 0) then at the end do turtle.update()
Python turtle goes very slowly because screen refreshes are performed after every modification is made to a turtle.
You can disable screen refreshing until all the work is done, then paint the screen, it will eliminate the millisecond delays as the screen furiously tries to update the screen from every turtle change.
For example:
import turtle
import random
import time
screen = turtle.Screen()
turtlepower = []
turtle.tracer(0, 0)
for i in range(1000):
t = turtle.Turtle()
t.goto(random.random()*500, random.random()*1000)
turtlepower.append(t)
for i in range(1000):
turtle.stamp()
turtle.update()
time.sleep(3)
This code makes a thousand turtles at random locations, and displays the picture in about 200 milliseconds.
Had you not disabled screen refreshing with turtle.tracer(0, 0) command, it would have taken several minutes as it tries to refresh the screen 3000 times.
https://docs.python.org/2/library/turtle.html#turtle.delay
For reference, turtle being slow is an existing problem.
Even with speed set to max, turtle can take quite a long time on things like fractals.
Nick ODell reimplemented turtle for speed here: Hide Turtle Window?
import math
class UndrawnTurtle():
def __init__(self):
self.x, self.y, self.angle = 0.0, 0.0, 0.0
self.pointsVisited = []
self._visit()
def position(self):
return self.x, self.y
def xcor(self):
return self.x
def ycor(self):
return self.y
def forward(self, distance):
angle_radians = math.radians(self.angle)
self.x += math.cos(angle_radians) * distance
self.y += math.sin(angle_radians) * distance
self._visit()
def backward(self, distance):
self.forward(-distance)
def right(self, angle):
self.angle -= angle
def left(self, angle):
self.angle += angle
def setpos(self, x, y = None):
"""Can be passed either a tuple or two numbers."""
if y == None:
self.x = x[0]
self.y = x[1]
else:
self.x = x
self.y = y
self._visit()
def _visit(self):
"""Add point to the list of points gone to by the turtle."""
self.pointsVisited.append(self.position())
# Now for some aliases. Everything that's implemented in this class
# should be aliased the same way as the actual api.
fd = forward
bk = backward
back = backward
rt = right
lt = left
setposition = setpos
goto = setpos
pos = position
ut = UndrawnTurtle()

Categories

Resources