2
\$\begingroup\$

I have just finished learning basic-intermediate Python subjects and wanted to test myself. So here is the my basic Rock-Paper-Scissors game.

def play():
    import random

    player_score = 0
    ai_score = 0

    while True:
        try:
            user_choice = int(input("\nRock: 1"
                                    "\nPaper: 2"
                                    "\nScissor: 3"
                                    "\nWrite the number of the object you choose: "))
        except NameError:
            print("You can only enter number.")
            continue
        except ValueError:
            print("You can only enter number.")
            continue

        if user_choice not in range(1, 4):
            print("Invalid number, you have to write numbers between 1-3")
            continue

        rps = {1: "Rock", 2: "Paper", 3: "Scissor"}
        ai_choice = random.randint(1, 3)

        print(f"\nPlayer's choice: {rps[user_choice]} | AI's choice: {rps[ai_choice]}")

        if user_choice == ai_choice:
            print("Draw!")
        elif user_choice == 1:
            if ai_choice == 2:
                print("AI's paper wrapped Player's rock, AI won this round!")
                ai_score += 1
            elif ai_choice == 3:
                print("Player's rock crushed AI's scissor, Player won this round!")
                player_score += 1
        elif user_choice == 2:
            if ai_choice == 1:
                print("Players's paper wrapped AI's rock, Player won this round!")
                player_score += 1
            elif ai_choice == 3:
                print("AI's scissor cut Player's paper, AI won this round!")
                ai_score += 1
        elif user_choice == 3:
            if ai_choice == 1:
                print("AI's rock crushed Player's scissor, AI won this round!")
                ai_score += 1
            elif ai_choice == 2:
                print("Player's scissor cut AI's paper, Player won this round!")
                player_score += 1

        print(f"\nPlayer's score: {player_score} | AI's score: {ai_score}"
              f"\nGame ends when player or AI's score reach 3!")

        if player_score == 3:
            print("Game over, Player won!")
            quit()
        elif ai_score == 3:
            print("Game over, AI won!")
            proceed = input("Wanna try again and show the AI your unmatched and great wisdom?"
                            "\nWrite Y or N >: ").lower()
            if proceed == "y":
                play()
            elif proceed == "n":
                quit()
            else:
                print("Invalid command, exiting program...")
                quit()


if __name__ == "__main__":
    play()

So, what do you think? How can i make it better, what are my mistakes?

\$\endgroup\$
2
\$\begingroup\$

I'd say there is two major issues in your code.

First one is that main loop which is very unclear, many if statements, filled with continue and quit() , this is spagetthi code and it is makes your code harder to read, so harder to debug, because that jumps everywhere.

The second one is the lack of OOP design, which could be very adapted to this case. Games are usually developped with strongly object oriented languages (Unreal engine uses C++ and Unity uses C# among others) because of the strong interactions between components, and a very favorable context to abstraction and modularity. To be fair, that's not really needed for short games like rock paper scissors but it prevents you to scale the game further.

There is few design improvements I suggest you:

Split your code into functions, first one could be the game winning condition that should be used in your game loop to avoid unwanted infinite loops, common with constant conditions like while True:. Replace this statement instead by while has_game_ended():, with has_game_ended looking like:

    def has_game_ended(player_score: int, ia_score: int) -> bool:
        if player_score == 3 or ia_score == 3:
            print("Game over: {} won!".format("player" if player_score == 3 else "AI"))
            return True
        return False

Then use an enum to list you moves to make them easier to reuse in your code (PAPER makes more sense than 1) and enable comparison using operator overload, note that this is a tricky comparison specific to game rules.

from enum import Enum

class Move(Enum):
    PAPER = 1
    ROCK = 2
    SCISSORS = 3

    def __lt__(self, other):
        return (self is Move.ROCK and other is Move.PAPER) or (self is Move.Paper and other is Move.SCISSORS) or (self is Move.SCISSORS and other is Move.ROCK)

Now you can write:

>>> player_choice = Move.PAPER
>>> ia_choice = Move.ROCK
>>> player_choice < ia_choice
True

and something like

player_score = player_score + int(player_choice > ia_choice)
ia_score = ia_score + int(ia_choice > player_choice)

This works well because it's not comparing two integers anymore, it's comparing two Move, which makes all the difference thanks to operator overloading.

Avoid quit() in your scripts, it totally cuts the code flow and doesn't allow nice error handling, prefer instead loop conditions or try except statements with customized Exception for unexpected behaviors (for example when you use invalid command, as in the example below).

def WrongCommand(Exception):
    pass

if __name__ == "__main__":
    try:
        play()
    except WrongCommand:
        print("Invalid command, exiting program...")
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.