Little black square in center of screen after each restart in turtle graphics pong game
Asked Answered
A

2

2

I tried to make a pong game on the turtle extension on Python and everything went smoothly.

I wanted to restart the game when a player reached an certain amount of points. The game restarts properly and the windows clears itself, except from a dark square in the center which stays during the next games. How can I get rid of that square?

Here is the the code where I restart the game:

import turtle

def run_game():
    wn = turtle.Screen()
    wn.title("Ping Pong Pour le Nuls")
    wn.bgcolor("green")
    wn.setup(width=800, height=600)
    wn.tracer(0)

    Player_1 = wn.textinput("Player 1", "Name of the player: ")
    Player_2 = wn.textinput("Player 2", "Name of the player: ")

    # Score
    score_a = 0
    score_b = 0
    # Paddle A
    paddle_a = turtle.Turtle()
    paddle_a.speed(0)
    paddle_a.shape("square")
    paddle_a.color("white")
    paddle_a.shapesize(stretch_wid=5, stretch_len=1)
    paddle_a.penup()
    paddle_a.goto(-350, 0)

    # Paddle B
    paddle_b = turtle.Turtle()
    paddle_b.speed(0)
    paddle_b.shape("square")
    paddle_b.color("white")
    paddle_b.shapesize(stretch_wid=5, stretch_len=1)
    paddle_b.penup()
    paddle_b.goto(350, 0)

    # Ball
    ball = turtle.Turtle()
    ball.speed(0)
    ball.shape("square")
    ball.color("white")
    ball.penup()
    ball.goto(0, 0)
    ball.dx = 0.4
    ball.dy = -0.4

    # Pen
    pen = turtle.Turtle()
    pen.speed(0)
    pen.color("white")
    pen.penup()
    pen.hideturtle()
    pen.goto(0, 260)
    pen.write(str(Player_1) + " : 0    " + str(Player_2) + " : 0    ", align="center", font=("Courier", 14, "normal"))


    # Funtion

    def paddle_a_up():
        y = paddle_a.ycor()
        y += 20
        paddle_a.sety(y)

    def paddle_a_down():
        y = paddle_a.ycor()
        y += -20
        paddle_a.sety(y)

    def paddle_b_up():
        y = paddle_b.ycor()
        y += 20
        paddle_b.sety(y)

    def paddle_b_down():
        y = paddle_b.ycor()
        y += -20
        paddle_b.sety(y)


    def pen_2():
        pen_2 = turtle.Turtle()
        pen_2.speed(0)
        pen_2.color("white")
        pen_2.penup()
        pen_2.hideturtle()
        pen_2.goto(0, 0)
        pen_2.write("Player 2 Wins", align="center", font=("Courier", 24, "normal"))

    # Keyboard binding

    wn.listen()
    wn.onkeypress(paddle_a_up, "w")
    wn.onkeypress(paddle_a_down, "x")
    wn.onkeypress(paddle_b_up, "Up")
    wn.onkeypress(paddle_b_down, "Down")

    # Main Game Loop
    while True:
        wn.update()

        # Move the ball
        ball.setx(ball.xcor() + ball.dx)
        ball.sety(ball.ycor() + ball.dy)


        # Border checking
        if ball.ycor() > 290:
            ball.sety(290)
            ball.dy *= -1

        if ball.ycor() < -290:
            ball.sety(-290)
            ball.dy *= -1

        if ball.xcor() > 390:
            ball.goto(0, 0)
            ball.dx *= -1
            score_a += 1
            pen.clear()
            pen.write(str(Player_1) + " : {}    ".format(score_a) + str(Player_2) + " : {}    ".format(score_b),
                      align="center", font=("Courier", 14, "normal"))


        if ball.xcor() < -390:
            ball.goto(0, 0)
            ball.dx *= -1
            score_b += 1
            pen.clear()
            pen.write(str(Player_1) + " : {}    ".format(score_a) + str(Player_2) + " : {}    ".format(score_b),
                      align="center", font=("Courier", 14, "normal"))



        # Paddle and ball collisions

        if (ball.xcor() > 340 and ball.xcor() < 350) and (ball.ycor() < paddle_b.ycor() + 40 and ball.ycor() > paddle_b.ycor() -40):
            ball.setx(340)
            ball.dx *= -1
        if (ball.xcor() < -340 and ball.xcor() > -350) and (ball.ycor() < paddle_a.ycor() + 40 and ball.ycor() > paddle_a.ycor() -40):
            ball.setx(-340)
            ball.dx *= -1

        # Paddles can't go beyond screen
        if paddle_a.ycor() >= 260:
            paddle_a_down()
        if paddle_a.ycor() <= -260:
            paddle_a_up()
        if paddle_b.ycor() >= 260:
            paddle_b_down()
        if paddle_b.ycor() <= -260:
            paddle_b_up()

        if score_a >= 3:
            restart = wn.textinput("Game result", "Well done " + Player_1 + ", you won ! \nDo you want to restart ? (y/n)")
            if restart == "y":
                wn.reset()
                run_game()
            else:
                break
        if score_b >= 3:
            restart = wn.textinput("Game result", "Well done " + Player_2 + ", you won ! \nDo you want to restart ? (y/n)")
            if restart == "y":
                wn.reset()
                run_game()
            else:
                break


run_game()

Thank you so much for your help and have a great day. Game Output

Aforetime answered 19/4, 2020 at 19:15 Comment(3)
Can you post your entire code? Particularly the part where you initialize the turtle. I have a hunch that "square" is the turtle.Cabrera
Hi, first of all thanks for you help ! I added the entire code to the main postAforetime
See this post regarding why not use while True: wn.update(). Related questions on "ghost" turtles: 1, 2, 3. I suggest minimizing the problem by removing unnecessary code until you've isolated the issue. Don't use recursion to restart the game or it'll crash after 1k restarts.Accentual
C
0

Here. Instead of using wn.reset(), I used wn.clearscreen(). It did the trick!!

Code:

import turtle

def run_game():

    wn = turtle.Screen()
    wn.title("Ping Pong Pour le Nuls")
    wn.bgcolor("green")
    wn.setup(width=800, height=600)
    wn.tracer(0)

    Player_1 = wn.textinput("Player 1", "Name of the player: ")
    Player_2 = wn.textinput("Player 2", "Name of the player: ")

    # Score
    score_a = 0
    score_b = 0
    # Paddle A
    paddle_a = turtle.Turtle()
    paddle_a.speed(0)
    paddle_a.shape("square")
    paddle_a.color("white")
    paddle_a.shapesize(stretch_wid=5, stretch_len=1)
    paddle_a.penup()
    paddle_a.goto(-350, 0)

    # Paddle B
    paddle_b = turtle.Turtle()
    paddle_b.speed(0)
    paddle_b.shape("square")
    paddle_b.color("white")
    paddle_b.shapesize(stretch_wid=5, stretch_len=1)
    paddle_b.penup()
    paddle_b.goto(350, 0)

    # Ball
    ball = turtle.Turtle()
    ball.speed(0)
    ball.color("black")
    ball.shape("circle")
    ball.penup()
    ball.goto(0, 0)
    ball.dx = 0.4
    ball.dy = -0.4

    # Pen
    pen = turtle.Turtle()
    pen.speed(0)
    pen.color("white")
    pen.penup()
    pen.hideturtle()
    pen.goto(0, 260)
    pen.write(str(Player_1) + " : 0    " + str(Player_2) + " : 0    ", align="center", font=("Courier", 14, "normal"))


    # Funtion

    def paddle_a_up():
        y = paddle_a.ycor()
        y += 20
        paddle_a.sety(y)

    def paddle_a_down():
        y = paddle_a.ycor()
        y += -20
        paddle_a.sety(y)

    def paddle_b_up():
        y = paddle_b.ycor()
        y += 20
        paddle_b.sety(y)

    def paddle_b_down():
        y = paddle_b.ycor()
        y += -20
        paddle_b.sety(y)


    def pen_2():
        pen_2 = turtle.Turtle()
        pen_2.speed(0)
        pen_2.color("white")
        pen_2.penup()
        pen_2.hideturtle()
        pen_2.goto(0, 0)
        pen_2.write("Player 2 Wins", align="center", font=("Courier", 24, "normal"))

    # Keyboard binding

    wn.listen()
    wn.onkeypress(paddle_a_up, "w")
    wn.onkeypress(paddle_a_down, "x")
    wn.onkeypress(paddle_b_up, "Up")
    wn.onkeypress(paddle_b_down, "Down")

    # Main Game Loop
    while True:
        wn.update()

        # Move the ball
        ball.setx(ball.xcor() + ball.dx)
        ball.sety(ball.ycor() + ball.dy)


        # Border checking
        if ball.ycor() > 290:
            ball.sety(290)
            ball.dy *= -1

        if ball.ycor() < -290:
            ball.sety(-290)
            ball.dy *= -1

        if ball.xcor() > 390:
            ball.goto(0, 0)
            ball.dx *= -1
            score_a += 1
            pen.clear()
            pen.write(str(Player_1) + " : {}    ".format(score_a) + str(Player_2) + " : {}    ".format(score_b),
                      align="center", font=("Courier", 14, "normal"))


        if ball.xcor() < -390:
            ball.goto(0, 0)
            ball.dx *= -1
            score_b += 1
            pen.clear()
            pen.write(str(Player_1) + " : {}    ".format(score_a) + str(Player_2) + " : {}    ".format(score_b),
                      align="center", font=("Courier", 14, "normal"))



        # Paddle and ball collisions

        if (ball.xcor() > 340 and ball.xcor() < 350) and (ball.ycor() < paddle_b.ycor() + 40 and ball.ycor() > paddle_b.ycor() -40):
            ball.setx(340)
            ball.dx *= -1
        if (ball.xcor() < -340 and ball.xcor() > -350) and (ball.ycor() < paddle_a.ycor() + 40 and ball.ycor() > paddle_a.ycor() -40):
            ball.setx(-340)
            ball.dx *= -1

        # Paddles can't go beyond screen
        if paddle_a.ycor() >= 260:
            paddle_a_down()
        if paddle_a.ycor() <= -260:
            paddle_a_up()
        if paddle_b.ycor() >= 260:
            paddle_b_down()
        if paddle_b.ycor() <= -260:
            paddle_b_up()

        if score_a >= 3:
            restart = wn.textinput("Game result", "Well done " + Player_1 + ", you won ! \nDo you want to restart ? (y/n)")
            if restart == "y":
                wn.clearscreen()
                run_game()
            else:
                break
        if score_b >= 3:
            restart = wn.textinput("Game result", "Well done " + Player_2 + ", you won ! \nDo you want to restart ? (y/n)")
            if restart == "y":
                wn.clearscreen()
                run_game()
            else:
                break


run_game()

Hope this helps!

Cabrera answered 23/5, 2020 at 16:29 Comment(1)
This will crash if the user replays too many times, due to using recursion to restart the game.Accentual
A
0

The existing answer solves the main problem with wn.clearscreen(), but doesn't explain why. The issue boils down to this minimal example:

from random import randint
import turtle

def run_game():
    wn = turtle.Screen()
    wn.setup(width=800, height=600)

    # Paddle A
    paddle_a = turtle.Turtle()
    paddle_a.shape("square")
    paddle_a.color("red")
    paddle_a.penup()
    paddle_a.goto(randint(-200, 200), randint(-200, 200))
    paddle_a.reset()

    print(f"there are {len(turtle.turtles())} turtles on the screen")
    run_game()


run_game()

If you run this program, you'll see that more and more turtles are created each time through the "game":

there are 1 turtles on the screen
there are 2 turtles on the screen
there are 3 turtles on the screen
there are 4 turtles on the screen
there are 5 turtles on the screen
...

Calling reset() on a turtle moves it back to the center and strips off properties like color, but the turtle still exists. Turtles are not destroyed when they go out of scope. They're pretty much permanent.

wn.clearscreen() actually removes all turtles from the screen rather than just resetting them to the middle:

def run_game():
    wn = turtle.Screen()
    wn.clearscreen()  # <-- added
    wn.setup(width=800, height=600)
    ...

Now the output is correct:

there are 1 turtles on the screen
there are 1 turtles on the screen
there are 1 turtles on the screen
there are 1 turtles on the screen
there are 1 turtles on the screen
...

Another way to do this is to declare your turtles outside the game restart loop, initializing them once per game:

from random import randint
import turtle


def run_game():
    wn = turtle.Screen()
    wn.setup(width=800, height=600)

    paddle_a.penup()
    paddle_a.goto(randint(-200, 200), randint(-200, 200))
    paddle_a.reset()

    print(f"there are {len(turtle.turtles())} turtles on the screen")
    run_game()


# Paddle A
paddle_a = turtle.Turtle()
paddle_a.shape("square")
paddle_a.color("red")
run_game()

Even after the clearscreen() fix, if you run the above code long enough, you'll see:

RecursionError: maximum recursion depth exceeded in __instancecheck__

Don't use recursion to do something, unless you can guarantee it'll be well under the CPython call stack limit of 1000. Since this program takes user input, if someone is really passionate about the game, they can theoretically hit 1000 replays, crashing the game.

Instead, use iteration or ontimer:

from random import randint
import turtle


def run_game():
    wn = turtle.Screen()
    wn.setup(width=800, height=600)

    paddle_a.penup()
    paddle_a.goto(randint(-200, 200), randint(-200, 200))
    paddle_a.reset()

    print(f"there are {len(turtle.turtles())} turtles on the screen")
    # run_game() # remove this--no recursion


# Paddle A
paddle_a = turtle.Turtle()
paddle_a.shape("square")
paddle_a.color("red")

while True:
    run_game()

Another problem is that your game runs at lightning speed on my machine, making it unplayable. The cause is while True: wn.update(), which pushes the CPU as fast as it can go, without establishing a consistent framerate. Instead, use ontimer to run your event loop.


See these related questions on "ghost turtles", which is a common issue in Python turtle:

Accentual answered 10/11, 2023 at 15:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.