raise class exception pass rock paper scissor game
Question: I have this hw here which I am currently stuck on. My professor provided the
class exception for us, but I was wondering is anyone can tell me how it works
and for some reason when I run this program I get this:

    
    
    Traceback (most recent call last):
      File "C:\Python34\lab3.1.py", line 32, in <module>
        rps_game_winner(game_2)
      File "C:\Python34\lab3.1.py", line 5, in rps_game_winner
        raise WrongNumberOfPlayersError('Wrong number of players!')
    WrongNumberOfPlayersError: Wrong number of players! 
    

so wondering if someone can tell me what i did wrong or what i can do to make
this program better. ty to everyone

    
    
    class WrongNumberOfPlayersError(Exception): pass
    class NoSuchStrategyError(Exception): pass
    
    def rps_game_winner(game):
        if len(game) != 2:
            raise WrongNumberOfPlayersError('Wrong number of players!')
            return print ("A WrongNumberOfPlayers error has occurred. \n\n")
    
        # Write the code for the NoSuchStratgyError
        stop_words = ['R', 'P', 'S']
        if stop_words not in game[0]:
            raise NoSuchStrategyError('No such strategy!')
    
        # Write the code to determine the winner
        # Write the code to return a List with the winner's name and strategy
        if ("R" in game[0]) and ("S" in game[1]):
            return print (game[0], "wins since Rock beat Scissor\n\n")
        elif ("P" in game[0]) and ("R" in game[1]):
            return print (game[0], "wins since Paper beat Rock\n\n")
        elif ("S" in game[0]) and ("P" in game[1]):
            return print (game[0], "wins since Scissor beat Paper\n\n")
        elif ("S" in game[1]) and ("P" in game[0]):
            return print (game[1], "wins since Scissor beat Paper\n\n")
        elif ("P" in game[1]) and ("R" in game[0]):
            return print (game[1], "wins since Paper beat Scissor\n\n")
        elif ("R" in game[1]) and ("S" in game[0]):
            return print (game[1], "wins since Rock beat Scissor\n\n")
    
    game = [["Armando", "P"], ["Dave", "S"]]# ["Dave", "S"] wins
    game_2 = [['Joe','R']] # wrong number of players
    game_3 = [['Joe','R'],['Walter','Q']] # no such strategy as 'Q'
    rps_game_winner(game)# returns the list ["Dave", "S"] wins since Scissors beat Paper
    rps_game_winner(game_2)# raises exception wrong number of players
    rps_game_winner(game_3)# raises exception no such strategy as 'Q'
    


Answer: I think you're having trouble understanding what an exception (or rather, an
`Exception`!) is. You define them for the purpose of understanding why your
code may have failed. For example:

    
    
    class WrongNumberOfPlayersError(Exception):
        pass
    

This is an `Exception` (literally, as it inherits from `Exception` so it can
do everything an `Exception` can do) that you've defined to know when you've
got the wrong number of players! Therefore

    
    
    players = [("Player 1", "S"), ("Player 2", "P"), ("Player 3", "R")]
    rps_game_winner(players)
    # this should raise an exception, since there are the WRONG NUMBER OF PLAYERS!
    

You handle these with `try/except` blocks (called `try/catch` in some
languages) as such:

    
    
    while True:
        players = get_players() # imagine a function that created this list,
                                # now you're looping over making a new one each
                                # time it's wrong
        try:
            rps_game_winner(players)
        except WrongNumberOfPlayersError as e:
            # handle the exception somehow. You only make it into this block if
            # there are the wrong number of players, and it's already looping forever
            # so probably just...
            pass
        else: # if there are no exceptions
            break # get out of the infinite loop!!
    

In your `rps_game_winner` function, you have the following logic:

    
    
    if len(game) != 2:
        raise WrongNumberOfPlayersError("Wrong number of players!")
        return print ("A WrongNumberOfPlayers error has occurred. \n\n")
    

This is why I think your understanding is slightly flawed. Once you `raise`
that exception, the function exits. It never reads the `return` line (which is
probably for the best since you can't `return` a print function, it's just
`None` for reasons that are outside the scope of this discussion. In Python 2
I believe this would cause your code to fail to run completely)

This of a function like a small machine that does Work for you ("Work" in this
case being some sort of computation, or running an algorithm, etc). Once the
machine finishes working, it `return`s the result of that Work. However if
something goes wrong, it should inform you that "Hey this isn't the result of
my work, this is Something Bad," so it `raise`s an exception instead.
Essentially: you can either `raise` if something goes wrong, or `return` if
everything goes right.

Note that there are more things wrong than this (e.g. you can NEVER throw a
`NoSuchStrategyError` with your current code) but that the basics of the issue
are in a misunderstanding of what exceptions are for.

Below is an overly abstracted bit of code that should accomplish what you want
it to. Keep in mind that I've purposely obfuscated some of the code so it's
unusable as a copy/paste. In particular, I'm rather proud of my implementation
of win/lose/draw :)

    
    
    R = 0b001
    P = 0b010
    S = 0b100
    
    class WrongNumberOfPlayersError(Exception): pass
    class NoSuchStrategyError(Exception): pass
    
    class RPSGame(object):
        def __init__(self, *players):
            try:
                self.p1, self.p2 = players
                # assume constructed as game('p1','p2')
            except Exception:
                try: self.p1, self.p2 = players[0]
                # assume constructed as game(['p1','p2'])
                except Exception:
                    raise WrongNumberOfPlayersError("Only two players per game")
                    # no more assumptions, raise that exception
        def start(self):
            print("{0.name} plays {0.human_choice} || {1.name} plays {1.human_choice}".format(
                self.p1, self.p2))
            def winner(p1, p2):
                global R, P, S
                wintable = {R: {R^S: 2, R^P: 1},
                            P: {P^R: 2, P^S: 1},
                            S: {S^P: 2, S^R: 1}}
                resulttable = ["Draw","Lose","Win"]
                return resulttable[wintable[p1.choice].get(p1^p2,0)] + " for {}".format(p1)
            return winner(self.p1, self.p2)
    
    class Player(object):
        rhyme_to_reason = {R:"Rock", P:"Paper", S:"Scissors"}
        def __init__(self, name, choice):
            self.name = name
            try: choiceU = choice.upper()
            except AttributeError:
                # choice is R, P, S not "R", "P", "S"
                choiceU = choice
            if choiceU not in ("R","P","S",R,P,S):
                raise NoSuchStrategyError("Must use strategy R, P, or S")
            choicetable = {"R":R,"P":P,"S":S}
            self.choice = choicetable.get(choiceU,choiceU)
            self.human_choice = Player.rhyme_to_reason[self.choice]
        def __xor__(self, other):
            if not isinstance(other, Player):
                raise NotImplementedError("Cannot xor Players with non-Players")
            return self.choice^other.choice
        def __hash__(self):
            return hash((self.name, self.choice))
        def __str__(self):
            return self.name
    
    if __name__ == "__main__":
        import random, itertools
        num_players = input("How many players are there? ")
        players = [Player(input("Player name: "), input("Choice: ") or random.choice([R,P,S])) for _ in range(int(num_players))]
        scoreboard = {player: 0 for player in players}
        for pairing in itertools.combinations(players, 2):
            game = RPSGame(pairing)
            result = game.start()
            if result.startswith("W"):
                scoreboard[pairing[0]] += 1
            elif result.startswith("L"):
                scoreboard[pairing[1]] += 1
            else:
                pass
            print(result)
        for player, wins in scoreboard.items():
            print("{:.<20}{}".format(player,wins))
    

