Problems Allocating Objects of Derived Class Where Base Class has Abstract Virtual Functions
Posted
by
user1743901
on Stack Overflow
See other posts from Stack Overflow
or by user1743901
Published on 2012-11-24T23:02:23Z
Indexed on
2012/11/24
23:03 UTC
Read the original article
Hit count: 321
I am trying to get this Zombie/Human agent based simulation running, but I am having problems with these derived classes (Human and Zombie) who have parent class "Creature". I have 3 virtual functions declared in "Creature" and all three of these are re-declared AND DEFINED in both "Human" and "Zombie". But for some reason when I have my program call "new" to allocate memory for objects of type Human or Zombie, it complains about the virtual functions being abstract. Here's the code:
definitions.h
#ifndef definitions_h
#define definitions_h
class Creature;
class Item;
class Coords;
class Grid
{
public:
Creature*** cboard;
Item*** iboard;
int WIDTH;
int HEIGHT;
Grid(int WIDTHVALUE, int HEIGHTVALUE);
void FillGrid(); //initializes grid object with humans and zombies
void Refresh(); //calls Creature::Die(),Move(),Attack(),Breed() on every square
void UpdateBuffer(char** buffer);
bool isEmpty(int startx, int starty, int dir);
char CreatureType(int xcoord, int ycoord);
char CreatureType(int startx, int starty, int dir);
};
class Random
{
public:
int* rptr;
void Print();
Random(int MIN, int MAX, int LEN);
~Random();
private:
bool alreadyused(int checkthis, int len, int* rptr);
bool isClean();
int len;
};
class Coords
{
public:
int x;
int y;
int MaxX;
int MaxY;
Coords() {x=0; y=0; MaxX=0; MaxY=0;}
Coords(int X, int Y, int WIDTH, int HEIGHT) {x=X; y=Y; MaxX=WIDTH; MaxY=HEIGHT; }
void MoveRight();
void MoveLeft();
void MoveUp();
void MoveDown();
void MoveUpRight();
void MoveUpLeft();
void MoveDownRight();
void MoveDownLeft();
void MoveDir(int dir);
void setx(int X) {x=X;}
void sety(int Y) {y=Y;}
};
class Creature
{
public:
bool alive;
Coords Location;
char displayletter;
Creature() {Location.x=0; Location.y=0;}
Creature(int i, int j) {Location.setx(i); Location.sety(j);}
virtual void Attack() =0;
virtual void AttackCreature(Grid G, int attackdirection) =0;
virtual void Breed() =0;
void Die();
void Move(Grid G);
int DecideSquare(Grid G);
void MoveTo(Grid G, int dir);
};
class Human : public Creature
{
public:
bool armed; //if armed, chances of winning fight increased for next fight
bool vaccinated; //if vaccinated, no chance of getting infected
int bitecount; //if a human is bitten, bite count is set to a random number
int breedcount; //if a human goes x steps without combat, will breed if next to a human
int starvecount; //if a human does not eat in x steps, will die
Human() {displayletter='H';}
Human(int i, int j) {displayletter='H';}
void Attack(Grid G);
void AttackCreature(Grid G, int attackdirection);
void Breed(Grid G); //will breed after x steps and next to human
int DecideAttack(Grid G);
};
class Zombie : public Creature
{
public:
Zombie() {displayletter='Z';}
Zombie(int i, int j) {displayletter='Z';}
void Attack(Grid G);
void AttackCreature(Grid G, int attackdirection);
void Breed() {} //does nothing
int DecideAttack(Grid G);
void AttackCreature(Grid G, int attackdirection);
};
class Item
{
};
#endif
definitions.cpp
#include <cstdlib>
#include "definitions.h"
Random::Random(int MIN, int MAX, int LEN) //constructor
{
len=LEN;
rptr=new int[LEN]; //allocate array of given length
for (int i=0; i<LEN; i++)
{
int random;
do {
random = rand() % (MAX-MIN+1) + MIN;
} while (alreadyused(random,LEN,rptr));
rptr[i]=random;
}
}
bool Random::alreadyused(int checkthis, int len, int* rptr)
{
for (int i=0; i<len; i++)
{
if (rptr[i]==checkthis)
return 1;
}
return 0;
}
Random::~Random()
{
delete rptr;
}
Grid::Grid(int WIDTHVALUE, int HEIGHTVALUE)
{
WIDTH = WIDTHVALUE;
HEIGHT = HEIGHTVALUE;
//builds 2d array of creature pointers
cboard = new Creature**[WIDTH];
for(int i=0; i<WIDTH; i++)
{
cboard[i] = new Creature*[HEIGHT];
}
//builds 2d array of item pointers
iboard = new Item**[WIDTH];
for (int i=0; i<WIDTH; i++)
{
iboard[i] = new Item*[HEIGHT];
}
}
void Grid::FillGrid()
{
/* For each creature pointer in grid, randomly selects whether to initalize
as zombie, human, or empty square. This methodology can be changed to initialize
different creature types with different probabilities */
int random;
for (int i=0; i<WIDTH; i++)
{
for (int j=0; j<HEIGHT; j++)
{
Random X(1,100,1); //create a single random integer from [1,100] at X.rptr
random=*(X.rptr);
if (random < 20)
cboard[i][j] = new Human(i,j);
else if (random < 40)
cboard[i][j] = new Zombie(i,j);
else
cboard[i][j] = NULL;
}
} //at this point every creature pointer should be pointing to either
//a zombie, human, or NULL with varying probabilities
}
void Grid::UpdateBuffer(char** buffer)
{
for (int i=0; i<WIDTH; i++)
{
for (int j=0; j<HEIGHT; j++)
{
if (cboard[i][j])
buffer[i][j]=cboard[i][j]->displayletter;
else
buffer[i][j]=' ';
}
}
}
bool Grid::isEmpty(int startx, int starty, int dir)
{
Coords StartLocation(startx,starty,WIDTH,HEIGHT);
switch(dir)
{
case 1:
StartLocation.MoveUp();
if (cboard[StartLocation.x][StartLocation.y])
return 0;
case 2:
StartLocation.MoveUpRight();
if (cboard[StartLocation.x][StartLocation.y])
return 0;
case 3:
StartLocation.MoveRight();
if (cboard[StartLocation.x][StartLocation.y])
return 0;
case 4:
StartLocation.MoveDownRight();
if (cboard[StartLocation.x][StartLocation.y])
return 0;
case 5:
StartLocation.MoveDown();
if (cboard[StartLocation.x][StartLocation.y])
return 0;
case 6:
StartLocation.MoveDownLeft();
if (cboard[StartLocation.x][StartLocation.y])
return 0;
case 7:
StartLocation.MoveLeft();
if (cboard[StartLocation.x][StartLocation.y])
return 0;
case 8:
StartLocation.MoveUpLeft();
if (cboard[StartLocation.x][StartLocation.y])
return 0;
}
return 1;
}
char Grid::CreatureType(int xcoord, int ycoord)
{
if (cboard[xcoord][ycoord]) //if there is a creature at location xcoord,ycoord
return (cboard[xcoord][ycoord]->displayletter);
else //if pointer at location xcoord,ycoord is null, return null char
return '\0';
}
char Grid::CreatureType(int startx, int starty, int dir)
{
Coords StartLocation(startx,starty,WIDTH,HEIGHT);
switch(dir)
{
case 1:
StartLocation.MoveUp();
if (cboard[StartLocation.x][StartLocation.y])
return (cboard[StartLocation.x][StartLocation.y]->displayletter);
case 2:
StartLocation.MoveUpRight();
if (cboard[StartLocation.x][StartLocation.y])
return (cboard[StartLocation.x][StartLocation.y]->displayletter);
case 3:
StartLocation.MoveRight();
if (cboard[StartLocation.x][StartLocation.y])
return (cboard[StartLocation.x][StartLocation.y]->displayletter);
case 4:
StartLocation.MoveDownRight();
if (cboard[StartLocation.x][StartLocation.y])
return (cboard[StartLocation.x][StartLocation.y]->displayletter);
case 5:
StartLocation.MoveDown();
if (cboard[StartLocation.x][StartLocation.y])
return (cboard[StartLocation.x][StartLocation.y]->displayletter);
case 6:
StartLocation.MoveDownLeft();
if (cboard[StartLocation.x][StartLocation.y])
return (cboard[StartLocation.x][StartLocation.y]->displayletter);
case 7:
StartLocation.MoveLeft();
if (cboard[StartLocation.x][StartLocation.y])
return (cboard[StartLocation.x][StartLocation.y]->displayletter);
case 8:
StartLocation.MoveUpLeft();
if (cboard[StartLocation.x][StartLocation.y])
return (cboard[StartLocation.x][StartLocation.y]->displayletter);
} //if function hasn't returned by now, square being looked at is pointer to null
return '\0'; //return null char
}
void Coords::MoveRight() {(x==MaxX)? (x=0):(x++);}
void Coords::MoveLeft() {(x==0)? (x=MaxX):(x--);}
void Coords::MoveUp() {(y==0)? (y=MaxY):(y--);}
void Coords::MoveDown() {(y==MaxY)? (y=0):(y++);}
void Coords::MoveUpRight() {MoveUp(); MoveRight();}
void Coords::MoveUpLeft() {MoveUp(); MoveLeft();}
void Coords::MoveDownRight() {MoveDown(); MoveRight();}
void Coords::MoveDownLeft() {MoveDown(); MoveLeft();}
void Coords::MoveDir(int dir)
{
switch(dir)
{
case 1:
MoveUp();
break;
case 2:
MoveUpRight();
break;
case 3:
MoveRight();
break;
case 4:
MoveDownRight();
break;
case 5:
MoveDown();
break;
case 6:
MoveDownLeft();
break;
case 7:
MoveLeft();
break;
case 8:
MoveUpLeft();
break;
case 0:
break;
}
}
void Creature::Move(Grid G)
{
int movedir=DecideSquare(G);
MoveTo(G,movedir);
}
int Creature::DecideSquare(Grid G)
{
Random X(1,8,8); //X.rptr now points to 8 unique random integers from [1,8]
for (int i=0; i<8; i++)
{
int dir=X.rptr[i];
if (G.isEmpty(Location.x,Location.y,dir))
return dir;
}
return 0;
}
void Creature::MoveTo(Grid G, int dir)
{
Coords OldLocation=Location;
Location.MoveDir(dir);
G.cboard[Location.x][Location.y]=this; //point new location to this creature
G.cboard[OldLocation.x][OldLocation.y]=NULL; //point old location to NULL
}
void Creature::Die()
{
if (!alive)
{
delete this;
this=NULL;
}
}
void Human::Breed(Grid G)
{
if (!breedcount)
{
Coords BreedLocation=Location;
Random X(1,8,8);
for (int i=0; i<8; i++)
{
BreedLocation.MoveDir(X.rptr[i]);
if (!G.cboard[BreedLocation.x][BreedLocation.y])
{
G.cboard[BreedLocation.x][BreedLocation.y])=new Human(BreedLocation.x,BreedLocation.y);
return;
}
}
}
}
int Human::DecideAttack(Grid G)
{
Coords AttackLocation=Location;
Random X(1,8,8);
int attackdir;
for (int i=0; i<8; i++)
{
attackdir=X.rptr[i];
switch(G.CreatureType(Location.x,Location.y,attackdir))
{
case 'H':
break;
case 'Z':
return attackdir;
case '\0':
break;
default:
break;
}
}
return 0; //no zombies!
}
int AttackRoll(int para1, int para2)
{
//outcome 1: Zombie wins, human dies
//outcome 2: Human wins, zombie dies
//outcome 3: Human wins, zombie dies, but human is bitten
Random X(1,100,1);
int roll= *(X.rptr);
if (roll < para1)
return 1;
else if (roll < para2)
return 2;
else
return 3;
}
void Human::AttackCreature(Grid G, int attackdirection)
{
Coords AttackLocation=Location;
AttackLocation.MoveDir(attackdirection);
int para1=33;
int para2=33;
if (vaccinated)
para2=101; //makes attackroll > para 2 impossible, never gets infected
if (armed)
para1-=16; //reduces chance of zombie winning fight
int roll=AttackRoll(para1,para2);
//outcome 1: Zombie wins, human dies
//outcome 2: Human wins, zombie dies
//outcome 3: Human wins, zombie dies, but human is bitten
switch(roll)
{
case 1:
alive=0; //human (this) dies
return;
case 2:
G.cboard[AttackLocation.x][AttackLocation.y]->alive=0;
return; //zombie dies
case 3:
G.cboard[AttackLocation.x][AttackLocation.y]->alive=0; //zombie dies
Random X(3,7,1); //human is bitten
bitecount=*(X.rptr);
return;
}
}
int Zombie::DecideAttack(Grid G)
{
Coords AttackLocation=Location;
Random X(1,8,8);
int attackdir;
for (int i=0; i<8; i++)
{
attackdir=X.rptr[i];
switch(G.CreatureType(Location.x,Location.y,attackdir))
{
case 'H':
return attackdir;
case 'Z':
break;
case '\0':
break;
default:
break;
}
}
return 0; //no zombies!
}
void Zombie::AttackCreature(Grid G, int attackdirection)
{
int reversedirection;
if (attackdirection < 9 && attackdirection>0)
{
(attackdirection<5)? (reversedirection=attackdirection+4):(reversedirection=attackdirection-4);
}
else
reversedirection=0; //this should never happen
//when a zombie attacks a human, the Human::AttackZombie() function is called
//in the "reverse" direction, utilizing that function that has already been written
Coords ZombieLocation=Location;
Coords HumanLocation=Location;
HumanLocation.MoveDir(attackdirection);
if (G.cboard[HumanLocation.x][HumanLocation.y]) //if there is a human there, which there should be
G.cboard[HumanLocation.x][HumanLocation.y]->AttackCreature(G,reversedirection);
}
void Zombie::Attack(Grid G)
{
int attackdirection=DecideAttack(G);
AttackCreature(G,attackdirection);
}
main.cpp
#include <cstdlib>
#include <iostream>
#include "definitions.h"
using namespace std;
int main(int argc, char *argv[])
{
Grid G(500,500);
system("PAUSE");
return EXIT_SUCCESS;
}
© Stack Overflow or respective owner