Cannot re-enter readline python

Question:

I’m making a basic Turtle game where you go go through a maze and get to the cheese, there’s an issue with my win condition though, I know what the problem is, it’s that the project is attempting to run an input when already expecting user input. I’m guessing that’s because the win() function gets called multiple times before the user actually responds. I’ve tried multiple ways of fixing this to no avail. Here’s my code:

import turtle
import datetime

won = False

turtle.setup(800, 590)

mouse_l = "mouse_l.gif"
mouse_r = "mouse_r.gif"
mouse_u = "mouse_u.gif"
mouse_d = "mouse_d.gif"
map = "map.gif"

turtle.setup(800, 590)
turtle.tracer(0, 0)
turtle.delay(0)

FRAME_TIME = 5 # in ms

wn = turtle.Screen()

wn.bgpic(map)
wn.register_shape(mouse_d)
wn.register_shape(mouse_r)
wn.register_shape(mouse_l)
wn.register_shape(mouse_u)

wn.tracer(0, delay=0)
wn.delay(0)

player = turtle.Turtle()
player.speed(0)
player.penup()

player.shape(mouse_d)

start_time = datetime.datetime.now()

keys = {
    "w": False,
    "s": False,
    "a": False,
    "d": False
}

def check_win():
    return player.ycor() < -230 and player.xcor() > 335

def win():
    global won
    global start_time
    
    print("You won!")
    won_time = datetime.datetime.now()
    final_time = won_time.replace(microsecond=0) - start_time.replace(microsecond=0)
    print(f"Your time was {final_time}")
    if input("Do you want to play again? y/n ").lower() == "y":
        won = False
        player.goto(-352, 195)
        start_time = datetime.datetime.now()

def movement():
    global won
    
    if keys["w"]:
        player.seth(90)
        player.fd(6)
    if keys["s"]:
        player.seth(270)
        player.fd(6)
    if keys["a"]:
        player.seth(180)
        player.fd(6)
    if keys["d"]:
        player.seth(0)
        player.fd(6)
    
    turtle.update()
    
    won = check_win()
    
    if won == True:
        won = "winning"
        return win()
    
    return wn.ontimer(movement, FRAME_TIME)

def c_keys(key, value):
    keys[key] = value

player.goto(-352, 195)
turtle.update()

movement()

wn.onkeypress(lambda: c_keys("w", True), "w")
wn.onkeyrelease(lambda: c_keys("w", False), "w")
wn.onkeypress(lambda: c_keys("s", True), "s")
wn.onkeyrelease(lambda: c_keys("s", False), "s")
wn.onkeypress(lambda: c_keys("a", True), "a")
wn.onkeyrelease(lambda: c_keys("a", False), "a")
wn.onkeypress(lambda: c_keys("d", True), "d")
wn.onkeyrelease(lambda: c_keys("d", False), "d")

wn.listen()

Any help is appreciated. Thanks!

EDIT 1: Addressing Johnny Mopp’s comment (Rework win check in movement(), tried a few ways, still getting the issue)

import turtle
import datetime

turtle.setup(800, 590)

prompting = False

mouse_l = "mouse_l.gif"
mouse_r = "mouse_r.gif"
mouse_u = "mouse_u.gif"
mouse_d = "mouse_d.gif"
map = "map.gif"

turtle.setup(800, 590)
turtle.tracer(0, 0)
turtle.delay(0)

FRAME_TIME = 5 # in ms

wn = turtle.Screen()

wn.bgpic(map)
wn.register_shape(mouse_d)
wn.register_shape(mouse_r)
wn.register_shape(mouse_l)
wn.register_shape(mouse_u)

wn.tracer(0, delay=0)
wn.delay(0)

player = turtle.Turtle()
player.speed(0)
player.penup()

player.shape(mouse_d)

start_time = datetime.datetime.now()

keys = {
    "w": False,
    "s": False,
    "a": False,
    "d": False
}

def won():
    return player.ycor() < -230 and player.xcor() > 335

def play_again():
    global start_time
    global prompting

    print("playing again", prompting)
    
    if prompting:
        return

    prompting = True
    
    print("You won!")
    won_time = datetime.datetime.now()
    final_time = won_time.replace(microsecond=0) - start_time.replace(microsecond=0)
    print(f"Your time was {final_time}")
    if input("Do you want to play again? y/n ").lower() == "y":
        player.goto(-352, 195)
        start_time = datetime.datetime.now()

def movement(): 
    if keys["w"]:
        player.seth(90)
        player.fd(6)
    if keys["s"]:
        player.seth(270)
        player.fd(6)
    if keys["a"]:
        player.seth(180)
        player.fd(6)
    if keys["d"]:
        player.seth(0)
        player.fd(6)
    
    turtle.update()

    if won():
        return play_again()
    
    return wn.ontimer(movement, FRAME_TIME)

def c_keys(key, value):
    keys[key] = value

player.goto(-352, 195)
turtle.update()

movement()

wn.onkeypress(lambda: c_keys("w", True), "w")
wn.onkeyrelease(lambda: c_keys("w", False), "w")
wn.onkeypress(lambda: c_keys("s", True), "s")
wn.onkeyrelease(lambda: c_keys("s", False), "s")
wn.onkeypress(lambda: c_keys("a", True), "a")
wn.onkeyrelease(lambda: c_keys("a", False), "a")
wn.onkeypress(lambda: c_keys("d", True), "d")
wn.onkeyrelease(lambda: c_keys("d", False), "d")

wn.listen()

EDIT 2: Original error message

You won!
Your time was 0:00:03
Exception in Tkinter callback
Traceback (most recent call last):
  File "/nix/store/2vm88xw7513h9pyjyafw32cps51b0ia1-python3-3.8.12/lib/python3.8/tkinter/__init__.py", line 1892, in __call__
    return self.func(*args)
  File "/nix/store/2vm88xw7513h9pyjyafw32cps51b0ia1-python3-3.8.12/lib/python3.8/tkinter/__init__.py", line 814, in callit
    func(*args)
  File "main.py", line 80, in movement
    if won() and not play_again():
  File "main.py", line 57, in play_again
    if input("Do you want to play again? y/n ").lower() == "y":
RuntimeError: can't re-enter readline
Asked By: bread

||

Answers:

Try something like this:

def play_again():
    global start_time
   
    print("You won!")
    won_time = datetime.datetime.now()
    final_time = won_time.replace(microsecond=0) - start_time.replace(microsecond=0)
    print(f"Your time was {final_time}")

    if input("Do you want to play again? y/n ").lower() == "y":
        player.goto(-352, 195)
        start_time = datetime.datetime.now()
        return True
    else:
        return False

def movement(): 
    # Other stuff removed for brevity
    #.......

    if won() and not play_again():
        return
    
    return wn.ontimer(movement, FRAME_TIME)
Answered By: Johnny Mopp
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.