Search code examples
pythonwhile-loopturtle-graphicspython-turtle

Why does a while True loop not work in my rock paper scissors game?


I'm trying to make a rock paper scissors game with 3 buttons to click on on-screen with import turtle. That went well and it worked, the buttons worked and the right text showed up on screen when clicking a certain button. (I haven't added the computer choosing part yet, so my code is only the users input) But now I wanted to add that when you clicked a button for the second time, the text of the last button disappears. Because if you keep clicking, the text stays there and it overlaps. So I wanted to add a "Do you want to play again?" input thing by putting the buttons in a while True loop so it asks the user if they want to play again every time you click a button, but that doesn't work. I don't get any errors, the screen opens and it draws everything for me but I can't click any buttons, the text doesn't show up and then it crashes. If I remove the while loop and the input question, it works again. It should work right? Or is this not possible? I read it somewhere else with another game so I don't know why it doesn't work. This is my code with the while True loop. The while True loop is at the end and I'm sorry if my code is longer than it should be, I don't know all the tricks yet:

import turtle

fontButtons = ("Courier", 22, "bold")
fontText = ("Arial", 26, "bold")

screen = turtle.Screen()
screen.bgcolor("light blue")
screen.setup(800,800)

#center = turtle.Turtle()
#center.goto(0,0)
#center.shape("circle")
#center.size(1)

#game
pen = turtle.Turtle()
pen.hideturtle()
pen.speed(0)
pen.penup()
pen.fillcolor("light yellow")
pen.goto(-250,0)
pen.begin_fill()
pen.pendown()

for i in range(2):
    pen.forward(225)
    pen.left(90)
    pen.forward(200)
    pen.left(90)

pen.end_fill()
pen.penup()
pen.goto(25,0)
pen.begin_fill()
pen.pendown()

for i in range(2):
    pen.forward(225)
    pen.left(90)
    pen.forward(200)
    pen.left(90)

pen.end_fill()
pen.penup()
pen.goto(-135,165)
pen.write("You", font=fontText, align="center")

pen.goto(135,165)
pen.write("Computer", font=fontText, align="center")
             

#rock

rock = turtle.Turtle()
rock.hideturtle()
rock.penup()
rock.speed(0)
rock.fillcolor("light yellow")
rock.goto(-200,-100)
rock.pendown()
rock.begin_fill()

for i in range(2):
    rock.forward(100)
    rock.left(90)
    rock.forward(40)
    rock.left(90)
    
rock.end_fill()

rock.penup()
rock.goto(-150,-95)
rock.write("ROCK",font=fontButtons, align="center")

#paper

paper = turtle.Turtle()
paper.hideturtle()
paper.penup()
paper.speed(0)
paper.fillcolor("light yellow")
paper.goto(-50,-100)
paper.pendown()
paper.begin_fill()

for i in range(2):
    paper.forward(100)
    paper.left(90)
    paper.forward(40)
    paper.left(90)
    
paper.end_fill()

paper.penup()
paper.goto(0,-95)
paper.write("PAPER",font=fontButtons, align="center")

#scissor

scissor = turtle.Turtle()
scissor.hideturtle()
scissor.penup()
scissor.speed(0)
scissor.fillcolor("light yellow")
scissor.goto(100,-100)
scissor.pendown()
scissor.begin_fill()

for i in range(2):
    scissor.forward(100)
    scissor.left(90)
    scissor.forward(40)
    scissor.left(90)
    
scissor.end_fill()

scissor.penup()
scissor.goto(150,-95)
scissor.write("SCISSOR",font=fontButtons, align="center")

while True:
#rock click
    def buttonClickR(x,y):
        if x > -200 and x < -100 and y > -100 and y < -60:
            pen.goto(-135,100)
            pen.write("Rock", font=fontText, align="center")
                            

    turtle.onscreenclick(buttonClickR, 1, True)
    turtle.listen()
    
#paper click
    def buttonClickP(x,y):
        if x > -50 and x < 50 and y > -100 and y < -60:
            pen.goto(-135,100)
            pen.write("Paper", font=fontText, align="center")
            

    turtle.onscreenclick(buttonClickP, 1, True)
    turtle.listen()
    

#scissor click
    def buttonClickS(x,y):
        if x > 100 and x < 200 and y > -100 and y < -60:
            pen.goto(-135,100)
            pen.write("Scissor", font=fontText, align="center")
            
    
    turtle.onscreenclick(buttonClickS, 1, True)
    turtle.listen()

    again = input("Do you want to play again? ")

    if again == 'yes':
        screen.clear()
    
    else:
        print('Bye!')       


turtle.done()

Solution

  • The "loop" for turtle needs no while loop - turtle.done() does not "end" the turtle code - it signals that you are "done" setting all up and starts the everlasting turtle mainloop.

    Your code is long because you solve the same problem over and over again instead of putting it into a method to be called with parameters.

    You draw 2 big boxes and 3 small boxes, the small boxes also get centered text inside, the bigger boxes different text on top.

    Asking for text input while in turtle can be done via turtle.textinput(...) more elegantly then skipping to input() on console.

    To quit, use turtle.exitonclick() where appropriate - or call turtle.bye().

    Your code repeats lots of things you should put into functions:

    import turtle
    
    # this uses the ealy defined "pen" turtle - no need to use different ones
    # and a global for logic - you can refine that if you want, for demo it works
    
    fontButtons = ("Courier", 22, "bold")
    fontText = ("Arial", 26, "bold")
    
    screen = turtle.Screen()
    pen = turtle.Turtle()
    pen.hideturtle()
    pen.speed(0)
    pen.penup()
    pen.fillcolor("light yellow")
    
    # reapeating things put into methods to be used multiple times
    
    def box_at(x, y, width, height):
        """Makes a box in the current fillcolor at x,y to x+width,y+height. 
        Pen is up afterwards."""
        pen.penup()
        pen.goto(x,y)
        pen.begin_fill()
        pen.pendown()
    
        for i in range(2):
            pen.forward(width)
            pen.left(90)
            pen.forward(height)
            pen.left(90)
    
        pen.end_fill()
        pen.penup()
    
    
    def button(x,y,width,height, text):
        """Makes box + text centered inside, space width appropriate to text."""
        # no need to repeat painting a box - we solved that earlier
        box_at(x,y,width,height)
    
        pen.goto(x+width/2,y)
        pen.write(text, font=fontButtons, align="center")
    
    def board():
        # YOU
        box_at(-250,10,225,200)
    
        # COMPUTER
        box_at(25,10,225,200)
    
        # custom text placement
        pen.goto(-135,165)
        pen.write("You", font=fontText, align="center")
        pen.goto(135,165)
        pen.write("Computer", font=fontText, align="center")
    
    def buttons():
        # all the buttons
        button(-200, -100, 120, 40, "ROCK")
        button( -50, -100, 120, 40, "PAPER")
        button( 100, -100, 120, 40, "SCISSOR")
    
    def reset():
        # clear screen and make it beautiful
        screen.bgcolor("light blue")
        screen.setup(800,800)
        board()
        buttons()
    

    The game uses these functions like so:

    reset()
    moves = 0
    
    # all the logic
    def buttonClick(x,y):
        global moves  # not fancy - buts its just a demo
    
        # handle all button clicks inside this one function
        # adapted the x/y values to fit my bigger buttons
        if x > -200 and x < -80 and y > -100 and y < -60:
            board()  # this clears the former things
            pen.goto(-135,100)
            pen.write("Rock", font=fontText, align="center")
            moves += 1
        elif x > -50 and x < 70 and y > -100 and y < -60:
            board() # this clears the former things
            pen.goto(-135,100)
            pen.write("Paper", font=fontText, align="center")
            moves += 1
        elif x > 100 and x < 220 and y > -100 and y < -60:
            board() # this clears the former things
            pen.goto(-135,100)
            pen.write("Scissor", font=fontText, align="center")
            moves += 1
        
        # TODO make the computer do something, evaluate moves, print tally
    
        # program logic, every 3rd click ask for continue
        if moves == 3:
    
            # TODO: print tally and announce winner
    
            again = turtle.textinput("Continue?" , "Do you want to play again? ")
    
            if again == 'yes':
                moves = 0
                reset() 
            else:
                print('Bye!')
                screen.clear()         
                turtle.exitonclick() # or turtle.bye() directly
    
    # register the game logic click handler
    turtle.onscreenclick(buttonClick, 1, True)
    
    # start the mainloop until turtly.bye() or exitonclick() is done
    turtle.mainloop()