How to fix a RecursionError in Turtle?

Question:

I’m trying to write a program with Python to emulate an ‘old’ online game in which you drive a worm through the screen with some inputs from the keyboard.

import turtle

# Set screen and background
wn = turtle.Screen()
wn.title("Turn with Left and Right buttons your keyboard. Click on screen to EXIT.")
wn.bgcolor("black")

# Snake settings
snake = turtle.Turtle()
snake.color("purple")
snake.shape("circle")
snake.shapesize(0.25,0.25)
snake.pensize(5)

snake.speed(10)
t = 0
# Define Go loop, turn Left and Right       
def go():
    t = 0
    while t < 1000:
        snake.forward(1)
        t += 1

def left():
    snake.circle(1,8)
    go()

def right():
    snake.circle(1,-8)
    go()


# Inputs and Exit on click
wn.onkey(right, "Right")
wn.onkeypress(right, "Right")
wn.onkey(left, "Left")
wn.onkeypress(left, "Left")
wn.listen()
wn.exitonclick()

turtle.done()

The problem here is that, after some moves, the program crashes returning:

RecursionError: maximum recursion depth exceeded while calling a Python object.

I’m still a beginner so i don’t get what I’m doing wrong. How can I fix the error?

Asked By: Mira

||

Answers:

From testing, what is apparent is that if your go function hasn’t completed, and you are still holding down a key, it’s called again, meaning that now there’s two go functions on the call stack (plus a bunch of other functions like eventfun and update). Because holding the key calls the go function many times a second, and the fact that the go function takes 8+ seconds to complete, you’re filling up the call stack with calls to go, hence the crash. The call stack is just something like this repeated over and over:

update, __init__.py:1314
_update, turtle.py:561
_update, turtle.py:2663
_goto, turtle.py:3197
_go, turtle.py:1606
forward, turtle.py:1638
go, script.py:21
left, script.py:26
eventfun, turtle.py:700
__call__, __init__.py:1892

Even holding down left for just 3 seconds, my call stack grows to over 600, which is really high.

To fix this you could make it so that left or right cannot be called again while that key is still held down. That’s one possible way.

Answered By: Random Davis

What you’re experiencing is a faux recursion due to events stacking. However, your code design:

while t < 1000:
    snake.forward(1)
    t += 1

actually relies on event stacking! That is, you expect left and right commands to come in during the go() portion of the event handler which keeps the turtle moving. This is a problematic design. Let’s rework your code using an event timer instead:

from turtle import Screen, Turtle

def go():
    snake.forward(1)
    screen.ontimer(go, 50)

def left():
    screen.onkeypress(None, 'Left')  # disable handler inside handler
    snake.circle(1, 8)
    screen.onkeypress(left, 'Left')  # reenable handler

def right():
    screen.onkeypress(None, 'Right')
    snake.circle(1, -8)
    screen.onkeypress(right, 'Right')

screen = Screen()
screen.title("Turn with Left and Right buttons your keyboard. Click on screen to EXIT.")
screen.bgcolor('black')

snake = Turtle()
snake.color('purple')
snake.shape('circle')
snake.shapesize(0.25)
snake.pensize(5)
snake.speed('fastest')

screen.onkeypress(left, 'Left')
screen.onkeypress(right, 'Right')
screen.listen()

go()

screen.exitonclick()
Answered By: cdlane