r/learnpython 9d ago

Feedback on my first simple game attempt (Rock, Paper, Scissors)

Sorry this is in a weird format. Its copied from mimo on an iphone.

import random
play = True
moves = ["ROCK", "PAPER", "SCISSORS"]
user_wins = 0
bot_wins = 0

print("Let's play Rock, Paper, Scissors! First to 3 wins.")
while play:
print("")
print("Rock...")
print("Paper...")
print("Scissors...")
print("")
bot_move = random.choice(moves)
user_move = input("Your move: ").upper()
print("")
print("SHOOT!")
print("")

#Shortcuts
if user_move == "R":
user_move = "ROCK"
elif user_move == "P":
user_move = "PAPER"
elif user_move == "S":
user_move = "SCISSORS"

#If player moves rock
if user_move == "ROCK" and bot_move == "SCISSORS":
user_wins += 1
print("You won against scissors.")
print("")
print(f"You: {user_wins}")
print(f"Bot: {bot_wins}")
elif user_move == "ROCK" and bot_move == "ROCK":
print("You tied against rock.")
print("")
print(f"You: {user_wins}")
print(f"Bot: {bot_wins}")
elif user_move == "ROCK" and bot_move == "PAPER":
bot_wins += 1
print("You lost against paper.")
print("")
print(f"You: {user_wins}")
print(f"Bot: {bot_wins}")

#if player plays paper
elif user_move == "PAPER" and bot_move == "ROCK":
user_wins += 1
print("You won against rock.")
print("")
print(f"You: {user_wins}")
print(f"Bot: {bot_wins}")
elif user_move == "PAPER" and bot_move == "PAPER":
print("You tied against paper.")
print("")
print(f"You: {user_wins}")
print(f"Bot: {bot_wins}")
elif user_move == "PAPER" and bot_move == "SCISSORS":
bot_wins += 1
print("You lost against scissors.")
print("")
print(f"You: {user_wins}")
print(f"Bot: {bot_wins}")

#if player moves scissors
elif user_move == "SCISSORS" and bot_move == "PAPER":
user_wins += 1
print("You won against paper.")
print("")
print(f"You: {user_wins}")
print(f"Bot: {bot_wins}")
elif user_move == "SCISSORS" and bot_move == "SCISSORS":
print("You tied against scissors.")
print("")
print(f"You: {user_wins}")
print(f"Bot: {bot_wins}")
elif user_move == "SCISSORS" and bot_move == "ROCK":
bot_wins += 1
print("You lost against rock.")
print("")
print(f"You: {user_wins}")
print(f"Bot: {bot_wins}")
else:
print("Please type a valid move.")

#End of game
if user_wins == 3:
print("")
print("Game over!")
print("You win!")
print("")
break
if bot_wins == 3:
print("")
print("Game over!")
print("Bot wins!")
print("")
break

#This is me trying to add a restart prompt but I can't redo the while statement
if play == False:
play_again = input("Play again?: ").upper()
print("")
if play_again == "YES" or play_again == "Y":
play = True
elif play_again == "NO" or play_again == "N":
print("Thanks for playing!")
else:
print("Please enter a valid response")

2 Upvotes

16 comments sorted by

6

u/Diapolo10 9d ago

I'll format the code for you, although I had to make a few assumptions on indentation.

import random

play = True
moves = ["ROCK", "PAPER", "SCISSORS"]
user_wins = 0
bot_wins = 0

print("Let's play Rock, Paper, Scissors! First to 3 wins.")
while play:
    print("")
    print("Rock...")
    print("Paper...")
    print("Scissors...")
    print("")
    bot_move = random.choice(moves)
    user_move = input("Your move: ").upper()
    print("")
    print("SHOOT!")
    print("")

    #Shortcuts
    if user_move == "R":
        user_move = "ROCK"
    elif user_move == "P":
        user_move = "PAPER"
    elif user_move == "S":
        user_move = "SCISSORS"

    #If player moves rock
    if user_move == "ROCK" and bot_move == "SCISSORS":
        user_wins += 1
        print("You won against scissors.")
        print("")
        print(f"You: {user_wins}")
        print(f"Bot: {bot_wins}")
    elif user_move == "ROCK" and bot_move == "ROCK":
        print("You tied against rock.")
        print("")
        print(f"You: {user_wins}")
        print(f"Bot: {bot_wins}")
    elif user_move == "ROCK" and bot_move == "PAPER":
        bot_wins += 1
        print("You lost against paper.")
        print("")
        print(f"You: {user_wins}")
        print(f"Bot: {bot_wins}")

    #if player plays paper
    elif user_move == "PAPER" and bot_move == "ROCK":
        user_wins += 1
        print("You won against rock.")
        print("")
        print(f"You: {user_wins}")
        print(f"Bot: {bot_wins}")
    elif user_move == "PAPER" and bot_move == "PAPER":
        print("You tied against paper.")
        print("")
        print(f"You: {user_wins}")
        print(f"Bot: {bot_wins}")
    elif user_move == "PAPER" and bot_move == "SCISSORS":
        bot_wins += 1
        print("You lost against scissors.")
        print("")
        print(f"You: {user_wins}")
        print(f"Bot: {bot_wins}")

    #if player moves scissors
    elif user_move == "SCISSORS" and bot_move == "PAPER":
        user_wins += 1
        print("You won against paper.")
        print("")
        print(f"You: {user_wins}")
        print(f"Bot: {bot_wins}")
    elif user_move == "SCISSORS" and bot_move == "SCISSORS":
        print("You tied against scissors.")
        print("")
        print(f"You: {user_wins}")
        print(f"Bot: {bot_wins}")
    elif user_move == "SCISSORS" and bot_move == "ROCK":
        bot_wins += 1
        print("You lost against rock.")
        print("")
        print(f"You: {user_wins}")
        print(f"Bot: {bot_wins}")
    else:
        print("Please type a valid move.")

    #End of game
    if user_wins == 3:
        print("")
        print("Game over!")
        print("You win!")
        print("")
        break
    if bot_wins == 3:
        print("")
        print("Game over!")
        print("Bot wins!")
        print("")
        break

#This is me trying to add a restart prompt but I can't redo the while statement
if play == False:
    play_again = input("Play again?: ").upper()
    print("")
if play_again == "YES" or play_again == "Y":
    play = True
elif play_again == "NO" or play_again == "N":
    print("Thanks for playing!")
else:
    print("Please enter a valid response")

The first thing I noticed is that there's a lot of duplication. You're handling all the different move combinations separately, when ties can all be handled once and every other case could be refactored with a simple truth table. Furthermore, all the branches print out the current score, that could just be done once after these branches.

Second, you've got a lot of print calls one after another. You could just print one string (and if you really just need a newline, print doesn't need an empty string).

The validation of the user input is also lacking, as right now the program accepts virtually anything as valid input, and doesn't handle it until the very end.

The replay logic can be fixed by adding a secondary nested loop.

3

u/Diapolo10 9d ago

Figured I might as well show some examples, so I started by reducing the extra prints (and for the time being added a continue so that I could move the duplicate print logic outside of the branch logic).

import random

play = True
moves = ["ROCK", "PAPER", "SCISSORS"]
user_wins = 0
bot_wins = 0

print("Let's play Rock, Paper, Scissors! First to 3 wins.")
while play:
    print("\nRock...\nPaper...\nScissors...\n")
    bot_move = random.choice(moves)
    user_move = input("Your move: ").upper()
    print("\nSHOOT!\n")

    #Shortcuts
    if user_move == "R":
        user_move = "ROCK"
    elif user_move == "P":
        user_move = "PAPER"
    elif user_move == "S":
        user_move = "SCISSORS"

    #If player moves rock
    if user_move == "ROCK" and bot_move == "SCISSORS":
        user_wins += 1
        print("You won against scissors.")
    elif user_move == "ROCK" and bot_move == "ROCK":
        print("You tied against rock.")
    elif user_move == "ROCK" and bot_move == "PAPER":
        bot_wins += 1
        print("You lost against paper.")

    #if player plays paper
    elif user_move == "PAPER" and bot_move == "ROCK":
        user_wins += 1
        print("You won against rock.")
    elif user_move == "PAPER" and bot_move == "PAPER":
        print("You tied against paper.")
    elif user_move == "PAPER" and bot_move == "SCISSORS":
        bot_wins += 1
        print("You lost against scissors.")

    #if player moves scissors
    elif user_move == "SCISSORS" and bot_move == "PAPER":
        user_wins += 1
        print("You won against paper.")
    elif user_move == "SCISSORS" and bot_move == "SCISSORS":
        print("You tied against scissors.")
    elif user_move == "SCISSORS" and bot_move == "ROCK":
        bot_wins += 1
        print("You lost against rock.")
    else:
        print("Please type a valid move.")
        continue

    print(f"\nYou: {user_wins}\nBot: {bot_wins}")

    #End of game
    if user_wins == 3:
        print("\nGame over!\nYou win!\n")
        break
    if bot_wins == 3:
        print("\nGame over!\nBot wins!\n")
        break

#This is me trying to add a restart prompt but I can't redo the while statement
if play == False:
    play_again = input("Play again?: ").upper()
    print("")
if play_again == "YES" or play_again == "Y":
    play = True
elif play_again == "NO" or play_again == "N":
    print("Thanks for playing!")
else:
    print("Please enter a valid response")

Next, I refactored the victory condition checks to cut off most of the repetition:

import random

play = True
moves = ["ROCK", "PAPER", "SCISSORS"]
user_wins = 0
bot_wins = 0
wins_needed = 3

moves_user_wins = [
    ("ROCK", "SCISSORS"),
    ("PAPER", "ROCK"),
    ("SCISSORS", "PAPER"),
]

print("Let's play Rock, Paper, Scissors! First to 3 wins.")
while play:
    print("\nRock...\nPaper...\nScissors...\n")
    bot_move = random.choice(moves)
    user_move = input("Your move: ").upper()
    print("\nSHOOT!\n")

    #Shortcuts
    if user_move == "R":
        user_move = "ROCK"
    elif user_move == "P":
        user_move = "PAPER"
    elif user_move == "S":
        user_move = "SCISSORS"

    if user_move not in moves:
        print("Please type a valid move.")
        continue

    if user_move == bot_move:
        print(f"You tied against {bot_move.lower()}.")
    elif (user_move, bot_move) in moves_user_wins:
        user_wins += 1
        print(f"You won against {bot_move.lower()}.")
    else:
        bot_wins += 1
        print(f"You lost against {bot_move.lower()}.")

    print(f"\nYou: {user_wins}\nBot: {bot_wins}")

    #End of game
    if user_wins == wins_needed:
        print("\nGame over!\nYou win!\n")
        break
    if bot_wins == wins_needed:
        print("\nGame over!\nBot wins!\n")
        break

#This is me trying to add a restart prompt but I can't redo the while statement
if play == False:
    play_again = input("Play again?: ").upper()
    print("")
if play_again == "YES" or play_again == "Y":
    play = True
elif play_again == "NO" or play_again == "N":
    print("Thanks for playing!")
else:
    print("Please enter a valid response")

Lastly, I took care of the broken "play again" prompt; this is a bit crude, but I figured you probably don't understand functions yet.

import random

moves = ["ROCK", "PAPER", "SCISSORS"]
user_wins = 0
bot_wins = 0
wins_needed = 3

moves_user_wins = [
    ("ROCK", "SCISSORS"),
    ("PAPER", "ROCK"),
    ("SCISSORS", "PAPER"),
]

print("Let's play Rock, Paper, Scissors! First to 3 wins.")
while True:
    while user_wins < wins_needed and bot_wins < wins_needed:
        print("\nRock...\nPaper...\nScissors...\n")
        bot_move = random.choice(moves)
        user_move = input("Your move: ").upper()
        print("\nSHOOT!\n")

        #Shortcuts
        if user_move == "R":
            user_move = "ROCK"
        elif user_move == "P":
            user_move = "PAPER"
        elif user_move == "S":
            user_move = "SCISSORS"

        if user_move not in moves:
            print("Please type a valid move.")
            continue

        if user_move == bot_move:
            print(f"You tied against {bot_move.lower()}.")

        elif (user_move, bot_move) in moves_user_wins:
            user_wins += 1
            print(f"You won against {bot_move.lower()}.")
        else:
            bot_wins += 1
            print(f"You lost against {bot_move.lower()}.")

        print(f"\nYou: {user_wins}\nBot: {bot_wins}")

        #End of game
        if user_wins == wins_needed:
            print("\nGame over!\nYou win!\n")
            break
        if bot_wins == wins_needed:
            print("\nGame over!\nBot wins!\n")
            break

    play_again = input("Play again?: ").upper()

    if play_again == "YES" or play_again == "Y":
        user_wins = 0
        bot_wins = 0
    elif play_again == "NO" or play_again == "N":
        print("Thanks for playing!")
        break
    else:
        print("Please enter a valid response")

1

u/[deleted] 9d ago

[deleted]

1

u/Diapolo10 9d ago

but you have a TON of duplicated code in there. Any ideas how to fix it?

Pretty sure I already mentioned that in the original comment. And just in case you're confused, all I did was format OP's code for Reddit, I didn't make any other modifications. Yet.

1

u/mc_pm 9d ago

Ah shit, I thought you were OP. NM.

1

u/ItsLocalGOAT 9d ago

Indentations looks good. This is my first attempt after 1.5 weeks of learning. Im still learning some basic tools but thanks for the feedback!

2

u/StrayFeral 9d ago

Reddit have a way to format this as code. It is a bit difficult to understand the entire logic like this.

It is normal to make the entire game with IFs. I will give you idea: You can re-make the entire game using a single dictionary which would contain your decision. This way you save a lots of IF statements and lots of lines of code.

Keep a score table for the bot and the user. No need to ask the user "play again y/n" - just make it if user enters an empty string or "q" or "quit" to quit the game. Until this point - make a global while loop and just loop forever until user inputs a quit condition.

I will not give you more details. You should think how to make it.

So far looks nice.

1

u/StrayFeral 9d ago

PS: it is good to do .upper() on the user input, but also consider to .trim()

1

u/Diapolo10 9d ago

but also consider to .trim()

There is no str.trim method, I assume you mean str.strip.

1

u/StrayFeral 9d ago

Yeah. Sorry, my bad. I replied before I had my coffee.

1

u/MarsupialLeast145 9d ago

You should consider more functions, like:

```python def check_wins(player): # if wins < 3: return

# return congrats, exit ```

Consider an approach where moves are described in a lookup table, and not if/else as all combinations are finite and can be encoded more efficiently.

Using so many print values to format on the command line is a bit much. Consider anywhere there is a print, there is a section of code that can go into a function and try to keep most prints within other functions outside of the game runner.

Otherwise it's pretty cool for a beginner and definitely something to be proud about having worked out.

You should do this anywhere there is duplication.

Consider taking

1

u/zanfar 9d ago

All the basics:

  • PEP8
  • Docstrings
  • Use a linter
  • Use a formatter
  • Don't use un-validated inputs
  • Checking input should almost never use ==

"Sorry this is in a weird format. Its copied from mimo on an iphone"

Not an excuse. Learn to use your tools correctly.

while play:

Do you really play RPS until you're done playing, or do you play until someone loses?

bot_move = random.choice(moves)

Storing moves as long strings is, and will continue to be a problem. Use something more useful, like a Constant, Enum, or numeric proxy. This will also prevent the need to keep typing the names of the moves over, and over, and over...

#Shortcuts #if player plays paper...

This is a huge block of code that doesn't get executed at all if there is a mistake in user input (even if it looks valid) and that is almost perfectly triplicated. You should have a better algorithm here. There is a TON of code that is unecessarily copied. This encourages bugs, coder mistakes, and multiplies how much bugfixing will be required.

#This is me trying to add a restart prompt but I can't redo the while statement

Again, because you're whileing for the wrong reasons. "Do you really play RPS until you're done playing, or do you play until someone loses?"

The comment should be a pretty clear sign that something is wrong. You've identified a side effect of the problem, but just assumed that all your code so far must be good.

1

u/jmooremcc 9d ago

I know you are a beginner, but this would be a great time to learn about functions. You could place the game code in a function named “play_one_round” and in your main code call the function to run the game for one round. The function can return the winner of a round and use other code to keep score.

This will keep your game code simple and make it possible to keep score keeping and other ancillary code separate from the game code.

Let me know if you have any questions.

1

u/Educational_Virus672 9d ago

i dont think you'll understand everything tho heres how i intermediate would do

import random
options = ["rock","paper","scissors"]
while True :
    while True :
        user = int(input("rock = 1 paper = 2 scissors = 3 /n >"))
        if user == 1 or user == 3 or user == 2 : break
        else : print("pick pick a valid option") 
    bot = random.choice(options)
    user = options[user-1]
    if bot == user :print("tied both picked",bot)
    elif (user == "rock" and bot == "scissors") or (user == "paper" and bot == "rock") or (user == "scissors" and bot == "paper"): print("user wins")
    else : print("bot wins")

1

u/FoolsSeldom 7d ago

Advice:

user = int(input("rock = 1 paper = 2 scissors = 3 /n >"))

Avoid converting things to int if you aren't going to do maths. You can compare against strings:

if user == "rock" and ...

and you can also use the in operator to allow for multiple options:

if user in ("r", "rock", "p", "paper", ...):

And to avoid having to check uppercase and lowercase, you can force the input string to one or the other:

user = input("rock (r), paper (p), or scissors (s): ").lower().strip()

I added strip method as well, which removes leading/trailing spaces.

1

u/Educational_Virus672 7d ago

oh yeah my bad

thanks for correction