Composition and Hero battle implementation

 Composition

  • A way to create objects made up of other objects.
  • In composition, a class contains one or more objects of another class as instance variables.
  • Provide layered functionality to the object.
  • Known as a HAS-A relationship

Code example:

Engine class

class Engine:
    def __init__(self, engineType):
        self.engineType = engineType

    def startEngine(self):
        print("Engine is running")

    def stopEngine(self):
        print("Engine is off")


class Vehicle:
    def __init__(self, type, forSale, engine):
        self.type = type
        self.forSale = forSale
        self.engine = engine


Sample code to validate it.
engine = Engine("V6")
vehicle = Vehicle("Car", True, engine)
vehicle.engine.startEngine()

  • Using composition here because the vehicle HAS-A engine.
  • This is different from an interface IS-A relationship.
  • A vehicle must have a engine, but an engine does not need to have a vehicle.

Composition implementation?

  • We will create a new Hero class
  • We will create a new Weapon class
  • Our Hero will have a HAS-A(composition) relationship with our Weapon class

Code sample:

class Weapon:
    def __init__(self, weapon_type, attack_increase):
        self.weapon_type = weapon_type
        self.attach_increase = attack_increase


class Hero:
    def __init__(self, health_points, attack_damage):
        self.health_points = health_points
        self.attack_damage = attack_damage
        self.is_weapon_equipped = False
        self.weapon: Weapon = None

def equipWeapon(self):
    if self.weapon is not None and not self.is_weapon_equipped:
        self.attack_damage += self.weapon.attack_increase
        self.is_weapon_equipped = True

main.py

def hero_battle(hero: Hero, enemy: Enemy):
    while hero.health_points > 0 and enemy.health_points > 0:
        enemy.special_attak()
        enemy.attack()
        hero.health_points -= enemy.attack_damage
        hero.attack()
        enemy.health_points -= hero.attack_damage

        if hero.health_points > 0:
            print("Hero Wins!")
        else:
            print("Enemy2 Wins!")

zombie = Zombie(10,1)
hero = Hero(10,1)
weapon = Weapon('Sword', 5)
hero.weapon = weapon
hero.equip_weapon()
hero_battle(hero, zombie)

Running code example:

Enemy class:
# Parent class Enemy is also known as super class or base class.

class Enemy:

    def __init__(self, typeOfEnemy: str,
                 healthPoints: int = 10,
                 attack_damage: int = 1):
        self.__typeOfEnemy = typeOfEnemy
        self.healthPoints = healthPoints
        self.attack_damage = attack_damage

    def get_typeOfEnemy(self):
        return self.__typeOfEnemy
   
    def talk(self):
        print(f"I am a {self.__typeOfEnemy}. Be prepared to fight!")

    def walk_forward(self):
        print(f"{self.__typeOfEnemy} moves closer to you!")

    def attack(self):
        print(f"{self.__typeOfEnemy} attacks you for {self.attack_damage} damage!")

    def special_attack(self):
        print(f"{self.__typeOfEnemy} has no special attack!")

Hero class:
# hero class file

from Weapon import Weapon

class Hero:
    def __init__(self, health_points, attack_damage):
        self.health_points = health_points
        self.attack_damage = attack_damage
        self.is_weapon_equipped = False
        self.weapon: Weapon = None
        print(f'New hero created! Health: {self.health_points}, Attack Damage: {self.attack_damage}')

    def equip_weapon(self):
        if self.weapon is not None and not self.is_weapon_equipped:
            self.attack_damage += self.weapon.attack_increase
            self.is_weapon_equipped = True
            print(f'Hero has equipped a {self.weapon.weapon_type}.')

    def attack(self):
        print(f'Hero attacks for {self.attack_damage} damage!')

Ogre class:
# Child class of Enemy Ogre
from Enemy import Enemy
import random

class Orge(Enemy):
    def __init__(self, healthPoints, attack_damage):
        super().__init__(typeOfEnemy='Ogre', healthPoints=healthPoints,
                         attack_damage=attack_damage)
        self.healthPoints = healthPoints
        self.attack_damage = attack_damage

    # method overriding
    # This method is overriding the talk method of parent class Enemy
    def talk(self):
        print(f"Ogre: Smash them!")

    def special_attack(self):
        did_special_attack_work = random.random() < 0.20
        if did_special_attack_work:
            self.attack_damage += 4
            print('Ogre attack has increased by 4!')

Weapon class:
# Weapon class file
class Weapon:
    def __init__(self, weapon_type: str, attack_increase: int):
        self.weapon_type = weapon_type
        self.attack_increase = attack_increase
        print(f'New weapon created! Type: {self.weapon_type}, Attack Increase:
{self.attack_increase}')

Zombie class:
# Child class of Enemy Zombie
from Enemy import Enemy
import random


class Zombie(Enemy):
    def __init__(self, healthPoints, attack_damage):
        super().__init__(typeOfEnemy='Zombie', healthPoints=healthPoints,
                         attack_damage=attack_damage)
        self.healthPoints = healthPoints
        self.attack_damage = attack_damage

    # method overriding
    # This method is overriding the talk method of parent class Enemy
    def talk(self):
        print(f"Zombie: Braaaains!")

    #  new method specific to Zombie class
    def spread_diesease(self):
        print(f"Zombie: Spreading disease!")

    def special_attack(self):
        did_special_attack_work = random.random() < 0.5
        if did_special_attack_work:
            self.healthPoints += 2
            print('Zombie regerated 2 HP!')

main2.py
from Enemy import Enemy
from Zombie import Zombie
from Ogre import Orge
from Hero import Hero
from Weapon import Weapon

def battle(e1: Enemy, e2: Enemy): # We are passing parent object as parameter
    e1.talk()
    e2.talk()

    while e1.healthPoints > 0 and e2.healthPoints > 0:
        e1.special_attack()
        e2.special_attack()
        e2.attack()
        e1.healthPoints -= e2.attack_damage
        e1.attack()
        e2.healthPoints -= e1.attack_damage

        if e1.healthPoints > 0:
            print("Enemy 1 wins!")
        else:
            print("Enemy 2 wins!")

zombie = Zombie(healthPoints=15, attack_damage=3)
ogre = Orge(healthPoints=25, attack_damage=7)

#battle(zombie, ogre)

def hero_battle(hero: Hero, enemy: Enemy): # We are passing parent object as parameter
    while hero.health_points > 0 and enemy.healthPoints > 0:
        print('-------------')
        print(f'Hero: {hero.health_points} HP left')
        print(f'{enemy.get_typeOfEnemy()}: {enemy.healthPoints} HP left')
        print('-------------')
        enemy.attack()
        hero.health_points -= enemy.attack_damage
        hero.attack()
        enemy.healthPoints -= hero.attack_damage

        if hero.health_points > 0:
            print("Hero wins!")
        else:
            print(f"{enemy.get_typeOfEnemy()} wins!")

hero = Hero(health_points=20, attack_damage=1)
weapon = Weapon(weapon_type='Sword', attack_increase=15)
hero.weapon = weapon
hero.equip_weapon()

hero_battle(hero, ogre)


Comments

Popular posts from this blog

Post Request with Pydantic usage (input validator)

Code Scalability and Routing

CRUD Assignment