I have posted my first version of RockPaperScissors a while back. It was on an old version of python with improper use of some functions. This version was updated to a more recent version of python and has a little extra for users who run on terminal. Is there anything I should add or change? Are my classes used correctly? Are there any bugs? Any overall improvements?

# Programmer: DeliriousSyntax
# Date: October 30, 2015
# File: RockPaperScissorsV2-py3.py

# This is a classic game of rock paper scissors.

import random
import shelve
import time
import colorama
import sys

class Scorecard(dict):
    """Stores all items related to "Score\""""

    def __init__(self, **kwargs):
        """Makes all values set to 0"""
        for key in ('rounds', 'losses', 'wins', 'draws', 'games_won',
                    'games_lost', 'games_played', "total_rounds",
                    'rounds_won', 'rounds_lost', 'rounds_tied', 'game_draws'):
            self[key] = 0

    def reset(self):
        """Resets the scores for next game"""
        for key in ('rounds', 'losses', 'wins', 'draws'):
            self[key] = 0

    def round_update(self, result):
        """Updates the info for each round
        :param result:
        self['rounds'] += 1
        if result == 'win':
            self['wins'] += 1
        elif result == 'loss':
            self['losses'] += 1
            self['draws'] += 1

    def add2score(self):
        """Adds the scores from the game to total scores"""
        self['games_played'] += 1
        self['rounds_lost'] += self['losses']
        self['rounds_won'] += self['wins']
        self['rounds_tied'] += self['draws']
        self['total_rounds'] += self['rounds']
        if self['losses'] > self['wins']:
            self['games_lost'] += 1
        elif self['losses'] < self['wins']:
            self['games_won'] += 1
            self['game_draws'] += 1

    def stats_board(self):
        """Returns the stat board"""
        return ('\n\n++++++++++++++++++++-=Stats=-++++++++++++++++++++++\n'
                '|--{:^11}--|--{:^12}--|--{:^12}--|\n'.format("X   ", "Rounds", "Games") +
                '|--{:^11}--|--{:^12}--|--{:^12}--|\n'.format("Wins  ", self['rounds_won'], self['games_won']) +
                '|--{:^11}--|--{:^12}--|--{:^12}--|\n'.format("Losses", self['rounds_lost'], self['games_lost']) +
                '|--{:^11}--|--{:^12}--|--{:^12}--|\n'.format("Draws ", self['rounds_tied'], self['game_draws']) +
                '|--{:^11}--|--{:^12}--|--{:^12}--|\n'.format("Played", self['total_rounds'], self['games_played']) +

    def final_score(self):
        """Returns the games final board"""
        return ('\n\n+++++++++++Final Score++++++++++++\n'
                '|--{:^11}--|--{:^12}--|\n'.format('Wins  ', self['wins']) +
                '|--{:^11}--|--{:^12}--|\n'.format('Losses', self['losses']) +
                '|--{:^11}--|--{:^12}--|\n'.format('Draws  ', self['draws']) +
                '|--{:^11}--|--{:^12}--|\n'.format('Rounds', self['rounds']) +

class Game:
    """Main Game Class"""
    MOVES = ('rock', 'paper', 'scissors')
    ROUND_OPTIONS = ('1', '3', '5', '7')
    MENU = ('stats', 'play', 'quit')
        "rock": "scissors",
        "scissors": "paper",
        "paper": "rock",
    green = colorama.Fore.GREEN
    yellow = colorama.Fore.YELLOW
    cyan = colorama.Fore.CYAN
    blue = colorama.Fore.BLUE
    magenta = colorama.Fore.MAGENTA
    COLORS = (green, yellow, cyan, blue, magenta)

    def delirioussyntax(self):
        idle = "idlelib" in sys.modules
        if idle is False:
            print(colorama.Back.BLACK + colorama.Style.BRIGHT, end='')
        for letter in "Delirious":
            if idle is False:
                print(random.choice(Game.COLORS), end='')
            print(letter, end='')
            time.sleep(random.uniform(.1, .4))
        if idle is False:
            print(colorama.Fore.RED, end='')
        for letter in "Syntax":
            print(letter, end='')
            time.sleep(random.uniform(.1, .4))
        print(colorama.Style.RESET_ALL, "\n\n")

    def intro(self):
        """Intro statements"""
        self.Score = Scorecard()
            with shelve.open("RPSv2.dat") as f:
                self.Score = f["statistics"]
        except KeyError:
            with shelve.open("RPSv2.dat") as f:
                f["statistics"] = self.Score

        print("Brought to you by: ", end='')
        print("Welcome to the greatest, mind blowing challenge of all time.")
        print("     - Rock, Paper, Scissors!\n")
        print("Many have tried and many have FAILED...")
        print("This will be a test between the human mind and my AI.")
        input("Press \"enter\" when you believe your ready for this challenge.\n")
        print("\nGood Luck... Human.\n\n")

    def custom_input(self, question, choices):
        """A custom loop that checks to see if choices are valid
        :param choices:
        :param question:
        response = None
        while True:
            while response not in choices:
                response = input(question).lower()
                if response in choices:
                    return response
                print("Correct inputs:\n  >", end='')
                print("\n  >".join(choices), "\n")

    def start_game(self):
        """Main menu of game basically"""
        while True:
            choice = self.custom_input("Would you like to play, look at stats, or quit?:\n   ->", self.MENU)
            if choice == 'play':
            elif choice == 'stats':
                stats = self.Score.stats_board()

    def computer_choice_gen(self):
        """Generates computer choice"""
        return random.choice(Game.MOVES)

    def play_game(self):
        """Player vs Computer game"""
        rounds = int(self.custom_input("\nHow many rounds would you like to play?\n   ->", self.ROUND_OPTIONS))
        while self.Score['rounds'] != rounds:
            self.user_choice = self.custom_input("\nWhat is your choice, human?\n   ->", self.MOVES)
            self.computer_choice = self.computer_choice_gen()
            print("\nComputer choice is {}".format(self.computer_choice))
            result = self.evaluate()
            if result == "win":
                print("{} beats {}! The human wins this round.\n\n".format(self.user_choice, self.computer_choice))
            elif result == "loss":
                print("{} beats {}! Hahaha! You lost this round!\n\n".format(self.computer_choice, self.user_choice))
                print("I knew you were going to pick {}!\n\n".format(self.user_choice))
        with shelve.open("RPSv2.dat") as f:
            f["statistics"] = self.Score

    def evaluate(self):
        """returns if outcome of round"""
        if Game.WIN_CONDITIONS[self.user_choice] == self.computer_choice:
            return "win"
        elif self.user_choice == self.computer_choice:
            return "draw"
            return "loss"

if __name__ == "__main__":
    Game = Game()

1 Answer 1


Great to see the advice from the first question helped! I just have some small notes for this one.

Good to have docstrings everywhere, but you can use ''' for docstrings, which is easier to read than having to escape a double quote.

'''Stores all items related to "Score"'''

Speaking of docstings

def delirioussyntax(self):

That's really not clear what it does. If it was intended as an easter egg then it is fun to be confused, call the function and see it run. But since you're calling it then it's not really hidden. Give it a clearer name or a docstring that actually outlines what it does.

def print_creator_name(self):

In custom_input you've got two while loops, but really you just need the while True one. You have a return statement, so as soon as there's a valid input there, your function returns and the loop ends. There's no other time the loop should end, so you can otherwise loop indefinitely:

def custom_input(self, question, choices):
    """A custom loop that checks to see if choices are valid
    :param choices:
    :param question:

    while True:
        response = input(question).lower()
        if response in choices:
            return response
        print("Correct inputs:\n  >", end='')
        print("\n  >".join(choices), "\n")

Even though you're only incrementing by one, I'd personally say it's better to use less than to test in play_game. It makes more logical sense to the brain that you want to run while you have played fewer rounds than the full set. But it's marginally more confusing to think that you're playing rounds while the amount played isn't equal to the chosen amount. It's also a way to avoid potential bugs, in case the round amount is ever incremented by more than 1, you might forget to update this and accidentally create an infinite loop.

    while self.Score['rounds'] < rounds:
  • 1
    \$\begingroup\$ Thank you! Let's me know that my final program is almost complete for the RockPaperScissors. I have a big project that is going to be posted within the next few weeks. It is going to be an RPG with a bunch of little add-ons. Can't wait to see what all I have to fix on that. I spent 9 hours just on the level GUI that will pop up and I haven't even started on the cosmetics of the GUI (color, pictures, etc.) \$\endgroup\$ Nov 16, 2015 at 13:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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