Q:

Python Turtle - Unable to restrict turtle within a border

I'm trying to make a turtle stop moving when it hits the border until it changes it's heading. 
from turtle import *
import turtle
import time

user_speed = 46
border_pen = Turtle()
shouldMove = True
#alreadyStoppedDown = False
#alreadyStoppedUp = False
prev_heading = heading()

#Set up border
def setup(turt):
    turt.ht()
    turt.pu()
    turt.speed(0)
    turt.goto(-250,250)
    turt.pd()
    turt.begin_fill()
    for x in range(4):
        turt.fd(500)
        turt.rt(90)
    turt.color('black')
    turt.end_fill()

#Set up user
def user():
    pu()
    shape('square')
    shapesize(2)
    color('orange')
    speed(0)
    goto(-230,230)
    speed(1)

#Keybind functions
def down():
    global prev_heading
    prev_heading = heading()
    speed(0)
    setheading(-90)
    speed(1)
def up():
    global prev_heading
    prev_heading = heading()
    speed(0)
    setheading(90)
    speed(1)
def left():
    global prev_heading
    prev_heading = heading()
    speed(0)
    setheading(180)
    speed(1)
def right():
    global prev_heading
    prev_heading = heading()
    speed(0)
    setheading(0)
    speed(1)

#Border restriction
def restrict():
    global shouldMove
    if ycor() <= -230:
        shouldMove = False
        global prev_heading
        prev_heading = heading()

def main():
    global shouldMove
    while True:
        #Debugging
        print(shouldMove)
        print(heading(),prev_heading,ycor())

        if shouldMove:
            fd(user_speed)
            restrict()
        else:
            if heading() != prev_heading:
                shouldMove = True
            else:
                continue

setup(border_pen)
user()

onkey(up,'w')
onkey(down,'s')
onkey(left,'a')
onkey(right,'d')
listen()

main()

I've only set up the restrictions for the bottom part of the border.
I am able to make the turtle stop moving, but it wont change it's heading when it stops moving. I try to change it's heading by pressing wasd, but it just stays there until the window crashes. I've tried to think of multiple solutions but I'm unable to. The main issue here is when the turtle's ycor() is less than or equal to -230, it stops but it does not change it's heading.
When I change main() such that it set's it's heading to 0 when it ycor() is less than or equal to -230, it will move forward to the right once and since the ycor() is still -230, it will stop moving.
def main():
    while True:
        #Debugging
        print(shouldMove)
        print(heading(),prev_heading,ycor())

        if shouldMove:
            fd(user_speed)
            restrict()
        else:
            if heading() != prev_heading:
                shouldMove = True
            else:
                setheading(0)

I'm doing this for fun, I watched The Coding Train code a snake game using processing and I'm trying to recreate it in Python using the turtle module. Yes, I'm a beginner. I want to attempt to remake the snake game using the turtle module, even though I can use other modules like PyGame as I'm not familiar with PyGame. Any solutions to this problem? Or am I not able to do this using the turtle module?

A:

The primary problem I see with your code is while True: which has no place in an event driven world.  (As it can block out events!)  We'll use a ontimer() event instead.  The next problem is the control structure is way too complicated.  We'll toss most of it and just keep the boolean shouldMove for control.  Finally, you're mixing the functional interface to turtle with the object-oriented interface to turtle.  We'll change the import to only allow the object-oriented interface:
from turtle import Turtle, Screen

USER_SPEED = 20

WIDTH = 500
CURSOR_SIZE = 20
CURSOR_MULTIPLIER = 2
MINIMUM = ((CURSOR_SIZE * CURSOR_MULTIPLIER) / 2) - WIDTH / 2
MAXIMUM = WIDTH / 2 - ((CURSOR_SIZE * CURSOR_MULTIPLIER) / 2)

# Set up border
def setup(turtle):
    turtle.penup()
    turtle.speed('fastest')
    turtle.goto(-WIDTH / 2, WIDTH / 2)
    turtle.pendown()

    turtle.begin_fill()
    for _ in range(4):
        turtle.forward(WIDTH)
        turtle.right(90)
    turtle.end_fill()

# Set up user
def user(turtle):
    turtle.penup()
    turtle.shapesize(CURSOR_MULTIPLIER)
    turtle.color('orange')
    turtle.speed('fastest')

# Keybind functions
def down():
    global shouldMove

    if user_pen.heading() != 270:
        user_pen.setheading(270)
        shouldMove = True

def up():
    global shouldMove

    if user_pen.heading() != 90:
        user_pen.setheading(90)
        shouldMove = True

def left():
    global shouldMove

    if user_pen.heading() != 180:
        user_pen.setheading(180)
        shouldMove = True

def right():
    global shouldMove

    if user_pen.heading() != 0:
        user_pen.setheading(0)
        shouldMove = True

# Border restriction

def move():
    global shouldMove

    if shouldMove:
        user_pen.forward(USER_SPEED)

        if not(MINIMUM <= user_pen.ycor() <= MAXIMUM and MINIMUM <= user_pen.xcor() <= MAXIMUM):
            user_pen.undo()
            shouldMove = False

    screen.ontimer(move, 25)

screen = Screen()

border_pen = Turtle(visible=False)
user_pen = Turtle('square')

setup(border_pen)
user(user_pen)

screen.onkey(up, 'w')
screen.onkey(down, 's')
screen.onkey(left, 'a')
screen.onkey(right, 'd')
screen.listen()

shouldMove = True

move()

screen.mainloop()

This should behave like you describe.  The turtle will keep moving in a straight line until you hit a direction key or it stops at a border.

