Hi, I am having a problem with an assignment for a C++ course. I have to make a Game of Life simulator (predator and prey). There are two organisms, lion and ant, and the lions eat the ants.

My problem is that for some reason I can't fathom, the lions are disappearing when they aren't supposed to.

I won't post the whole thing for readability sake, just the parts I think are relevant. Let me know if I should include other files.

World.h

#ifndef _WORLD_H
#define _WORLD_H

#include <iostream>

using namespace std;

class Organism;

const int GRID_WIDTH = 20;
const int GRID_HEIGHT = 20;

class World
{
protected:
	Organism *grid[GRID_WIDTH][GRID_HEIGHT];

public:
	World();
	virtual ~World();

	Organism *getOrganism( int x, int y );
	void setOrganism( Organism *organism, int x, int y );

	void move();

	friend ostream& operator<<( ostream &output, World &world );

};

#endif

World.cpp

#include <iostream>
#include "World.h"
#include "Organism.h"
#include "Ant.h"
#include "Lion.h"

using namespace std;

World::World()
{	
	for( int i=0; i<GRID_WIDTH; i++ )
	{
		for( int j=0; j<GRID_HEIGHT; j++ )
		{
			grid[i][j] = NULL;
		}
	}
}

World::~World() 
{
	 //free up allocated memory
	for( int i=0; i<GRID_WIDTH; i++ )
	{
		for( int j=0; j<GRID_HEIGHT; j++ )
		{
			if( grid[i][j] != NULL )
			{
				delete grid[i][j];
			}
		}
	}
}

Organism *World::getOrganism( int x, int y )
{
	return grid[x][y];
}

void World::setOrganism( Organism *organism, int x, int y )
{
	grid[x][y] = organism;
}

void World::move()
{
	// object method call priority:

	// 1 - Move all lions
	for( int i=0; i<GRID_WIDTH; i++ )
	{
		for( int j=0; j<GRID_HEIGHT; j++ )
		{
			// verify organism is a lion and that the lion
			// has not already moved once, before moving
			if( (dynamic_cast<lion *>(grid[i][j]) != NULL) &&
			    (grid[i][j]->isTurn()) )
			{
				grid[i][j]->move();
			}
		}
	}

	// 2 - Move all ants
	for( int i=0; i<GRID_WIDTH; i++ )
	{
		for( int j=0; j<GRID_HEIGHT; j++ )
		{
			// verify organism is an ant and that the ant
			// has not already moved once, before moving
			if( (dynamic_cast<ant *>(grid[i][j]) != NULL) &&
			    (grid[i][j]->isTurn()) )
			{
				grid[i][j]->move();
			}
		}
	}

	// 3 - Breed all eligible lions
	for( int i=0; i<GRID_WIDTH; i++ )
	{
		for( int j=0; j<GRID_HEIGHT; j++ )
		{
			// verify organism is a lion and that it's the lion's
			// turn before attempting to breed
			if( (dynamic_cast<lion *>(grid[i][j]) != NULL) &&
			    (grid[i][j]->isTurn()) )
			{
				grid[i][j]->breed();
			}
		}
	}

	// 4 - Breed all eligible ants
	for( int i=0; i<GRID_WIDTH; i++ )
	{
		for( int j=0; j<GRID_HEIGHT; j++ )
		{
			// verify organism is an ant and that it's the ant's
			// turn before attempting to breed
			if( (dynamic_cast<ant *>(grid[i][j]) != NULL) &&
			    (grid[i][j]->isTurn()) )
			{
				grid[i][j]->breed();
			}
		}
	}

	// 6 - Starve all eligible lions
	for( int i=0; i<GRID_WIDTH; i++ )
	{
		for( int j=0; j<GRID_HEIGHT; j++ )
		{
			// verify organism is a lion and that it's the lion's
			// turn before attempting to starve
			if( (dynamic_cast<lion *>(grid[i][j]) != NULL) &&
			    (grid[i][j]->isTurn()) )
			{
				(dynamic_cast<lion *>(grid[i][j])->starve());
			}
		}
	}

	// 7 - End all the organism's turns
	for( int i=0; i<GRID_WIDTH; i++ )
	{
		for( int j=0; j<GRID_HEIGHT; j++ )
		{
			// verify presence of organism
			if( grid[i][j] != NULL )
			{
				grid[i][j]->endTurn();
			}
		}
	}

	// 8 - All remaining organisms have "survived" this step. Increment each organism's counter by one.
	for( int i=0; i<GRID_WIDTH; i++ )
	{
		for( int j=0; j<GRID_HEIGHT; j++ )
		{
			// verify presence of organism
			if( grid[i][j] != NULL )
			{
				grid[i][j]->incCounter();
			}
		}
	}

}

ostream& operator<<( ostream &output, World &world )
{
	// display grid
	for( int i=0; i<GRID_WIDTH; i++ )
	{
		for( int j=0; j<GRID_HEIGHT; j++ )
		{
			output << world.grid[i][j];
		}
		output << endl;
	}

	return output;
}

Lion.h

#ifndef _Lion_H
#define _Lion_H

#include "Organism.h"

class lion : public Organism
{
protected:
	int newX, 
		newY;
	bool eaten;

public:
	lion();
	lion( World *world, int width, int height);
	virtual ~lion();

	void eat(int coordX, int coordY);
	void move();
	void breed();
	void starve();
	void spawn(int coordX, int coordY);
};

#endif

Lion.cpp

#include <cstdlib>
#include <ctime>
#include "World.h"
#include "Ant.h"
#include "Lion.h"

using namespace std;

lion::lion() 
{
}

lion::lion( World *world, int width, int height) 
	:Organism( world, width, height )
{
	srand( (unsigned int)time(NULL) );
	eaten = false;
}

lion::~lion() 
{
}

void lion::eat(int coordX, int coordY)
{
	if(dynamic_cast<ant *>(world->getOrganism(coordX, coordY)) != NULL) // if the space is occupied by an ant
	{
			// move to the ant's space and replace with the lion
			newX = coordX;
			newY = coordY;

			world->setOrganism( this, newX, newY );
			world->setOrganism( NULL, x, y );

			x = newX;
			y = newY;

	}
}

void lion::move()
{
	int direction;

	// eat the first ant that the lion comes across, and move into its space

	if(	// the space is occupied,
		(world->getOrganism(x, y-1) != NULL) && 
		// the space isn't over the edge of the grid,
		(x >= 0) && (y-1 >=0) && (x < GRID_WIDTH) && (y-1 < GRID_HEIGHT) &&
		// and the space is occupied by an ant,
		(dynamic_cast<ant *>(world->getOrganism(x, y-1)) != NULL) )
	{
		// replace the ant with the lion
		eat(x, y-1);
		eaten = true;
	}
	else if((world->getOrganism(x, y+1) != NULL) && (x >= 0) && (y+1 >=0) &&
		(x < GRID_WIDTH) && (y+1 < GRID_HEIGHT) && (dynamic_cast<ant *>(world->getOrganism(x, y+1)) != NULL)) // SOUTH
	{
		eat(x, y+1);
		eaten = true;
	}
	else if((world->getOrganism(x+1, y) != NULL) && (x+1 >= 0) && (y >=0) &&
		(x+1 < GRID_WIDTH) && (y < GRID_HEIGHT) && (dynamic_cast<ant *>(world->getOrganism(x+1, y)) != NULL)) // EAST
	{
		eat(x+1, y);
		eaten = true;
	}
	else if((world->getOrganism(x-1, y) != NULL) && (x-1 >= 0) && (y >=0) && 
		(x-1 < GRID_WIDTH) && (y < GRID_HEIGHT) && (dynamic_cast<ant *>(world->getOrganism(x-1, y)) != NULL)) // WEST
	{
		eat(x-1, y);
		eaten = true;
	}
	else if((world->getOrganism(x+1, y-1) != NULL) && (x+1 >= 0) && (y-1 >=0) &&
		(x+1 < GRID_WIDTH) && (y-1 < GRID_HEIGHT) && (dynamic_cast<ant *>(world->getOrganism(x+1, y-1)) != NULL)) // NORTHEAST
	{
		eat(x+1, y-1);
		eaten = true;
	}
	else if((world->getOrganism(x-1, y-1) != NULL) && (x-1 >= 0) && (y-1 >=0) &&
		(x-1 < GRID_WIDTH) && (y-1 < GRID_HEIGHT) && (dynamic_cast<ant *>(world->getOrganism(x-1, y-1)) != NULL)) // NORTHWEST
	{
		eat(x-1, y-1);
		eaten = true;
	}
	else if((world->getOrganism(x+1, y+1) != NULL) && (x+1 >= 0) && (y+1 >=0) &&
		(x+1 < GRID_WIDTH) && (y+1 < GRID_HEIGHT) && (dynamic_cast<ant *>(world->getOrganism(x+1, y+1)) != NULL)) // SOUTHEAST
	{
		eat(x+1, y+1);
		eaten = true;
	}
	else if((world->getOrganism(x-1, y+1) != NULL) && (x-1 >= 0) && (y+1 >=0) &&
		(x-1 < GRID_WIDTH) && (y+1 < GRID_HEIGHT) && (dynamic_cast<ant *>(world->getOrganism(x-1, y+1)) != NULL)) // SOUTHWEST
	{
		eat(x-1, y+1);
		eaten = true;
	}
	else // move normally
	{
		direction = rand() % NUM_DIRECTIONS;
		switch( direction )
		{
			case NORTH:
				newX = x;
				newY = y - 1;
				break;
			case SOUTH:
				newX = x;
				newY = y + 1;
				break;
			case EAST:
				newX = x + 1;
				newY = y;
				break;
			case WEST:
				newX = x - 1;
				newY = y;
				break;
			default:
				break;
		}

		// check limits of the grid
		if( newX < 0 ) newX = 0;
		if( newY < 0 ) newY = 0;
		if( newX >= GRID_WIDTH ) newX = GRID_WIDTH - 1;
		if( newY >= GRID_HEIGHT ) newY = GRID_HEIGHT - 1;

		// move lion to new location
		if( world->getOrganism( newX, newY ) == NULL )
		{
			world->setOrganism( this, newX, newY );
			world->setOrganism( NULL, x, y );

			x = newX;
			y = newY;

		}
	}

}

void lion::breed()
{
	// if 8 turns are up, attempt to breed
	if(counter % 8 == 0 && counter != 0)
	{
		// check availability of surrounding spaces for breeding
		if((world->getOrganism(x, y-1) == NULL) && (x >= 0) && (y-1 >=0) &&
			(x < GRID_WIDTH) && (y-1 < GRID_HEIGHT) ) // NORTH
		{
			// if the space is available, place new lion
			spawn(x, y-1);
		}
		else if((world->getOrganism(x, y+1) == NULL) && (x >= 0) && (y+1 >=0) &&
			(x < GRID_WIDTH) && (y+1 < GRID_HEIGHT) ) // SOUTH
		{
			spawn(x, y+1);
		}
		else if((world->getOrganism(x+1, y) != NULL) && (x+1 >= 0) && (y >=0) &&
			(x+1 < GRID_WIDTH) && (y < GRID_HEIGHT)) // EAST
		{
			spawn(x+1, y);
		}
		else if((world->getOrganism(x-1, y) != NULL) && (x-1 >= 0) && (y >=0) && 
			(x-1 < GRID_WIDTH) && (y < GRID_HEIGHT) ) // WEST
		{
			spawn(x-1, y);
		}
		// if no available spaces, do not breed
	}
}

void lion::starve()
{
	// if three turns have gone by
	if(counter % 3 == 0 && counter != 0)
	{
		// the lion will starve if it hasn't eaten
		if(eaten == false)
		{
			world->setOrganism(NULL, x, y);
		}
		else
		{
			eaten = false;
		}
	}
	// last action for lion of its turn
	done = true;
}


void lion::spawn(int coordX, int coordY)
{
	// method to be called from breed() to instantiate new lion
	lion *offspring = new lion( world, GRID_WIDTH, GRID_HEIGHT );
	offspring->setPosition( coordX, coordY );

	world->setOrganism(offspring, coordX, coordY );
}

Ant.h

#ifndef _Ant_H
#define _Ant_H

#include "Organism.h"

class ant : public Organism
{
public:
	ant(); 
	ant( World *world, int width, int height );
	virtual ~ant();

	void move();
	void breed();
	void spawn(int coordX, int coordY);
};

#endif

Ant.cpp

#include <cstdlib>
#include <ctime>
#include "World.h"
#include "Ant.h"

using namespace std;

ant::ant()
{
}

ant::ant( World *world, int x, int y )
	: Organism( world, x, y )
{
	srand( (unsigned int)time(NULL) );
}

ant::~ant()
{
}

void ant::move()
{
	int direction;

	direction = rand() % 4;

	int newX, newY;
	switch( direction )
	{
		case NORTH:
			newX = x;
			newY = y - 1;
			break;
		case SOUTH:
			newX = x;
			newY = y + 1;
			break;
		case EAST:
			newX = x + 1;
			newY = y;
			break;
		case WEST:
			newX = x - 1;
			newY = y;
			break;
		default:
			break;
	}

	// check limits of the grid
	if( newX < 0 ) newX = 0;
	if( newY < 0 ) newY = 0;
	if( newX >= GRID_WIDTH ) newX = GRID_WIDTH - 1;
	if( newY >= GRID_HEIGHT ) newY = GRID_HEIGHT - 1;

	// move ant to new location
	if( world->getOrganism( newX, newY ) == NULL )
	{
		world->setOrganism( this, newX, newY );
		world->setOrganism( NULL, x, y );

		x = newX;
		y = newY;
	}
}

void ant::breed()
{
	// if 3 turns are up, attempt to breed
	if(counter % 3 && counter != 0)
	{
		// check availability of surrounding spaces for breeding
		if((world->getOrganism(x, y-1) == NULL) && (x >= 0) && (y-1 >=0) &&
			(x < GRID_WIDTH) && (y-1 < GRID_HEIGHT) ) // NORTH
		{
			// if the space is available, place new ant
			spawn(x, y-1);
		}
		else if((world->getOrganism(x, y+1) == NULL) && (x >= 0) && (y+1 >=0) &&
			(x < GRID_WIDTH) && (y+1 < GRID_HEIGHT) ) // SOUTH
		{
			spawn(x, y+1);
		}
		else if((world->getOrganism(x+1, y) != NULL) && (x+1 >= 0) && (y >=0) &&
			(x+1 < GRID_WIDTH) && (y < GRID_HEIGHT)) // EAST
		{
			spawn(x+1, y);
		}
		else if((world->getOrganism(x-1, y) != NULL) && (x-1 >= 0) && (y >=0) && 
			(x-1 < GRID_WIDTH) && (y < GRID_HEIGHT) ) // WEST
		{
			spawn(x-1, y);
		}
		// if no available spaces, do not breed

		// last action for ant of its turn
		done = true;
	}
}

void ant::spawn(int coordX, int coordY)
{
	ant *offspring = new ant( world, GRID_WIDTH, GRID_HEIGHT );
	offspring->setPosition( coordX, coordY );

	world->setOrganism(offspring, coordX, coordY );
}

Organism.h (base class)

#ifndef _Organism_H
#define _Organism_H

#include <iostream>

using namespace std;

class World;

class Organism
{
protected:
	int x;
	int y;
	int width;
	int height;
	int counter;
	bool done;
	World *world;

	enum { WEST, NORTH, EAST, SOUTH, NUM_DIRECTIONS };

public:
	Organism();
	Organism( World *world, int width, int height );
	virtual ~Organism();

	virtual void move();
	virtual void breed();
	virtual void spawn();

	void incCounter();
	void setPosition( int x, int y );
	void endTurn();
	bool isTurn();

	friend ostream& operator<<( ostream &output, Organism *organism );
};

#endif

Organism.cpp

#include <iostream>
#include "World.h"
#include "Organism.h"
#include "Ant.h"
#include "Lion.h"

using namespace std;

Organism::Organism() : done(false)
{
}

Organism::Organism( World *world, int width, int height ) : done(false)
{
	this->world = world;
	this->width = width;
	this->height = height;
	counter = 0;
}

Organism::~Organism()
{
}

void Organism::incCounter()
{
	counter++;
}

void Organism::setPosition( int x, int y ) 
{ 
	this->x = x; 
	this->y = y; 
}

void Organism::endTurn()
{
	done = false;
}

bool Organism::isTurn()
{
	return !done;
}

ostream& operator<<( ostream &output, Organism *organism )
{
	if( dynamic_cast<ant *>(organism) != NULL )
	{
		output << "O ";
	}
	else if( dynamic_cast<lion *>(organism) != NULL )
	{
		output << "X ";
	}
	else
	{
		output << "- ";
	}

	return output;
}

void Organism::spawn(){}

void Organism::move(){}

void Organism::breed(){}

Hopefully someone can see what I've missed.

thanks

Recommended Answers

All 9 Replies

Wow! That's a lot of code!
The best thing to do, before we read through your code, is for you to use "cout" statements to work out where the lions are disappearing. Look at the last console line when a lion disappears at the wrong time, and then you can work out where the line is, or where abouts it is.

Wow! That's a lot of code!

I agree. Over 1000 lines of code with no indication of where the problem could be. That's why I for one ignored the question. I already have a full time job and don't need another one being a code maintenance technician -- unpaid no less.

Yeah, sorry about that. I'm very new to programming (only been coding in C++ since September) and my debugging skills leave a lot to be desired. Not to mention I was a little desperate and frustrated at the time I posted this.

I spent another little while spinning my wheels until I finally noticed I just had some typos causing incorrect assignment statements in the breed() method for both ant and lion, causing the organisms to move into occupied spaces and thus "eating" them... meant to test == null, accidently had != null.

Thanks for the tips; I'll make sure next time my code is pared down considerably.

Nah it's cool :)
So is your problem fixed or do you have another problem or the same problem or what?

yep, it's fixed. Program is nicely balanced so the simulation works well.

Please mark the thread as solved then :)

Done. Thanks for the help.

would you please post the rset of the program AS SOON AS POSSIBLE including everything if u can ?

commented: Piss off and do your own homework, stupid "me too" thread hijacker -4

great code for learning, please post all the files, archived. thanks

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.