Search code examples
python-3.xkey-bindingsturtle-graphics

Get keybindings to work in Python turtle


I'm trying to make a treasure hunt type game with Python and turtle
The goal is that a random step will be written on the screen the user will then use the arrow keys to follow the directions and press enter when done, then another step will be displayed

I'm coding it using trinket.io

I know that I have to have some way to stop the program but still allow the user to move the turtle with the arrows after each step but what I've tried so far hasn't worked.

Here is the program so far:

################################################
##  This program simulates following a series ## 
##  of directions in a random order to see if ##
##  You will end up at the same place         ##
################################################

import turtle
import random

#############################
### Variable declareation ###
#############################

unit = 10 # this is the distance the turtle will move each time a key is pressed
hunt1 = turtle.Turtle() # turtle used in the first hunt
hunt2 = turtle.Turtle()# turtle used in the sedond hunt
hunt3 = turtle.Turtle()#turtle used in the third hunt

directions = turtle.Turtle()#used to print the steps
screen = turtle.Screen()# creates a Screen

koopa = [hunt1,hunt2,hunt3] # a collection of 'hunt' turtles
color = ['black','green','red']

map = ['Step 1', 'step 2', 'step3'] #this holds all the steps in the treasure hunt
x = random.randint(-50,0)
y = random.randint(-50,0)

next_step = False
#################
##  Functions  ##
#################
def set_koopa():
  c=0
  for i in koopa:
    i.penup()
    i.goto(x,y)
    i.color(color[c])
    i.pendown()
    c=c+1

def east():
  k.setheading(180)
  k.forward(unit)

def west():
  k.setheading(0)
  k.forward(unit)

def north():
  k.setheading(90)
  k.forward(unit)

def south():
  k.setheading(270)
  k.forward(unit)

def enter():
  next_step = True

##################
## key bindings ##
##################
screen.onkey(east, 'left')
screen.onkey(west, 'right')
screen.onkey(north, 'up')
screen.onkey(south, 'down')
screen.onkey(enter, 'enter')
screen.listen()# tells the compouter to listen for keystrokes

z=0 #debug
############
##  Main  ##
############
directions.goto(0,0)
directions.ht()
set_koopa()
for k in koopa:
  while len(map)!=0:   # checks if there are items left in the list
    l = len(map)       # returns the number if items
    directions.write(map.pop(random.randint(0,l-1))) #randomly takes an item and removes it and writes in on the screen
    while not next_step:  #gives time for user to move
      z=z+1#debug
      print z  #debug
      print next_step #debug

    directions.clear()

The code in question is at the bottom.

The first thing I tried didn't have the loop:

while not next_step:  #gives time for user to move
    z=z+1#debug
    print z  #debug
    print next_step #debug

Instead, I had the code asking for input, but that didn't allow the user to move the turtle.


Solution

  • I don't believe your code would work under the regular turtle that comes with standard Python. First, you tagged [python-3.x] but you use Python 2 style print statements.

    But more importantly, your event logic is misguided. You shouldn't spin in a tight loop waiting for a turtle event:

    while not next_step:  #gives time for user to move
          z=z+1#debug
          print z  #debug
          print next_step #debug
    

    as you're likely blocking out events by not turning control over to the event handler. Plus other little errors in the code (eg. a missing global statement.) Below is my rework that roughly operates under standard Python 2 or Python 3 turtle:

    ################################################
    ##  This program simulates following a series ##
    ##  of directions in a random order to see if ##
    ##  you will end up at the same place         ##
    ################################################
    
    from turtle import Turtle, Screen, mainloop
    from random import randint, choice
    
    #############################
    ### Variable declarations ###
    #############################
    
    unit = 10  # this is the distance the turtle will move each time a key is pressed
    
    directions = Turtle(visible=False)  # used to print the steps
    directions.penup()
    
    screen = Screen()  # creates a Screen
    
    koopa = {  # a collection of 'hunt' turtles
        'black': Turtle(visible=False),  # turtle used in the first hunt,
        'green': Turtle(visible=False),  # turtle used in the second hunt, 
        'red': Turtle(visible=False),  # turtle used in the third hunt,
    }
    
    steps = ['Step 1', 'Step 2', 'Step 3']  # this holds all the steps in the treasure hunt
    
    x = randint(-50, 0)
    y = randint(-50, 0)
    
    next_step = True
    
    active = None
    
    #################
    ##  Functions  ##
    #################
    
    def set_koopa():
        for color, hunt in koopa.items():
            hunt.penup()
            hunt.goto(x, y)
            hunt.color(color)
            hunt.pendown()
    
    def east():
        active.setheading(0)
        active.forward(unit)
    
    def west():
        active.setheading(180)
        active.forward(unit)
    
    def north():
        active.setheading(90)
        active.forward(unit)
    
    def south():
        active.setheading(270)
        active.forward(unit)
    
    def enter():
        global next_step
    
        next_step = True
    
    ##################
    ## key bindings ##
    ##################
    screen.onkey(east, 'Right')
    screen.onkey(west, 'Left')
    screen.onkey(north, 'Up')
    screen.onkey(south, 'Down')
    screen.onkey(enter, 'Return')
    screen.listen()  # tells the computer to listen for keystrokes
    
    ############
    ##  Main  ##
    ############
    
    set_koopa()
    
    def check_step():
        global active, next_step
    
        if next_step:
            next_step = False
    
            directions.clear()
    
            active = choice(list(koopa.values()))  # or somesuch
    
            active.showturtle()
    
            if steps:
                l = len(steps)  # returns the number if items
    
                directions.write(steps.pop(randint(0, l - 1)))  # randomly takes an item and removes it and writes in on the screen
    
            else:
                return
    
        screen.ontimer(check_step, 100)
    
    check_step()
    
    mainloop()