January 23, 2012

A Basic Object Oriented Programming Exercise

A primary advantages of using Python over other computational programs (i.e. Matlab, Maple, etc.) is the ease of  object oriented programming.  Thus Python combines the computational muscle of your favorite number crunching program with the flexibility of languages like C or Java. This post is not meant to be a step-by-step guide to object oriented programming, but rather a quick overview.

Classes: I think of classes as the machines that make objects. It specifies the methods and attributes that you want all of your objects to have. The object that we make below is a "player" in a game of rock-paper-scissors. We create new player objects with the first of our methods that begins with the line:


def __init__(self,name):

Every "self.xxxx" line included in the method specifies an "attribute", which can be thought of as a "rule for being an object of this sort." In the example below we require that each player has a name, strategy, and a tally of their wins and losses. If I make and object with the name Josh

Josh = player('Josh')

and I want to see how many wins he has, I input:

Josh.wincount


Because this is an attribute instead of a method, I do not need parenthesis [i.e. Josh.wincount( )].

The example below includes three methods that all player objects. Players can add to their win-count, they can train themselves so that their strategy is more competitive, and they can (based on the probabilities given in their strategy attribute) randomly generate rock, paper, or scissors. A few comments on these:
  • The players "rock,paper,scissors" strategy is represented by an array of three numbers between zero and one that sum to one. Players are initially given random strategy, but if you really think that rock will win every time you can give your player that strategy after he/she has been created. Each entry in the array represents the relative frequency for rock, paper, or scissors respectively. The train method improves the strategy by bringing it closer to ([1/3, 1/3, 1/3]) (this represents a Nash equilibrium for rock, paper, scissors).
  • To determine what "weapon" a player will choose to use based on a random number, we use the following line of code:

    choice         = sp.argmax(sp.cumsum(self.strategy)>k)
    

    Because our three strategy numbers sum to one, the sp.cumsum() command gives us an array that divides the interval (0,1) into three "buckets" of sizes equal to the three strategy numbers. The >k command turns this array into "true" and "false" entries, depending on whether k exceeded the upper limit of the bucket. The sp.argmax command gives the index for the first true entry.
    Inheritance: One advantage of creating classes to make our objects is that we can very quickly create special cases. In our program, we want to make a player that will always win. To do this, we include a "chuckNorris" class that inherits the methods of the "playerClass" class. To make the "chuckNorris" class unique, we override the "fight" method. Thus a player of the "chuckNorris" class will still have a random strategy and will keep track of his/her wins and losses. The neat part about inheritance is that we don't have to specify any of those common elements.

    The entire code for making our players is below:

    class playerClass:
        # This is called whenever a new player is created
        def __init__(self):
            # Create and initiallized attributes.
            # Strategy is mostly random at first.
            # Strategy can improve their via the "train" method.
            # Strategy is a length three array that sums to one
            # Each entry in the array corresponds to the  
            # probability of choosing rock, paper, or scissors. 
            r = sp.random.rand()
            self.strategy   = sp.array([r,(1-r)/2.,(1-r)/2.])
            self.wincount   = 0
            self.losscount  = 0
            
            
        def addwin(self,w):
            #This method keeps tracks of wins and losses
            if w == 1:
                self.wincount    +=1
                if self.wincount ==5:
                    print "The winner is a Jedi Master!"
            if w == 0:
                self.losscount   += 1
                if self.losscount > 5:
                    print "Maybe the loser should get some training"
                
        def train(self):
            #This method improves the players strategy
            print('Hitting the gym!')
            correction = (self.strategy[sp.argmax(self.strategy)] -self.strategy[sp.argmin(self.strategy)] )/3.
            self.strategy[sp.argmin(self.strategy)] += correction
            self.strategy[sp.argmax(self.strategy)] -= correction
            
        def fight(self):
            #This method chooses rock, paper, or scissors 
            #It is determined probabilistically based on the players strategy
            k     = sp.random.rand()
            choice         = sp.argmax(sp.cumsum(self.strategy)>k)
            weapon         = "scissors"
            if choice     ==0:
                weapon     = "rock"
            if choice     ==1:
                weapon     = "paper"
            elif choice ==2:
                weapon     = "scissors"
            return weapon
            
            
    class chuckNorris(playerClass):
            
        def train(self):
            print self.strategy
            
        def fight(self):
            weapon = "Roundhouse kick to the face"
            return weapon
    


    To actually make players and pit them against each other, use the following code as a separate file ("rpsFile.py" if you want to run this code as is). One neat thing we do here is use recursion within the rockpaperscissors( ) method in the event of a draw. That reduces the number of rock/paper/scissors combinations we need to hard code.

    #---------------------------------------------------
    # This file creates player objects and pits them
    # against one another in fierce games of rock-paper-scissors
    # Those who win can become jedi masters
    #---------------------------------------------------
    import scipy as sp
    from rpsFile import playerClass as player
    from rpsFile import chuckNorris as killer
    
    ben      = player()
    jeff     = player()
    jared    = player()
    ryan     = player()
    jonathan = player()
    matt     = player()
    jess     = killer()
    
    
    #---------------------------------------------------
    # Now we hold the rockpaperscissors contest
    #---------------------------------------------------
    def rockpaperscissors(player1, player2):
        p1 = player1.fight(); print p1
        p2 = player2.fight(); print p2
        if p1 == "Roundhouse kick to the face":
            player1.addwin(1)
            player2.addwin(0)
            print "player1 wins"
        if p2 == "Roundhouse kick to the face":
            player1.addwin(0)
            player2.addwin(1)
            print "player2 wins"
        if p1 == "rock" and p2 =="scissors":
            player1.addwin(1)
            player2.addwin(0)
            print "player1 wins"
        if p1 == "rock" and p2 =="paper":
            player1.addwin(0)
            player2.addwin(1)
            print "player2 wins"
        if p1 == "paper" and p2 =="scissors":
            player1.addwin(0)
            player2.addwin(1)
            print "player2 wins"
        if p1 == "paper" and p2 =="rock":
            player1.addwin(1)
            player2.addwin(0)
            print "player1 wins"
        if p1 == "scissors" and p2 =="paper":
            player1.addwin(1)
            player2.addwin(0)
            print "player1 wins"
        if p1 == "scissors" and p2 =="rock":
            player1.addwin(0)
            player2.addwin(1)
            print "player2 wins"
        if p1 == p2:
            print "draw"
            rockpaperscissors(player1,player2)
    

    It really is basic, and could be cleaner I guess, but here it is.

    No comments:

    Post a Comment