Day 15 of 100 - Classes Day 3 of 3

Classes Day 3 of 3

Day 15 of 100

For this three day challenge, we dove head first into classes. We started out with an intro to classes by building a little D&D text adventure game along with host Michael. You create classes of characters in the game and then write methods for them to attack or defend, depending on the outcome of a dice roll. All of this takes place in while loop that continues to prompt the user until the hero wins or until the hero is defeated. It was a great game and a good, easy to follow demo of classes.

Day 2 we were given a scaffold of a Rock, Paper, Scissors text game to build using classes. It was harder for me initially because we used classes to build the players and the rolls. The players I understood because I could visualize that class and the attributes. The other class we created was for the rolls and the characteristics of each roll. This was a lot harder for me to grasp and it took a long time for me to really get the code right.

I did get stuck on how to assign the human player a roll object after getting their input and then how to apply the Roll class methods to that input. In the end, I had to actually modify my code to run backwards when awarding the winners and losers of each round because I had spent so much time on it. I knew there would be another day to refine code and I wanted to take some time to think more about how to implement the challenge. I also left input validation, handling of ties, and calculating a final winner to day 3 as well.

Day 2 Code rps.py

from classes import Player, Roll
import random
import time

def main():
    print_title()

    rolls = build_the_three_rolls()

    name = get_players_name()

    player1 = Player(name)
    player2 = Player("computer")

    game_loop(player1, player2, rolls)


def print_title():
    print('-' * 40)
    print()
    print(' ' * 9 + 'ROCK, PAPER, SCISSORS')
    print()
    print('-' * 40)
    print()

def get_players_name():
    name = input('What is your name: ')
    return name

def build_the_three_rolls():
    rolls = []
    rolls.append(Roll('Rock', 'Scissors', 'Paper'))
    rolls.append(Roll('Scissors', 'Paper', 'Rock'))
    rolls.append(Roll('Paper', 'Rock', 'Scissors'))
    return rolls

def game_loop(player1, player2, rolls):
    count = 1
    while count < 6:
        player2.roll = random.choice(rolls)
        #TODO: validate user input and store roll as object - correct can_defeat method
        player1.roll = input('Enter [Rock], [Paper], or [Scissors]: ')

        outcome = player2.roll.can_defeat(player1.roll)

        time.sleep(1)

        print(f'{player2.name} has rolled {player2.roll.name}')

        time.sleep(1)

        # TODO: add win counts and handle ties better
        if outcome == 'Win':
            print(f'{player1.roll} beats {player2.roll.name}, {player1.name} wins!')
        elif outcome == 'Lose':
            print(f'{player2.roll.name} beats {player1.roll}, {player2.name} wins!')
        else:
            print("It's a tie!")

        count += 1

    # TODO: Compute who won

if __name__ == '__main__':
    main()

Day 2 Code classes.py

class Player:
    def __init__(self, name):
        self.name = name
        self.roll = None
        self.wins = 0


class Roll:
    def __init__(self, name, cn_defeat, defeated_by):
        self.name = name
        self.cn_defeat = cn_defeat
        self.defeated_by = defeated_by

    def can_defeat(self, roll):
        if roll == self.cn_defeat:
            return 'Lose'
        elif roll == self.defeated_by:
            return 'Win'
        else:
            return 'Tie'

Day 3 was filled with refining code and try to fix what wasn't running correctly from day 2 and finish implementing all the requirements for the challenge. Assigning the human player the correct Roll object based on their input was the biggest challenge. My solution finally worked after I realized the three difference Roll objects were being passed to the game loop as a list. I had to iterate over the list to access each Roll object and assign the correct one to the user.

It also took longer than expected to fix the battle method that return a wine, lose, or tie. After lots of print statements and a little dive into the PyCharm debugger, I was able to sort out the methods, announce the winner of each round and calculate the final winner. All in a all a successful 3 days spent learning about classes.

Day 3 game code rps.py

from classes import Player, Roll
import random
import time

def main():
    print_title()

    rolls = build_the_three_rolls()

    name = get_players_name()

    player1 = Player(name)
    player2 = Player("RPS Bot")

    game_loop(player1, player2, rolls)


def print_title():
    print('-' * 40)
    print()
    print(' ' * 9 + 'ROCK, PAPER, SCISSORS')
    print()
    print('-' * 40)
    print()

def get_players_name():
    name = input('What is your name: ')
    return name

def build_the_three_rolls():
    rolls = []
    rolls.append(Roll('Rock', 'Scissors', 'Paper'))
    rolls.append(Roll('Scissors', 'Paper', 'Rock'))
    rolls.append(Roll('Paper', 'Rock', 'Scissors'))
    return rolls

def game_loop(player1, player2, rolls):

    # prompt the user for the number of rounds and validate the input
    while True:
        num_games = input(f'How many rounds do you want? ')
        try:
            val = int(num_games)
            if val > 0:
                num_games = int(num_games)
                break
            else:
                print("Please enter a positive number greater than 0.")
        except ValueError:
            print("Please enter an integer.")

    # set the round counter
    count = 0

    # convert the number of games to an int
    num_games = int(num_games)

    while count < num_games:

        # assign player 2 a random roll
        player2.roll = random.choice(rolls)

        # prompt the user for a roll and validate input
        player1.roll = input('Enter [R]ock, [P]aper, or [S]cissors: ')
        while True:
            if player1.roll == 'R':
                for roll in rolls:
                    if roll.name == 'Rock':
                        player1.roll = roll
                break
            elif player1.roll == 'P':
                for roll in rolls:
                    if roll.name == 'Paper':
                        player1.roll = roll
                break
            elif player1.roll == 'S':
                for roll in rolls:
                    if roll.name == 'Scissors':
                        player1.roll = roll
                break
            else:
                print("Input not recognized.")
                player1.roll = input('Enter [R]ock, [P]aper, or [S]cissors: ')

        outcome = player1.roll.can_defeat(player2.roll)

        time.sleep(1)

        # report the player 2 roll
        print(f'{player2.name} has rolled {player2.roll.name}')

        time.sleep(1)

        # award the win and increment the round if there is a winner
        if outcome == 'Win':
            player1.wins += 1
            count += 1
            print(f'{player1.roll.name} beats {player2.roll.name}, {player1.name} wins!')
            time.sleep(1)
            print(f'That concludes round {count}. {player1.name} has '
                  f'{player1.wins} wins and {player2.name} has {player2.wins} wins.')
        elif outcome == 'Lose':
            player2.wins += 1
            count += 1
            print(f'{player2.roll.name} beats {player1.roll.name}, {player2.name} wins!')
            time.sleep(1)
            print(f'That concludes round {count}. {player1.name} has '
                  f'{player1.wins} wins and {player2.name} has {player2.wins} wins.')
        else:
            print("It's a tie. Doesn't count!")

    # Compute the winner
    if player1.wins > player2.wins:
        winner = player1.name
    elif player2.wins > player1.wins:
        winner = player2.name
    else:
        winner = None

    # Report the results
    print(f'The final score is:')
    print(f'\t {player1.name} with {player1.wins} wins')
    print(f'\t {player2.name} with {player2.wins} wins')
    if winner:
        print(f'The winner is {winner}!')
    else:
        print(f'Looks like a tie. Next time, pick an odd number of rounds to avoid a tie.')

if __name__ == '__main__':
    main()

Day 3 code for classes.py

class Player:
    def __init__(self, name):
        self.name = name
        self.roll = None
        self.wins = 0


class Roll:
    def __init__(self, name, cn_defeat, defeated_by):
        self.name = name
        self.cn_defeat = cn_defeat
        self.defeated_by = defeated_by

    def can_defeat(self, roll):
        if roll.name == self.cn_defeat:
            return 'Win'
        elif roll.name == self.defeated_by:
            return 'Lose'
        else:
            return 'Tie'

And here is a 3 round game (with a couple ties):

----------------------------------------

         ROCK, PAPER, SCISSORS

----------------------------------------

What is your name: Clark
How many rounds do you want? 3
Enter [R]ock, [P]aper, or [S]cissors: R
RPS Bot has rolled Rock
It's a tie. Doesn't count!
Enter [R]ock, [P]aper, or [S]cissors: R
RPS Bot has rolled Scissors
Rock beats Scissors, Clark wins!
That concludes round 1. Clark has 1 wins and RPS Bot has 0 wins.
Enter [R]ock, [P]aper, or [S]cissors: S
RPS Bot has rolled Rock
Rock beats Scissors, RPS Bot wins!
That concludes round 2. Clark has 1 wins and RPS Bot has 1 wins.
Enter [R]ock, [P]aper, or [S]cissors: S
RPS Bot has rolled Scissors
It's a tie. Doesn't count!
Enter [R]ock, [P]aper, or [S]cissors: R
RPS Bot has rolled Rock
It's a tie. Doesn't count!
Enter [R]ock, [P]aper, or [S]cissors: R
RPS Bot has rolled Rock
It's a tie. Doesn't count!
Enter [R]ock, [P]aper, or [S]cissors: P
RPS Bot has rolled Paper
It's a tie. Doesn't count!
Enter [R]ock, [P]aper, or [S]cissors: R
RPS Bot has rolled Rock
It's a tie. Doesn't count!
Enter [R]ock, [P]aper, or [S]cissors: R
RPS Bot has rolled Rock
It's a tie. Doesn't count!
Enter [R]ock, [P]aper, or [S]cissors: R
RPS Bot has rolled Rock
It's a tie. Doesn't count!
Enter [R]ock, [P]aper, or [S]cissors: R
RPS Bot has rolled Scissors
Rock beats Scissors, Clark wins!
That concludes round 3. Clark has 2 wins and RPS Bot has 1 wins.
The final score is:
     Clark with 2 wins
     RPS Bot with 1 wins
The winner is Clark!

It's beginning to feel like the random.choice() method isn't so random...