A Virtual Cockfight Example

From Knowledge Kitchen
Jump to navigation Jump to search


Here we have code that creates a virtual fight between two chickens.

  • The FuriousChicken class represents all the core functionality a chicken needs in order to fight, but it lacks any decision making in its make_your_move() function
  • The JitteryChicken class inherits everything from the FuriousChicken, and adds decision making code to its version of the make_your_move() function
  • the chicken_fight.py code sets up the fight and continues the fight until one of the chickens loses

FuriousChicken class

The FuriousChicken class contains all the necessary properties and methods to make a cockfighting chicken. This chicken is meant for breeding, and is intended to be sub-classed.

The make_your_move() method has been intentionally left blank - this method should be overridden in a sub-class and should contain the logic of what the chicken should do at any given moment.

"""This file contains the code for the FuriousChicken class."""
import random

class FuriousChicken:
    """Template for a warrior chicken who fights till the end.  This class is meant to be sub-classed and have its make_your_move() function overidden."""
    
    def __init__(self, name):
        """Initial setup of each FuriousChicken object, including its name, health, and list of body parts."""
        
        #set the chicken's name
        self.name = name

        #start off with most body parts
        self.body_parts = [
                            "left leg", "right leg",
                            "left foot", "right foot",
                            "left breast", "right breast",
                            "left wing", "right wing",
                            "left eye", "right eye",
                            "beak",
                            "wattle",
                            "comb"
                            ]

        #start out with each body part being totally healthy
        self.initialize_health(100)

        #start off not attacking or protecting any body parts (note the use of the None literal keyword indicating no value)
        self.reset_attack_and_defence()

        #start off with no opponent (note the use of the None literal keyword indicating no value)
        self.opponent = None

    def initialize_health(self, max_value):
        """set up the initial health values for each body part"""
        self.health = {} #empty dictionary

        #set an initial health value for each body part
        for body_part in self.get_body_parts():
            self.health[body_part] = max_value
    
    def set_health(self, body_part, x):
        """setter for health value

        Keyword arguments:
            x - the new health value 
        """
        #health is allowed to be a value between 0 and 100
        if x >=0 and x <= 100:
            #update this body part's health
            self.health[body_part] = x

    def get_health(self, body_part):
        """getter for health property

        Returns the current health property's int value
        """
        return self.health[body_part]

    def get_average_health(self):
        sum = 0
        #number should be hard-coded as if many parts are destroyed there will be BUG
        num_body_parts = 13
        for body_part in self.get_body_parts():
            sum += self.get_health(body_part)
            #num_body_parts += 1

        if num_body_parts >= 1:
            avg = sum / num_body_parts
        else:
            avg = 0
        return avg
    
    def get_attacking(self):
        """getter for attacking property

        Returns the current attacking property's string value
        """
        return self.attacking

    def get_defending(self):
        """getter for defending property

        Returns the current defending property's string value
        """
        return self.defending
        
    def adjust_health(self, body_part, x):
        """This method adjust the chicken's health by a certain amount

        Keyword arguments:
            x - the amount to adjust the health
        """
        #calculate the new health 
        current_health = self.get_health(body_part)
        new_health = current_health + x
        
        #prevent negative health which resulting in BUG
        if new_health < 0:
            new_health = 0

        #set the new health
        self.set_health(body_part, new_health)

    def set_opponent(self, opponent):
        """Sets the opponent of this chicken to be another chicken

        Keyword arguments:
            opponent - another chicken to fight
        """
        if isinstance(opponent, FuriousChicken):
            self.opponent = opponent
        else:
            print("Sorry, {} only fights other Furious Chickens or their descendents.".format(self.name))

    def get_opponent(self):
        """getter for opponent property

        Returns the current opponent property's value, which is most likely some kind of FuriousChicken object
        """
        return self.opponent

    def get_body_parts(self):
        """getter for the body_parts property

        Returns the current body_parts value, which is a list of body parts this chicken has
        """
        return self.body_parts

    def reset_attack_and_defence(self):
        """reset the attack and defence targets in preparation a round of fighting.  This sets the attacking and defending properties to non-values"""
        #set both attacking and defending to non-values... they must be explicitly set with every turn
        self.attacking = None
        self.defending = None

    def recuperate(self):
        """this method allows the chicken to regain a bit of strength by increasing its health value"""
        print("{} is resting".format(self.name))

        #give 5 point for resting
        for body_part in self.get_body_parts():
            self.adjust_health(body_part, 5)

    def defend(self, body_part):
        """defend the selected body part of the chicken against attacks by setting the defending property to the body part the chicken wants to protect

        Keyword arguments:
            body_part - a string representing the body part this chicken should protect
        """
        self.defending = body_part

        print("{} is defending its {} ({}%)".format(self.name, body_part, self.get_health(body_part)))
       
    def attack(self, body_part):
        """attack the opponent chicken at the selected body part

        Keyword arguments:
            body_part - a string representing the body part of the opponent that this chicken should attack
        """
        self.attacking = body_part #remember which body part we are attacking
        opponent = self.get_opponent() #get the opponent chicken in a simple variable

        #injure the selected body part, if it exists in the opponent's list of body parts
        if body_part in opponent.get_body_parts():
            #if the opponent has the selected body part, initiate an attack
            print("{} is attacking {}'s {} ({}%)!".format(self.name, opponent.name, body_part, opponent.get_health(body_part)))
            opponent.receive_attack(body_part)
        else:
            #if the opponent doesn't have the selected body part, the attack is futile!
            print("{} is trying to attack {}'s {}, but {} doesn't have a {}!!!".format(self.name, opponent.name, body_part, opponent.name, body_part))
            

    def receive_attack(self, body_part):
        """determine whether the attack will inflict damage

        Keyword arguments:
            body_part - a string representing the body part that this chicken is receiving an attack upon
        """
        opponent = self.get_opponent() #get the opponent chicken in a simple variable

        #as long as this chicken is not defending this body part, they will suffer an injury
        if body_part != self.defending:
            #if the chicken was not defending the attacked body part, inflict damage
            self.suffer_injury(body_part)
        else:
            #this chicken was defending the attacked body part
            print("{} defended against {}'s attack on its {} ({}%)!".format(self.name, opponent.name, body_part, self.get_health(body_part))) 

    def suffer_injury(self, body_part):
        """suffer injury on the selected body part on the chicken

        Keyword arguments:
            body_part - a string representing the body part that this chicken will suffer an injury upon
        """
        opponent = self.get_opponent() #get the opponent chicken in a simple variable

        #either injure or outright remove the selected body part, if it exists in this chicken's list of body parts
        if body_part in self.get_body_parts():

            health = self.get_health(body_part) #the health value for this body part
            self.adjust_health(body_part, -25) #subtract points for each attack
            health = self.get_health(body_part) #the health value for this body part
            
            if health <= 0:
                #if the health of this body part has dropped below zero, remove the body part
                self.remove_body_part(body_part)
                print("{}'s {} is completely out of action!!!".format(self.name, body_part))
            else:
                print("{} injured {}'s {} ({}%)".format(opponent.name, self.name, body_part, self.get_health(body_part)))

    def remove_body_part(self, body_part):
        """remove a body part from the chicken

        Keyword arguments:
            body_part - a string representing the body part that this chicken will completely lose
        """
        #remove the selected body part from this chicken's list of body parts
        self.body_parts.remove(body_part)        


    def make_your_move(self):
        """decide what move to make at any given round.  This function is intended to be customized in all sub-classes of this FuriousChicken class"""
        #you will create a sub-class of the FuriousChicken class
        #in that sub-class you will ovveride this make_your_move() function with your own customized version in order to make your chicken superior to all other chickens
        #this should be the only function you override from FuriousChicken... all other functions should be left as-is in the FuriousChicken class
        #your job is to make your chicken win by adding intelligent logic here

        #ultimately, this function must call one of the following other functions:
        # - self.attack(body_part), where body_part is a string representing the body part of the other chicken to attack
        # - self.defend(body_part), where body_part is a string representing the body part of this chicken to protect
        # - self.recuperate()

        #some ideas:
        # - use the various functions in this class to figure out what's going on with this chicken and the opponent chicken
        # - if your chicken is in bad shape, let it rest or defend itself!
        # - if the other chicken is in bad shape, perhaps attack more!
        # - don't try to attack body parts that don't exist... the body_parts list contains all body parts  that each chicken has available
        # - perhaps streamline your chicken so it doesn't have extraneous body parts that are vulnerabilities
        # - try to guess what the other chicken is about to do and pre-empt it!

        #which one of these functions you call at any given time, and which body part you protect or attack is the key to success

JitteryChicken class

This represents a sub-class of FuriousChicken. This sub-class contains the logic of what this type of chicken should do at any given moment.

Ideally, you will create your own sub-classes of FuriousChicken like this that has its own make_your_move() function. The make_your_move() function in this example simply decides randomly whether to attack, defend, or recuperate at any given time. If this were less random, and more based on what's happening to each chicken, perhaps this chicken would be a stronger performer in the fight.

Several functions in the FuriousChicken class could be useful to help this decision-making. The JitteryChicken, of course, has inherited all of these useful functions from FuriousChicken.

"""
This file contains the JitteryChicken class, which is a sub-class of FuriousChicken
The make_your_move() function of FuriousChicken has been overidden here
Modified from original author Foo Barstein
Date: 31 May 2016
"""

#import the necessary modules
import random
from FuriousChicken import *

class JitteryChicken(FuriousChicken):
    """Template for a random decision-making chicken descended from the original Furious Chicken"""

    #the only function you are allowed to override is the make_your_move function

    def make_your_move(self):
        """Overriding the FuriousChicken class's function by the same name.  This function randomly decides what this chicken should do each round"""

        #EXAMPLE ONLY... YOUR CHICKEN SHOULD MAKE ITS OWN DECISIONS WHAT TO DO
        
        #this chicken randomly decides whether it should attack, defend, or rest and recuperate
        what_to_do = random.randint(1,3)

        #randomly decide what body part to attack or defend
        #added a if statement to make sure there are actually body parts to pick from
        if len(self.get_body_parts()) != 0:
            num = random.randint(0, len(self.get_body_parts())-1) #a random number from 1 to however many body parts this chicken has
            body_part = self.body_parts[num] #select the random body part from the list of this chicken's body parts
        else:
            print("{} has no body parts left!!!".format(self.name))
            return
            
        if what_to_do == 1:
            #attack!
            self.attack(body_part)
        elif what_to_do == 2:
            #defend!
            self.defend(body_part)
        else:
            #rest and recuperate
            self.recuperate()

Chicken Fight

This represents the game logic that will be run. This can be used to have any two chickens fight each other, so long as each chicken is of a sub-class of FuriousChicken.

Don't modify this code. Just save this in a file named chicken_fight.py and run it:

"""This file contains code to create a virtual cockfight.  It instantiates two chicken objects and sets them to fight each other"""

#import some useful modules
import random
import time

#import the chicken classes we want to create
from JitteryChicken import *

#make two chicken objects - two JitteryChicken objects are created here for example.  
#We can create any type of chicken class, as long as it is a sub-class of FuriousChicken
#Ideally, we will create two different types of chickens to see which is superior
chick1 = JitteryChicken("Toker")
chick2 = JitteryChicken("Stoker")

#set the two chickens as opponents
chick1.set_opponent(chick2)
chick2.set_opponent(chick1)

#start the fight
round_num = 1
game_over = False
MAX_ROUNDS = 100

#loop until the game is over
while not game_over:
    #print out the round number
    print("\nROUND {}\n".format(round_num))
    
    #let the underdog go first 20% of the time
    num = random.randint(1,5)
    if num == 1:
        shake_it_up = True
    else:
        shake_it_up = False

    #figure out who goes first
    if chick1.get_average_health() == chick2.get_average_health():
        #if both chickens have equal health, choose who goes first randomly
        num = random.randint(1,2)
        if num == 1:
            #chick1 goes first
            chick1.make_your_move()
            chick2.make_your_move()
        else:
            #chick2 goes first
            chick2.make_your_move()
            chick1.make_your_move()
        
    elif (chick1.get_average_health() > chick2.get_average_health()) and not shake_it_up:
        # otherwise, chick1 goes first if it has higher health, except for 20% random exceptions
        #each chicken makes a move in this order
        chick1.make_your_move()
        chick2.make_your_move()
    else:
        # otherwise, chick2 goes first if it has higher health or this is a random 
        #each chicken makes a move in reverse order
        #make their moves
        chick2.make_your_move()
        chick1.make_your_move()

    #check for a win scenario
    if len(chick1.get_body_parts()) <= 0:
        #chick1 has expired... output who won
        print("\n{} the {} WINS!!!".format(chick2.name.upper(), type(chick2).__name__))
        print("\n{} the {} WINS!!!".format(chick2.name.upper(), type(chick2).__name__))
        game_over = True
    elif len(chick2.get_body_parts()) <= 0:
        #chick2 has expired... output who won
        print("\n{} the {} WINS!!!".format(chick1.name.upper(), type(chick1).__name__))
        print("\n{} the {} WINS!!!".format(chick1.name.upper(), type(chick1).__name__))
        game_over = True
    elif round_num >= MAX_ROUNDS:
        print("\nOUT OF TIME!!!")
        #check who wins in a timeout scenario
        if chick1.get_average_health() > chick2.get_average_health():
            #chick1 has won... output who won
            print("\n{} the {} WINS!!!".format(chick1.name.upper(), type(chick1).__name__))
            print("\n{} the {} WINS!!!".format(chick1.name.upper(), type(chick1).__name__))
        elif chick2.get_average_health() > chick1.get_average_health():
            #chick1 has won... output who won
            print("\n{} the {} WINS!!!".format(chick2.name.upper(), type(chick2).__name__))
            print("\n{} the {} WINS!!!".format(chick2.name.upper(), type(chick2).__name__))
        else:
            #tie game
            print("\nTIE!!!")
            print("\nTIE!!!")
        game_over = True        

    #if there is a win, output the remaining body parts on each chicken
    if game_over:
        print("\n{}'s ({}%) remaining body parts: {}\n".format(chick1.name, chick1.get_average_health(), ", ".join(chick1.get_body_parts())))
        print("\n{}'s ({}%) remaining body parts: {}\n".format(chick2.name, chick2.get_average_health(), ", ".join(chick2.get_body_parts())))
                
    #increase the round number
    round_num += 1

    #need to reset Chicken's attacking and defending to prevent BUG
    chick1.reset_attack_and_defence()
    chick2.reset_attack_and_defence()
    
    #pause so we can read what's happening
    time.sleep(4)

What links here