The following won't compile because Fighter doesn't have specialMove so in Engage we can't call
Solution
Lets add Fighter::specialMove
What do we expect in our output?
We would expect to see
but instead we get
The reason is because Fighter* p1,p2 are unaware of the specialMove overloaded function in Ninja and Zombie
Solution
Lets add virtual before Fighter::specialMove
And now we get
this allows use to call at run-time the overloaded function or in otherwords polymorphism
but wait????
Why are we getting Fighter special move shouldn't we be Ninja special move?
Maybe we should add override to the end of our function to see what happened
And we get this compile-time error
So I guess we forgot the const on Ninja. So I guess thats why they added override, one dumb mistake ruined our code.
Lets fix it now
And here we get:
Code
So we fixed our code by adding polymorphism using virtual and making sure we overrided the functions using override. It seems that Fighter::specialMove won't be used at all, so maybe we should make it abstract?
Pure Virtual Function
We can make Fighter::specialMove pure virtual function (or abstract) meaning that we must implement it in order to use our class by adding =0 :
From now on we can't create
Because now that Fighter contains a pure virtual function it is itself an abstract class but we can still create a pointer
So now we have the following:
Lets add some game features 😃
And now we have a working game 😃
Adding group matches (not on test)
Here we are going to add 2 teams of 3 and let them fight each other
Each fighter will switch randomly and fight another oponent each turn
Here all of team1 (Kotaro,bobzie and Loki) were defeated.
Notice the power that polymorphism gives us. We are fighting using 6 different objects of 3 different classes and using the same functions to do so. The Engage function will allow us to fight regardless of which type of sub-Fighter your are.
We could scale this up to 50 versus 50 matches easily
Wait!!!!
We forgot to deallocate (delete) the memory!!!! Well we can add the following to our main
But we get
So it seems we are calling the Fighters destructor but not each sub-Fighters. This is not what we wanted.
Lets take a closer look here
Solution
Lets add virtual to Fighter
Notice that we also are calling the fighters destructor.
A car is not an engine; it has one. And a coffee machine has a grinder and a brewing unit, but it is none of them. The car and the coffee machine integrate an engine, grinder and brewing unit via their external APIs to compose a higher level of abstraction and provide more significant value to their users.
You can do the same in software development when you design a class to keep a reference to an object and to use it in one or more of its methods.
The main reason to use composition is that it allows you to reuse code without modeling an is-a association as you do by using inheritance. It enables you to reuse code by modeling a has-a association between objects.
class Fighter{
...
public:
...
virtual void specialMove(Fighter& other) const =0;
};
class Ninja: public Fighter{
public:
...
void specialMove(Fighter& other) const override { cout << "Ninja special move\n"; }
};
class Zombie: public Fighter{
public:
...
void specialMove(Fighter& other) const override { cout << "Zombie special move\n"; }
};
#include <iostream>
using std::cout, std::endl, std::string;
class Fighter{
protected:
string name;
int hp;
int power;
protected:
void applyDamageTo(Fighter& target, int damage) const {
if (target.hp-damage>0)
target.hp-=damage;
else
target.hp = 0;
cout << target.name << " was inflicted " << damage << ", current hp: " <<target.hp << "\n";
}
public:
Fighter(const string &name, int hp, int power) : name(name), hp(hp), power(power) {}
const string& getName() const { return name;}
bool isAlive() const {return hp > 0;}
void attack(Fighter& other) const {
if(rand() % 6 >=3)
applyDamageTo(other, power);
}
virtual void specialMove(Fighter& other) const =0;
};
class Ninja: public Fighter{
public:
Ninja(const string &name, int hp=10000, int power=1) : Fighter(name, hp, power) {}
void specialMove(Fighter& other) const override {
if(rand() % 10 >=9){
cout << "Ninja special move\n";
applyDamageTo(other, power*5);
}
}
};
class Zombie: public Fighter{
public:
Zombie(const string &name, int hp=10000, int power=1) : Fighter(name, hp, power) {}
void specialMove(Fighter& other) const override {
if(rand() % 10 >=8){
cout << "Zombie special move\n";
applyDamageTo(other, power*3);
}
}
};
void Engage(Fighter& f1, Fighter& f2){
Fighter* p1 = &f1;
Fighter* p2 = &f2;
p1->attack(*p2);
p2->attack(*p1);
p1->specialMove(*p2);
p2->specialMove(*p1);
}
int main(){
Ninja f1("Hanzo");
Zombie f2("zombat");
while (f1.isAlive() && f2.isAlive()){
Engage(f1, f2);
}
cout << (f1.isAlive() ? f1.getName() : f2.getName()) << " won" << std::endl;
return 0;
}
Ninja special move
zombat was inflicted 5, current hp: 1333
zombat was inflicted 1, current hp: 1332
Hanzo was inflicted 1, current hp: 6
Hanzo was inflicted 1, current hp: 5
Zombie special move
Hanzo was inflicted 3, current hp: 2
zombat was inflicted 1, current hp: 1331
Hanzo was inflicted 1, current hp: 1
Hanzo was inflicted 1, current hp: 0
zombat won
#include <iostream>
#include <vector>
#include <algorithm>
using std::cout, std::endl, std::string, std::vector;
class Fighter{
protected:
string name;
int hp;
int power;
protected:
void applyDamageTo(Fighter& target, int damage) const {
if (target.hp-damage>0)
target.hp-=damage;
else
target.hp = 0;
cout << target.name << " was inflicted " << damage << ", current hp: " <<target.hp << "\n";
}
public:
Fighter(const string &name, int hp, int power) : name(name), hp(hp), power(power) {}
const string& getName() const { return name;}
bool isAlive() const {return hp > 0;}
void attack(Fighter& other) const {
if(rand() % 6 >=3)
applyDamageTo(other, power);
}
virtual void specialMove(Fighter& other) const =0;
~Fighter() { cout << "Destroying fighter\n";}
};
class Ninja: public Fighter{
public:
Ninja(const string &name, int hp=10000, int power=1) : Fighter(name, hp, power) {}
~Ninja() { cout << "Destroying Ninja\n";}
void specialMove(Fighter& other) const override {
if(rand() % 10 >=9){
cout << "Ninja special move\n";
applyDamageTo(other, power*5);
}
}
};
class Zombie: public Fighter{
public:
Zombie(const string &name, int hp=10000, int power=1) : Fighter(name, hp, power) {}
~Zombie() { cout << "Destroying Zombie\n";}
void specialMove(Fighter& other) const override {
if(rand() % 10 >=8){
cout << "Zombie special move\n";
applyDamageTo(other, power*3);
}
}
};
class Viking: public Fighter{
public:
Viking(const string &name, int hp=10000, int power=1) : Fighter(name, hp, power) {}
~Viking() { cout << "Destroying Viking\n";}
void specialMove(Fighter& other) const override {
if(rand() % 10 >=8){
cout << "Viking special move\n";
applyDamageTo(other, power*3);
}
}
};
void Engage(Fighter& f1, Fighter& f2){
Fighter* p1 = &f1;
Fighter* p2 = &f2;
p1->attack(*p2);
p2->attack(*p1);
p1->specialMove(*p2);
p2->specialMove(*p1);
}
bool alive_predicate(Fighter* f){
return f->isAlive();
}
int main(){
vector<Fighter*> team1 = {
new Ninja ("Hanzo"),
new Zombie ("zombat"),
new Viking ("Thor")
};
vector<Fighter*> team2 = {
new Ninja ("Kotaro"),
new Zombie ("bobzie"),
new Viking ("Loki")
};
while (std::any_of(team1.begin(), team1.end(), alive_predicate) && std::any_of(team2.begin(), team2.end(), alive_predicate) ){
//shuffle the group to fight different oponents
std::random_shuffle(team1.begin(), team1.end());
//partition alive and dead
std::partition(team1.begin(), team1.end(), alive_predicate);
std::random_shuffle(team2.begin(), team2.end());
std::partition(team2.begin(), team2.end(), alive_predicate);
for (int i=0; i< team1.size(); ++i){
Engage(*team1[i],*team2[i]);
cout << "------------" << endl;
}
cout << "============" << endl;
}
cout << (std::any_of(team1.begin(), team1.end(), alive_predicate) ? "team1 " : "team2 " ) << " wins" << std::endl;
return 0;
}
bobzie was inflicted 1, current hp: 0
...
============
Kotaro was inflicted 1, current hp: 1
Zombie special move
Kotaro was inflicted 3, current hp: 0
------------
Zombie special move
Hanzo was inflicted 3, current hp: 127
------------
Thor was inflicted 1, current hp: 48
Viking special move
Loki was inflicted 3, current hp: 0
------------
============
team1 wins
Process finished with exit code 0
class Fists : public Weapon{
public:
Fists(const string &name="fists") : Weapon(name) {}
int calculateDamage(const Attributes &attr) const override {
int num = rand() % 3;
if(num >=1) {
return attr.power + num;
}
}
};
class Sword : public Weapon{
public:
Sword(const string &name="sword") : Weapon(name) {}
int calculateDamage(const Attributes &attr) const override {
int num = rand() % 10;
if(num >=8) {
return attr.power + num;
}
}
};
class Bat : public Weapon{
public:
Bat(const string &name="bat") : Weapon(name) {}
int calculateDamage(const Attributes &attr) const override {
int num = rand() % 6;
if(num >=3) {
return attr.power + num;
}
}
};
class Ninja: public Fighter{
public:
Ninja(const string &name, int hp=10000,int speed=3, int power=1, Weapon* pWeapon= nullptr)
: Fighter(name, hp,speed, power,pWeapon) {}
...
}
int main(){
vector<Fighter*> team1 = {
new Ninja ("Hanzo", new Sword),
new Zombie ("zombat", new Fists),
new Viking ("Thor", new Bat)
};
vector<Fighter*> team2 = {
new Ninja ("Kotaro", new Sword),
new Zombie ("bobzie", new Fists),
new Viking ("Loki", new Bat)
};
...
}
============
Thor attacks Loki with his bat
Loki was inflicted 2, current hp: 111
Loki attacks Thor with his bat
Thor was inflicted 6, current hp: 0
Viking special move
Thor was inflicted 3, current hp: 0
------------
zombat attacks Kotaro with his fists
Kotaro was inflicted 0, current hp: 0
Kotaro attacks zombat with his sword
zombat was inflicted 1, current hp: 0
Zombie special move
Kotaro was inflicted 3, current hp: 0
------------
Hanzo attacks bobzie with his sword
bobzie was inflicted 4, current hp: 0
bobzie attacks Hanzo with his fists
Hanzo was inflicted 0, current hp: 0
------------
============
team2 wins
Destroying Viking
Destroying fighter
...
#include <iostream>
#include <vector>
#include <algorithm>
using std::cout, std::endl, std::string, std::vector;
struct Attributes{
int hp,speed,power;
Attributes(int hp, int speed, int power) : hp(hp), speed(speed), power(power) {}
};
class Weapon{
string name;
public:
Weapon(const string &name) : name(name) {}
const string &getName() const {return name;}
virtual int calculateDamage(const Attributes& attr) const =0;
};
class Fists : public Weapon{
public:
Fists(const string &name="fists") : Weapon(name) {}
int calculateDamage(const Attributes &attr) const override {
int num = rand() % 3;
if(num >=1) {
return attr.power + num;
}
}
};
class Sword : public Weapon{
public:
Sword(const string &name="sword") : Weapon(name) {}
int calculateDamage(const Attributes &attr) const override {
int num = rand() % 10;
if(num >=8) {
return attr.power + num;
}
}
};
class Bat : public Weapon{
public:
Bat(const string &name="bat") : Weapon(name) {}
int calculateDamage(const Attributes &attr) const override {
int num = rand() % 6;
if(num >=3) {
return attr.power + num;
}
}
};
class Fighter{
Weapon* pWeapon = nullptr;
protected:
string name;
Attributes attr;
protected:
void applyDamageTo(Fighter& target, int damage) const {
if (target.attr.hp-damage>0)
target.attr.hp-=damage;
else
target.attr.hp = 0;
cout << target.name << " was inflicted " << damage << ", current hp: " <<target.attr.hp << "\n";
}
public:
Fighter(const string &name, int hp, int speed, int power, Weapon* w= nullptr) :
name(name),
attr(hp,speed,power),
pWeapon(w){}
const string& getName() const { return name;}
bool isAlive() const {return attr.hp > 0;}
void attack(Fighter& other) const {
cout << name << " attacks " << other.name << " with his " << pWeapon->getName() << endl;
applyDamageTo(other, pWeapon->calculateDamage(attr));
}
void giveWeapon(Weapon* pNewWeapon){
delete pWeapon;
pWeapon = pNewWeapon;
}
virtual void specialMove(Fighter& other) const =0;
virtual ~Fighter() {
delete pWeapon;
cout << "Destroying fighter\n";
}
};
class Ninja: public Fighter{
public:
Ninja(const string &name,Weapon* pWeapon= nullptr, int hp=10000,int speed=3, int power=1) : Fighter(name, hp,speed, power,pWeapon) {}
~Ninja() { cout << "Destroying Ninja\n";}
void specialMove(Fighter& other) const override {
if(rand() % 10 >=9){
cout << "Ninja special move\n";
applyDamageTo(other, attr.power*5);
}
}
};
class Zombie: public Fighter{
public:
Zombie(const string &name, Weapon* pWeapon= nullptr, int hp=10000,int speed=3, int power=1) : Fighter(name, hp,speed, power,pWeapon) {}
~Zombie() { cout << "Destroying Zombie\n";}
void specialMove(Fighter& other) const override {
if(rand() % 10 >=8){
cout << "Zombie special move\n";
applyDamageTo(other, attr.power*3);
}
}
};
class Viking: public Fighter{
public:
Viking(const string &name,Weapon* pWeapon= nullptr, int hp=10000,int speed=3, int power=1) : Fighter(name, hp,speed, power,pWeapon) {}
~Viking() { cout << "Destroying Viking\n";}
void specialMove(Fighter& other) const override {
if(rand() % 10 >=8){
cout << "Viking special move\n";
applyDamageTo(other, attr.power*3);
}
}
};
void Engage(Fighter& f1, Fighter& f2){
Fighter* p1 = &f1;
Fighter* p2 = &f2;
p1->attack(*p2);
p2->attack(*p1);
p1->specialMove(*p2);
p2->specialMove(*p1);
}
bool alive_predicate(Fighter* f){
return f->isAlive();
}
int main(){
vector<Fighter*> team1 = {
new Ninja ("Hanzo", new Sword),
new Zombie ("zombat", new Fists),
new Viking ("Thor", new Bat)
};
vector<Fighter*> team2 = {
new Ninja ("Kotaro", new Sword),
new Zombie ("bobzie", new Fists),
new Viking ("Loki", new Bat)
};
while (std::any_of(team1.begin(), team1.end(), alive_predicate) && std::any_of(team2.begin(), team2.end(), alive_predicate) ){
//shuffle the group to fight different oponents
std::random_shuffle(team1.begin(), team1.end());
//partition alive and dead
std::partition(team1.begin(), team1.end(), alive_predicate);
std::random_shuffle(team2.begin(), team2.end());
std::partition(team2.begin(), team2.end(), alive_predicate);
for (int i=0; i< team1.size(); ++i){
Engage(*team1[i],*team2[i]);
cout << "------------" << endl;
}
cout << "============" << endl;
}
cout << (std::any_of(team1.begin(), team1.end(), alive_predicate) ? "team1 " : "team2 " ) << " wins" << std::endl;
for (int i=0; i< team1.size(); ++i){
delete team1[i];
delete team2[i];
}
return 0;
}