making an endpoint in turtle graphics - python

I'm making a maze with turtle graphics for a class project and I have one more main thing to complete before I'm finished...
I've created a second "turtle" to make a box at the endpoint. So the objective is to finish the maze and get the turtle in the box. But I am unsure how to make the box an actual endpoint and have a message pop up.
Here is my code:
from turtle import Turtle, Screen
screen = Screen()
screen.setup(650, 850)
screen.title("Turtle Keys")
screen.bgpic('scooby_doo_maze.gif')
move = Turtle(shape="triangle")
move.penup()
move.setx(-150)
move.sety(200)
move.pendown()
move.pensize(5)
box = Turtle(shape="square")
box.hideturtle()
box.speed(0)
box.penup()
box.setx(150)
box.sety(-190)
box.pendown()
box.right(90)
box.forward(100)
box.right(90)
box.forward(100)
box.right(90)
box.forward(100)
box.right(90)
box.forward(100)
def keyUp():
move.forward(12)
def keyLeft():
move.left(90)
def keyRight():
move.right(90)
def keyDown():
move.backward(12)
def keyReset():
move.reset()
move.penup()
move.setx(-150)
move.sety(200)
move.pendown()
move.pensize(5)
screen.onkey(keyUp, "Up")
screen.onkey(keyLeft, "Left")
screen.onkey(keyRight, "Right")
screen.onkey(keyDown, "Down")
screen.onkey(keyReset, "r")
screen.listen()
screen.exitonclick()

We just need to add few features. First, instead of drawing the end point with turtle box, we make turtle box the endpoint by expanding the turtle itself via box.shapesize(). This way, we can use move.distance(box) to determine if move is near the center of box.
Second, we need a function called by all the movement functions to test if above distance is close enough and then invoke the following:
Third, we introduce screen.textinput() to let the play know they've succeeded and offer then the option to play again, or quit. I've reworked your code below to introduce these additions and tweak it a bit for style:
from turtle import Turtle, Screen
screen = Screen()
screen.setup(650, 850)
screen.title("Turtle Keys")
screen.bgpic('scooby_doo_maze.gif')
def insideBox():
if move.distance(box) < 60:
play_again = screen.textinput("Success!", "Play again?")
if play_again and play_again.lower().startswith('y'):
keyReset()
else:
screen.bye()
def keyUp():
move.forward(12)
insideBox()
def keyLeft():
move.left(90)
def keyRight():
move.right(90)
def keyDown():
move.backward(12)
insideBox()
def keyReset():
move.reset()
move.penup()
move.goto(-150, 200)
move.pendown()
move.pensize(5)
screen.listen() # it's here because screen.textinput() unsets it
screen.onkey(keyUp, "Up")
screen.onkey(keyLeft, "Left")
screen.onkey(keyRight, "Right")
screen.onkey(keyDown, "Down")
screen.onkey(keyReset, "r")
move = Turtle(shape="triangle")
keyReset()
box = Turtle(shape="square")
box.color("black", "white")
box.shapesize(5, 5, 5)
box.penup()
box.goto(150, -190)
screen.mainloop()
This is a situation where I would avoid screen.exitonclick() as you need to click the window to get it to listen and easily end up closing it! Using screen.mainloop() should be sufficient and let the user close the window by not choosing to play again or using the window controls.

Related

Why won't Python Turtle respond to my movement inputs?

I'm taking a coding course this semester and for one of the projects it requires us to make a game of any kind. I chose to make a game contained within a box where enemies move on screen travel completely across then appear again at a different x or y coordinate to add some variety. The issue I'm dealing with is following adding the code needed to create "enemies" the keys I set for the movement of the player turtle no longer work, another strange thing is that the player turtle will spin in place right as the program is run. I am at a total loss as to how to make the turtle respond to the code as it worked correctly prior to writing the code for the enemies. I don't believe the code for the enemies is the issue as when it is commented out it still acts the same way. I didn't include the enemies code for the sake of brevity. this code is done in Python 3. Please help if you can!
EDIT: I removed the parenthesis following the window.onkeypress(mov_x,"x") and after adding the code back in for the enemies it won"t respond, it works without the code for the enemies but that kinda removes the point of the game. Thank you for the help!
import turtle
import random
#screen
window = turtle.Screen()
window.title("Final Project Game")
window.bgcolor("gray")
window.setup(width=600,height=600)
#player
t= turtle.Turtle()
t.speed(5)
t.shape("triangle")
t.color("blue")
t.penup()
#player movement
def mov_rt():
t.seth(0)
t.fd(20)
def mov_lt():
t.seth(180)
t.fd(20)
def mov_up():
t.seth(90)
t.fd(20)
def mov_dw():
t.seth(270)
t.fd(20)
window.onkeypress(mov_rt,"d")
window.onkeypress(mov_lt,"a")
window.onkeypress(mov_up,"w")
window.onkeypress(mov_dw,"s")
window.listen()
#enemies
enemies = []
turt_num = turtle.numinput("Final","Number of Enemies", default=5, minval=1,maxval=10)
e_dir= [0,90,180,270]
if turt_num == 1:
e1= turtle.Turtle("square",visible=False)
e1.speed(5)
e1.color("red")
e1.penup()
e1.setpos(random.randint(-290,290),random.randint(-290,290))
e1.seth(random.choice(e_dir))
enemies.append(e1)
e1.st()
elif turt_num == 2:
e1= turtle.Turtle("square",visible=False)
e1.speed(5)
e1.color("red")
e1.penup()
e1.setpos(random.randint(-290,290),random.randint(-290,290))
e1.seth(random.choice(e_dir))
enemies.append(e1)
e2= turtle.Turtle("square",visible=False)
e2.speed(5)
e2.color("red")
e2.penup()
e2.setpos(random.randint(-290,290),random.randint(-290,290))
e2.seth(random.choice(e_dir))
enemies.append(e2)
e1.st()
e2.st()
elif turt_num ==3:
e1= turtle.Turtle("square",visible=False)
e1.speed(5)
e1.color("red")
e1.penup()
e1.setpos(random.randint(-290,290),random.randint(-290,290))
e1.seth(random.choice(e_dir))
enemies.append(e1)
e2= turtle.Turtle("square",visible=False)
e2.speed(5)
e2.color("red")
e2.penup()
e2.setpos(random.randint(-290,290),random.randint(-290,290))
e2.seth(random.choice(e_dir))
enemies.append(e2)
e3= turtle.Turtle("square",visible=False)
e3.speed(5)
e3.color("red")
e3.penup()
e3.setpos(random.randint(-290,290),random.randint(-290,290))
e3.seth(random.choice(e_dir))
enemies.append(e3)
e1.st()
e2.st()
e3.st()
#borders
def border(): #if you hold down the button it wont reappear bc youre still moving while the turtle is trying to move to the desired target
tx, ty= t.pos()
if t.xcor() >295:
t.ht()
t.setpos(-295,ty)
t.st()
if t.xcor() <-295:
t.ht()
t.setpos(295,ty)
t.st()
if t.ycor() >295:
t.ht()
t.setpos(tx,-295)
t.st()
if t.ycor() <-295:
t.ht()
t.setpos(tx,295)
t.st()
#main game loop
while True:
window.update()
border()
turtle.mainloop()
Try to use
window.onkey()
That is a different way to listen and it does the exact same thing.
Also, remember at the end of that code you need
window.listen()
Common beginner's error. Instead of:
window.onkeypress(mov_rt(),"d")
window.onkeypress(mov_lt(),"a")
window.onkeypress(mov_up(),"w")
window.onkeypress(mov_dw(),"s")
Do:
window.onkeypress(mov_rt, "d")
window.onkeypress(mov_lt, "a")
window.onkeypress(mov_up, "w")
window.onkeypress(mov_dw, "s")
That is, you don't want to call your event handler function, you instead want to pass the name of your event hanlder function for the system to call later, when something actually happens.
Below is a rework of your code to address this as well as some other turtle and Python issues:
from turtle import Screen, Turtle
def border():
x, y = turtle.position()
if x > 295:
turtle.hideturtle()
turtle.setx(-295)
turtle.showturtle()
elif x < -295:
turtle.hideturtle()
turtle.setx(295)
turtle.showturtle()
if y > 295:
turtle.hideturtle()
turtle.sety(-295)
turtle.showturtle()
elif y < -295:
turtle.hideturtle()
turtle.sety(295)
turtle.showturtle()
# player movement
def mov_rt():
turtle.setheading(0)
turtle.forward(20)
border()
def mov_lt():
turtle.setheading(180)
turtle.forward(20)
border()
def mov_up():
turtle.setheading(90)
turtle.forward(20)
border()
def mov_dw():
turtle.setheading(270)
turtle.forward(20)
border()
screen = Screen()
screen.title("Final Project Game")
screen.bgcolor('gray')
screen.setup(width=600, height=600)
# player
turtle = Turtle()
turtle.speed('normal')
turtle.shape('triangle')
turtle.color('blue')
turtle.penup()
screen.onkeypress(mov_rt, 'd')
screen.onkeypress(mov_lt, 'a')
screen.onkeypress(mov_up, 'w')
screen.onkeypress(mov_dw, 's')
screen.listen()
screen.mainloop()
Note removal of while True: loop as it has no place in an event-driven environment like turtle -- it potentially blocks events from being processed.

Python turtle not moving on arrow press

I am trying to make my turtle (main_ship) move across the bottom of my screen according to when the user presses the left and right arrow keys but the turtle is not moving. I have used the same code before when making Pong so I'm not sure why it's not working.
import turtle
wn = turtle.Screen()
wn.title("Game")
wn.bgcolor("black")
wn.setup(width=800, height=600)
wn.tracer(0)
main_ship = turtle.Turtle()
main_ship.speed(0)
main_ship.shape("turtle")
main_ship.color("green")
main_ship.shapesize(stretch_wid=2, stretch_len=4)
main_ship.penup()
main_ship.goto(0, -290)
main_ship.left(90)
def main_ship_right():
x = main_ship.xcor()
x += 20
main_ship.setx(x)
def main_ship_left():
x = main_ship.xcor()
x -= 20
main_ship.setx(x)
while True:
wn.update()
wn.mainloop()
wn.listen()
wn.onkeypress(main_ship_right, "Right")
wn.onkeypress(main_ship_left, "Left")
When I press the arrow keys, nothing happens but the code still runs and there are no error messages.
You have to assign keys before mainloop() which runs all time till you close window.
You don't need while True because mainloop() already runs internal loop.
You may have to remove wm.tracer(0) or you will have to run wn.update() to refresh elements in window.
import turtle
# --- functions ---
def main_ship_right():
x = main_ship.xcor()
x += 20
main_ship.setx(x)
wn.update()
def main_ship_left():
x = main_ship.xcor()
x -= 20
main_ship.setx(x)
wn.update()
# --- main ---
wn = turtle.Screen()
wn.title("Game")
wn.bgcolor("black")
wn.setup(width=800, height=600)
wn.tracer(0)
main_ship = turtle.Turtle()
main_ship.speed(0)
main_ship.shape("turtle")
main_ship.color("green")
main_ship.shapesize(stretch_wid=2, stretch_len=4)
main_ship.penup()
main_ship.goto(0, -290)
main_ship.left(90)
wn.update()
wn.listen()
wn.onkeypress(main_ship_right, "Right")
wn.onkeypress(main_ship_left, "Left")
wn.mainloop()
For this style of motion, there's another way to implement it. The idea is to leave the cursor moving in it's original orientation, but use settiltangle() to make it look like it's facing upward.
This lets us use forward(20) and backward(20) to move our cursor, and not have to write:
x = main_ship.xcor()
x += 20
main_ship.setx(x)
Works great for Space Invader style games, where the player faces upwards but moves sideways:
from turtle import Screen, Turtle
from functools import partial
screen = Screen()
screen.title("Game")
screen.bgcolor('black')
screen.setup(width=800, height=600)
main_ship = Turtle('turtle')
main_ship.speed('fastest')
main_ship.color('green')
main_ship.shapesize(stretch_wid=2, stretch_len=4)
main_ship.settiltangle(90)
main_ship.penup()
main_ship.sety(-290)
screen.onkeypress(partial(main_ship.forward, 20), 'Right')
screen.onkeypress(partial(main_ship.backward, 20), 'Left')
screen.listen()
screen.mainloop()
Only works in the turtle library that comes with Python -- online Python development sites usually provide limited turtle implementations that don't include methods like settiltangle().

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 ;)

My Python turtle window crashes when using onkey event

My Python turtle window crashes when I try to move to the left. I am using the onkey in Python turtle graphics and when I try to use it my window completely crashes. Here is my code:
import turtle
wn = turtle.Screen()
wn.bgcolor("green")
crosshair = turtle.Turtle()
crosshair.shape("circle")
draw = turtle.Pen()
draw.color("brown")
draw.ht()
while True:
def left():
draw.st()
draw.penup()
draw.left(90)
draw.forward(50)
draw.pendown()
draw.forward(100)
draw.left(90)
draw.forward(50)
draw.right(90)
draw.forward(100)
draw.right(90)
draw.forward(100)
draw.right(90)
draw.forward(100)
draw.right(90)
draw.forward(50)
turtle.listen()
turtle.onkey(left, "Left")
Your code isn't structured properly. You should not have an infinite while True: loop when using turtle. Instead, you should set up your event handlers, like onkey(), and turn control over to Tk's event loop using mainloop(). Python-wise, you don't need to define left() inside the loop, move it to the top level. Here's a rework that includes the above changes:
from turtle import Turtle, Screen
def left():
draw.st()
draw.penup()
draw.left(90)
draw.forward(50)
draw.pendown()
draw.forward(100)
draw.left(90)
draw.forward(50)
draw.right(90)
for _ in range(3):
draw.forward(100)
draw.right(90)
draw.forward(50)
draw.ht()
screen = Screen()
screen.bgcolor("green")
crosshair = Turtle("circle")
draw = Turtle(visible=False)
draw.color("brown")
screen.onkey(left, "Left")
screen.listen()
screen.mainloop()
Click on the window to make it active, then you can use your left arrow key to invoke the graphics:

Python - Keyboard Multiple Turtle Objects

I would like to create a program in which a Turtle object responds to key presses. I can do this, but I can't seem to understand how to move a second Turtle object, which is controlled by the computer, while the first one is moving. Any help would be appreciated.
Here is my code:
from turtle import *
from Tkinter import Tk
root = Tk()
root.withdraw()
turtle = Turtle()
def h1():turtle.forward(10)
def h2():turtle.left(45)
def h3():turtle.right(45)
def h4():turtle.back(10)
def h5(root=root):root.quit()
onkey(h1,"Up")
onkey(h2,"Left")
onkey(h3,"Right")
onkey(h4,"Down")
onkey(h5,"q")
listen()
root.mainloop()
Insert a second turtle before listen() that moves with keys w,a,s,d:
turtle2 = Turtle()
def h11():turtle2.forward(10)
def h21():turtle2.left(45)
def h31():turtle2.right(45)
def h41():turtle2.back(10)
onkey(h11,"w")
onkey(h21,"a")
onkey(h31,"d")
onkey(h41,"s")
I can't seem to understand how to move a second Turtle object, which
is controlled by the computer, while the first one is moving.
Below is some minimal code that does as you describe. Green turtle Pokey is computer controlled while red turtle Hokey is user controlled (click on the window first so your keystrokes are heard):
from turtle import Turtle, Screen
def move_pokey():
pokey.forward(10)
x, y = pokey.position()
if not (-width/2 < x < width/2 and -height/2 < y < height/2):
pokey.undo()
pokey.left(90)
screen.ontimer(move_pokey, 100)
hokey = Turtle(shape="turtle")
hokey.color("red")
hokey.penup()
pokey = Turtle(shape="turtle")
pokey.setheading(30)
pokey.color("green")
pokey.penup()
screen = Screen()
width = screen.window_width()
height = screen.window_height()
screen.onkey(lambda: hokey.forward(10), "Up")
screen.onkey(lambda: hokey.left(45), "Left")
screen.onkey(lambda: hokey.right(45), "Right")
screen.onkey(lambda: hokey.back(10), "Down")
screen.onkey(screen.bye, "q")
screen.listen()
screen.ontimer(move_pokey, 100)
screen.mainloop()
This is not finished code (shutdown of the timer event should be cleaner, Hokey's handlers should lock out additional events while running, etc.) but should give you a basic idea of how to go about it.

Categories