import random
 
class Die:
    '''Represents a die
    sides: list of results the die can take
    value: current result showing on the die'''
 
    def __init__(self, sides):
        '''Die(sides) -> Die
        Constructs a Die object with the faces in list sides.  value
        attribute is set to sides[0].'''
        self.sides = sides      # list of possible die results
        self.value = sides[0]   # current result showing
 
    def __str__(self):
        '''str(Die) -> str
        Returns a string like:
          'A die with sides [1, 2, 3, 4, 5, 6] and value 1' '''
        output = 'A die with sides ' # start of string
        output += str( self.sides )  # add sides
        output += ' and value '      # more text
        output += str( self.value )  # add value
        return output                # return, not print
 
    def get_value(self):
        '''Die.get_value() -> value
        Returns the current value of the Die.'''
        return self.value
 
    def roll(self):
        '''Die.roll() -> obj
        Sets value to a random side and returns it.'''
        # Pick a random index
        randomIndex = random.randrange( len( self.sides ) )
        # Get the side
        self.value = self.sides[ randomIndex ]
        return self.value       # return new value
 
    def add(self, otherDie):
        '''Die.add(Die) -> obj
        Returns the sum of value attributes of two Die objects.'''
        # Return sum
        return self.value + otherDie.value
 
class Pig(Die):
    '''Creates a Pig object for Pass the Pigs'''
 
    def __init__(self):
        '''Pig() -> Pig
        Constructs a Pig die.'''
        # Approximate proportion of each result
        sides = ['left']*59 + ['right']*51 + ['back']*38 \
                  + ['feet']*15 +['nose']*5+['ear']*1
        Die.__init__(self, sides) # use Die constructor
 
    def __str__(self):
        '''str(Pig) -> str
        Returns the current value of the Pig.'''
        return self.value
 
    def get_value(self):
        '''Pig.get_value() -> str
        Returns the current value of the Pig'''
        return self.value
 
    def set_value(self, newValue):
        '''Pig.set_value(str)
        Sets value attribute to new value.'''
        self.value = newValue
 
    def is_pig_out(self, other):
        '''Pig.is_pig_out(Pig) -> bool
        Returns whether or not this is a Pig Out.'''
        results = (self.value, other.value)
        return 'left' in results and 'right' in results
 
    def add(self, other):
        '''Pig.add(Pig) -> int
        Returns the total value of a roll.'''
        if self.is_pig_out(other): # if a Pig Out
            return 0
 
        # For converting values to a number
        valueDict = {'left':0, 'right':0, 'back':5,
                     'feet':5, 'nose':10, 'ear':15}
 
        # Add the scores
        score = valueDict[self.value] + valueDict[other.value]
 
        # If double was rolled
        if self.value == other.value:
            # If Siders
            if self.value in ('left', 'right'):
                return 1
            else:               # other doubles
                score *= 2      # double score
 
        return score            # return score
 
    def get_combo(self, other):
        '''Pig.get_combo(Pig) -> str
        Returns the name of the combo formed by self and other.'''
        # Names of different results
        nameDict = {'back':'Razorback', 'feet':'Trotter', \
                    'nose':'Snouter', 'ear':'Leaning Jowler'}
 
        names = []              # will contain any non-side names
        for pig in (self,other): # for each Pig
            # If not a side
            if pig.value not in ('left','right'):
                # Add name to list
                names.append( nameDict[pig.value] )
 
        if len(names) == 0:     # if 2 sides
            # If doubles
            if self.value == other.value:
                return 'Siders' # Siders
            return 'Pig Out'    # else: Pig Out
 
        if len(names) == 1:     # if 1 side
            return names[0]     # return that name
 
        # Only cases left have 2 non-sides
        # If doubles
        if self.value == other.value:
            # Double the name
            return 'Double ' + names[0]
        return 'Mixed Combo'    # else: Mixed Combo
 
class Player(Die):
    '''Player in the Pass the Pigs game'''

    def __init__(self, name, score=0):
        '''Player(str) -> Player
        Sets name for player; score defaults to 0.'''
        self.__name = name
        self.__score = score

    def __str__(self):
        '''str(Player) -> str
        String for printing'''
        return self.__name + " has " + str(self.__score) + " points"

    def get_name(self):
        '''Player.get_name() -> str
        Returns name attribute.'''
        return self.__name

    def get_score(self):
        '''Player.get_score() -> int
        Returns score attribute.'''
        return self.__score

    def add_to_score(self, points):
        '''Player.add_to_score(int)
        Updates score by adding points.'''
        self.__score += points

    def has_won(self):
        '''Player.has_won() -> bool
        Determines if player has won (score 100 or more).'''
        return self.__score >= 100

    def stop_turn(self, points):
        '''Player.stop_turn(int) -> bool
        Allows the player to decide if he/she is going to stop.
        Returns True if player wants to stop.'''
        choice = input('Enter STOP to bank ' + str(points) + ' points, or anything else to keep going: ')
        return choice == 'STOP'

    def take_turn(self):
        '''Player.take_turn()
        Has player take one full turn.'''
        # Initialization
        pig1 = Pig() # create pigs
        pig2 = Pig()
        total = 0    # running total of points this turn
        stop = False # control while loop

        # Warn player it's the start of their turn
        input(self.__name + ", press ENTER to start your turn")

        while not stop: # while turn continues
            pig1.roll() # roll pigs
            top2 = pig2.roll() # save result
            # Print pigs
            print('You rolled ' + str(pig1) + ' and ' + top2)
            combo = pig1.get_combo(pig2) # name of combo
            value = pig1.add(pig2) # value of combo
            # Print result
            print('You scored ' + str(value) + ' with a ' + combo)

            if combo == 'Pig Out': # if Pig Out
                stop = True # end the turn
            else:
                total += value # update running total
                if self.stop_turn(total): # ask if they want to STOP
                    stop = True # stop the turn
                    self.add_to_score(total) # bank these points
# part A


class PlayerSet(Pig):

    def __init__(self, integer, name): # Contructor contains an integer attribute, and a list player attribute to store all players in a list.

        self.integer = integer

        self.name = input()

        pList = []

        for element in self.integer:

            if element == 1: # If only one player

                return "Player, please enter your name: " + self.name

                pList.append(name) # Append the entered name to a list

            if element > 1: # If more than one player

                return "Player, please enter your name: " + self.name

                pList.append(name) # Append the entered name(s) to a list
        
        
    def __str__(self):
        # Display the number of players playing as well as the list of those who are playing
        return 'The number of players playing is' + str(self.integer) + ' and here are the players who are playing: ' + str(pList) # 

    def get_curr_name(self):

        return str(self._name) # Display current player 

    def go_to_next_player(self, pNum): # pNum is used to continue to next player

        '''PlayerSet.go_to_next_player()
        Goes onto the next player.'''

        self.pNum = pNum 

        self._name = (self.integer + 1) % self.pNum

        return self._name

        
    def is_curr_winner(self):

        '''PlayerSet.is_curr_winner()
        Determines if the current player is a winner.'''

        if self._score >= 100:

            return self._name + "You have won!"

        elif self._score < 100:

            return self._name + "You do not have 100 points."

        

    def take_turn():

        '''PlayerSet.take_turn()
        Has player take one full turn.'''
        # Initialization
        pig1 = Pig() # create pigs
        pig2 = Pig()
        total = 0    # running total of points this turn
        stop = False # control while loop

        # Warn player it's the start of their turn
        input(self.__name + ", press ENTER to start your turn")

        while not stop: # while turn continues
            pig1.roll() # roll pigs
            top2 = pig2.roll() # save result
            # Print pigs
            print('You rolled ' + str(pig1) + ' and ' + top2)
            combo = pig1.get_combo(pig2) # name of combo
            value = pig1.add(pig2) # value of combo
            # Print result
            print('You scored ' + str(value) + ' with a ' + combo)

            if combo == 'Pig Out': # if Pig Out
                stop = True # end the turn
            else:
                total += value # update running total
                if self.stop_turn(total): # ask if they want to STOP
                    stop = True # stop the turn
                    self.add_to_score(total) # bank these points
        

# part B

    def play_pigs(self):

        self.__init__() # Verify players

        for player in self.integer:
        
            self.get_curr_name() # Get the current player's name

            self.take_turn() # Have current player take a turn

            self.is_pig_out() # See if player has a pig-out

            self.get_combo() # Calculate combo

            self.add() # Add points to bank

            self.is_curr_winner() # See if current player is a winner

            self.go_to_next_player() # go to next player

        self.play_pigs() # Repeat process
