1

I'm trying to make a game where the turtle needs to be able register and respond to user inputs (Simplified, a letter will appear and the user needs to click it on the keyboard. So if it shows "b" the user types "b"). I only added the letters a to f to make it simpler during the test. Each of them has a function that that'll execute when the letter has been pressed and the program is listening to it.

Everything worked out fine until I added a while function. Currently there is nothing in the while function (Asides from pass) but after I made it the code will no longer respond to user inputs. Could someone tell me how to fix this? My end goal is to have the program always listening for user input while a while loop runs and does its code. Below is my current code

import signal, turtle

def timeout_handler(signal, frame): # End of timer function
    raise Exception('Time is up!')

signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(10) # Number inside is how long the game will last.

def hit_a():
    print("a registered")
def hit_b():
    print("b registered")
def hit_c():
    print("c registered")
def hit_d():
    print("d registered")
def hit_e():
    print("e registered")
def hit_f():
    print("f registered")

turtle.onkey(hit_a, "a")
turtle.onkey(hit_b, "b")
turtle.onkey(hit_c, "c")
turtle.onkey(hit_d, "d")
turtle.onkey(hit_e, "e")
turtle.onkey(hit_f, "f")
turtle.listen()


while True:
    pass
    # Add program here
turtle.mainloop()

EDIT: I need a while code in there or at least a loop to keep a section repeating. In pseudocode it looks like this:

Several turtles each write one letter somewhere on the screen
Program waits for user to input a letter
Award/deduct points based on if they got it right or wrong
go back to line 2 if they got it wrong.
repeat

I plan on adding more stuff but I first need to get the base game going. The only way I know on how to code this is by using a while loop. But using one appears to be impossible since it prevents the program from listening to user inputs. Note that I need this all on turtle, not on the terminal (Command prompt if on windows) As I will colour code the letters to show which one to avoid and which ones to enter. How should I write this?

I also want to quickly mention that I have "avoided" this problem before. In the code below the program responds to user inputs when it's in the while loop. (Ignore the problems and the functions onkey() is assigned to. The idea is that the program responds while in the loop). However, I can't find out why in this code the program responds while in the loop but in the code above it doesn't register any user input

turtle.onkey(lower_game1_number, "s")
turtle.onkey(increase_game1_number, "w")
turtle.listen(xdummy=None, ydummy=None)

while True: # game1 is a turtle
    game1.write(first_game_num_display,False,"center",("Arial",30))
    game1_timer = random.randint(2,4)
    time.sleep(game1_timer)
    increase_game1_number()
    game1.undo()
    print(game1_timer)


mainloop()
Alex
  • 27
  • 7
  • 2
    The `turtle` module is based on the `tkinter` GUI module which is "user-event driven". This means scripts using either will "hang" whenever it isn't—and your `while` is preventing it from even starting. – martineau Feb 22 '19 at 01:10

1 Answers1

1

Along with the "never use while True: in an event-based environment" advice that @martineau provided, you don't need to drag the 'signal' library into the code as you can handle that with an ontimer() event.

I need a while code in there or at least a loop to keep a section repeating.

To address this, I've replaced my previous example code with a simple game that shows a letter and will keep moving it around the screen and changing it's color every two seconds, until you type that letter. Upon which, it will change to a different letter and continue on:

from turtle import Screen, Turtle
from random import shuffle, randrange
from itertools import cycle

WIDTH, HEIGHT = 600, 600

FONT_SIZE = 36
FONT = ('Arial', FONT_SIZE, 'bold')

LETTERS = list("abcdefghijklmnopqrstuvwxyz")

COLORS = ['red', 'blue', 'green', 'magenta', 'cyan', 'black', 'orange', 'gray']

def play():
    global letter

    if hit == letter:
        letter = next(letters)

    turtle.clear()
    turtle.color(next(colors))
    turtle.goto(randrange(FONT_SIZE - WIDTH/2, WIDTH/2 - FONT_SIZE), randrange(FONT_SIZE - HEIGHT/2, HEIGHT/2 - FONT_SIZE))
    turtle.write(letter, font=FONT)

    screen.ontimer(play, 2000)

letters = LETTERS
shuffle(letters)
letters = cycle(letters)
letter = next(letters)

colors = COLORS
shuffle(colors)
colors = cycle(colors)

hit = None

screen = Screen()
screen.setup(WIDTH, HEIGHT)

turtle = Turtle(visible=False)
turtle.penup()

for character in LETTERS:

    def hit_character(character=character):
        global hit

        hit = character

    screen.onkey(hit_character, character)

screen.listen()

play()

screen.mainloop()

Make sure to click on the window before typing to get it to listen for input.

Hopefully this will give you some ideas how to go about your more complex problem without using a while True: loop. Or at least show you how you might set up all your onkey() event assignments and handlers without rewriting the same code for every letter in the alphabet...

cdlane
  • 40,441
  • 5
  • 32
  • 81
  • I need a block of code that repeats. I made an edit explaining it in more depth. – Alex Feb 22 '19 at 01:52
  • @Alex, I've completely rewritten my example to try to address this issue. – cdlane Feb 22 '19 at 07:48
  • Thanks! Just a quick question, when I add another turtle there is a 0.5 delay for the turtle.goto() to assign it a coordinate. Sometimes it takes a whole second for the turtle.goto() line to assign both turtles a coordinate. Is there a way to speed it up? (The only thing I changed is adding a second turtle with the same commands as the first). And is there a way to save the coordinate? Thanks again! – Alex Feb 22 '19 at 19:30
  • @Alex, this is only meant to be a simple example -- your needs may need more coordination logic. The second argument to `ontimer()` is how long to wait before executing the code, you can try to tweak that. You could save the coordinates as globals -- ugly but realistic. – cdlane Feb 23 '19 at 18:21