I do not know why my dynamic_cast isn't working...

Hello,

I'm trying to make an agents-based simulation (Preys vs Hunters) and so far got it running with random "movement".
For the simulation, I have a grid of pointers to a class `Agent` and have classes `Prey` and `Hunter` that are also `Agent`.

Now I'm trying to implement some directed movement through some flags (boolean variables in Hunter class) but my issue is that when I add the following code, my simulation 'breaks'. (It compiles, but runs only for three rounds)
And it doesn't work as intended because it either doesn't change the boolean in a probabilistic way or is counting them erroneously, but I do not know which one is the mistaken.
I'm missing something obvious about the use of dynamic_cast?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
	void Environment::setNumberOfAltruistHunters(long _prop) {
		for (int j = 0; j < WORLDSIZEY; j++) {
			for (int i = 0; i < WORLDSIZEX; i++) {
				if (grid[i][j] && grid[i][j]->getType() == HUNTER) {
					long roll = (long)rand() / (long)RAND_MAX;
					if (roll < _prop) {
						Hunter *hptr = dynamic_cast<Hunter*>(grid[i][j]);
						if (hptr) {
							hptr->altruistic = true;
						}
					}
				}
			}
		}
	}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
	int Environment::numberAltruisticHunters() {
		int numAltruisticHunters = 0;
		for (int j = 0; j < WORLDSIZEY; j++) {
			for (int i = 0; i < WORLDSIZEX; i++) {
				if (grid[i][j] && grid[i][j]->getType() == HUNTER) {
					Hunter *hptr = dynamic_cast<Hunter*>(grid[i][j]);
					if (hptr) {
						numAltruisticHunters++;
					}
				}
			}
		}
		return numAltruisticHunters;
	}	


This is a reduced version of the structure of classes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
	class Environment {
	public:
		Environment();
		~Environment();
	private:
		Agent* grid[WORLDSIZEX][WORLDSIZEY];
	};

	enum AgentType { PREY, HUNTER };

	class Agent {
	public:
		Agent(Environment* aWorld, int xcoord, int ycoord);
		virtual ~Agent() { }
		virtual void move() = 0;
		virtual void breed() = 0;
		virtual AgentType getType() const = 0;

	protected:
		void movesTo(int xNew, int yNew);
		int x;
		int y;
		Environment* world;

	private:
	};

	class Prey : public Agent {
	public:
		Prey(Environment* aWorld, int xcoord, int ycoord);
		void move();
		void breed();
		AgentType getType() const;

	private:
		int huntingDifficulty;
	};
		
	class Hunter : public Agent {
	public:
		Hunter(Environment* aWorld, int xcoord, int ycoord);
		void move();
		void breed();
		AgentType getType() const;
		vector<Agent *> preyList;
		bool altruistic;

	private:
		int huntingSkill;
	};


And this is my `main` program:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
	#include <iostream>
	#include <ctime>    // for time
	#include <vector>

	#include "Environment.h"
	using namespace std;

	void simpleSimulation();
	void multipleSimulation();

	int main() {
		int choice;

		do {
			cout << "Simulation of Cooperative Behaviour: Hunters vs Preys" << endl;
			cout << "SIMULATION OPTIONS" << endl;
			cout << "==================" << endl;
			cout << "0 - Exit the program" << endl;
			cout << "1 - Simulate once" << endl;
			cout << "2 - Simulate automatically for different ammounts of cooperative hunters and create results file" << endl;
			cout << endl << "Enter choice: ";
			cin >> choice;

			switch (choice) {
			case 0:
				cout << "Auf wiedersehen." << endl;
				break;
			case 1:
				simpleSimulation();
				break;
			case 2:
				multipleSimulation();
				break;
			}
		} while (choice != 0);
		return 0;
	}

	void simpleSimulation() {
		//For world size check "Parameters.h"
		// Number of initial preys
		int initialPreys = 60;
		cout << "The number of initial Preys is:" << initialPreys << endl;

		// Number of initial hunters
		int initialHunters = 15;
		cout << "The number of initial Hunters is:" << initialHunters << endl;

		long probAltruisticChoice = 0;
		cout << "Indicate probability of presence of altruistic hunters: (0-100)";
		cin >> probAltruisticChoice;
		probAltruisticChoice = (float)probAltruisticChoice / 100;

		// Time steps between breeding of preys
		int breedPreys = BREED_PREYS;

		// Time steps between breeding of hunters
		int breedHunters = BREED_HUNTER;

		// Time steps until hunters die if they have not eaten
		int starveHunters = STARVE_HUNTERS;

		int id = 0;

		//Creating the pattern world
		int seed = time(NULL);
		Environment myWorld(seed, id, initialPreys, initialHunters, breedPreys, breedHunters, starveHunters);
		myWorld.setNumberOfAltruistHunters(probAltruisticChoice);

		cout << "Press q to quit world simulation." << endl;
		cout << "This is the timestep 0" << endl;
		myWorld.display();
		char ch;
		while (cin.get(ch) && ch != 'q') {   // q for quit
			myWorld.simulateOneStep();
			myWorld.display();
		}
	}

	void multipleSimulation() {
		//For world size check "Parameters.h"
		// Number of initial preys
		int initialPreys = 60;
		cout << "The number of initial Preys is:" << initialPreys << endl;

		// Number of initial hunters
		int initialHunters = 15;
		cout << "The number of initial Hunters is:" << initialHunters << endl;

		long probAltruisticChoice = 0;

		// Time steps between breeding of preys
		int breedPreys = BREED_PREYS;

		// Time steps between breeding of hunters
		int breedHunters = BREED_HUNTER;

		// Time steps until hunters die if they have not eaten
		int starveHunters = STARVE_HUNTERS;

		int id = 0;

		//Creating the pattern world
		int seed = time(NULL);
		Environment myWorld(seed, id, initialPreys, initialHunters, breedPreys, breedHunters, starveHunters);
		cout << "This is the timestep " << myWorld.getTimestep() << " of the environment for all the simulations" << endl;
		myWorld.display();

		vector<Environment> simulationResults;
		simulationResults.reserve(310);
		simulationResults.push_back(myWorld);

		int	timestepsPerSimulation = 600;
		int ammountSimulations = 300;

		do {
			id++;
			myWorld = simulationResults[0];
			myWorld.setId(id);

			long probAltruisticChoice = (long)(id % 10) / (long)10;
			myWorld.setNumberOfAltruistHunters(probAltruisticChoice);

			cout << "\n\n\n" << "This is the timestep " << myWorld.getTimestep() << " of the environment for the simulation number " << id << endl;
			myWorld.display();

			for (int j = 0; j < timestepsPerSimulation; j++) {
				myWorld.simulateOneStep();
			}
			simulationResults.push_back(myWorld);

			cout << "\n\n\n" << "This is the timestep " << myWorld.getTimestep() << " of the environment for the simulation number " << id << endl;
			myWorld.display();

			myWorld.setNumberOfAltruistHunters(probAltruisticChoice);
		} while (id < ammountSimulations);


		char ch;
		while (cin.get(ch) && ch != 'q') {   // q for quit
			cout << "Press q to quit world simulation." << endl;
		}
	}


Should I post my whole code?

Thanks for any comment. Best!
Last edited on
Where do you initialize grid?
In the constructor of the Environment

It starts with all in NULL and then populate them with Hunter and Prey

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Environment::Environment(unsigned int seed, int _id, int _initialPopulationPreys, int _initialPopulationHunters, int _timeToBreed_Preys, int _timeToBreed_Hunters, int _timeToStarve_Hunters) {
	// seed the random generator
	srand(seed);

	// assign the id
	id = _id;
	huntersExtinction = false;
	preysExtinction = false;

	initialPopulationPreys = _initialPopulationPreys;
	initialPopulationHunters = _initialPopulationHunters;

	timeToBreed_Preys = _timeToBreed_Preys;
	timeToBreed_Hunters = _timeToBreed_Hunters;
	timeToStarve_Hunters = _timeToStarve_Hunters;

	for (int i = 0; i < WORLDSIZEX; i++) {
		for (int j = 0; j < WORLDSIZEY; j++) {
			grid[i][j] = NULL;
		}
	}
	timeStep = 0;
	// creates the preys
	createOrganisms(PREY, initialPopulationPreys);
	// creates the hunters
	createOrganisms(HUNTER, initialPopulationHunters);
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void Environment::createOrganisms(AgentType orgType, int count) {
	int orgCount = 0;
	while (orgCount < count) {

		Position p = randomPosition();
		Position q = randomPositionHunter();

		if (orgType == PREY) {
			if (grid[p.x][p.y] == NULL) {			// Only put Organism in empty spot
				orgCount++;
				new Prey(this, p.x, p.y);		// Create a Prey and put it into the world
			}
		}
		else if (orgType == HUNTER) {
			if (grid[q.x][q.y] == NULL) {			// Only put Organism in empty spot
				orgCount++;
				new Hunter(this, q.x, q.y);		// Create a Hunter and put it into the world
			}
		}
	}
}

You've forgot to assign the new objects to the grid.
 
grid[p.x][p.y] = new Prey(this, p.x, p.y);
@Peter87 Thanks for the comment. I think I've dealt with that with the constructor, but maybe it wasn't the most elegant way. I have a setAt function that I also use when I move the agents in the grid.

I'd appreciate a comment about my use of dynamic_cast. At the moment I'm thinking of changing it to a property of the Agent class, instead of being only a property of the Hunter class.

1
2
3
4
Prey::Prey(Environment* aWorld, int xcoord, int ycoord) : Agent(aWorld, xcoord, ycoord) {
	huntingDifficulty= int(rand() % 3);

}


1
2
3
4
5
6
Hunter::Hunter(Environment* aWorld, int xcoord, int ycoord) : Agent(aWorld, xcoord, ycoord) {
	death_tik = 0;
	huntingSkill = int(rand() % 3);
	altruistic = false;
	preyList.reserve(10);
}


1
2
3
4
5
6
7
8
Agent::Agent(Environment* aWorld, int xcoord, int ycoord) {
	world = aWorld;
	x = xcoord;
	y = ycoord;
	breedTicks = 0;
	moved = false;
	world->setAt(x, y, this);
}


1
2
3
4
5
void Environment::setAt(int x, int y, Agent* org) {
	if ((x >= 0) && (x < WORLDSIZEX) && (y >= 0) && (y < WORLDSIZEY)) {
		grid[x][y] = org;
	}
}

There is no problem with dynamic_cast since you check the result. The problem needs to be somewhere else. Moste likely an out of bounds access.

It is actually confusing that you set the object within the constructor. It is probably better to leave the environment out of the 'Agent' (which is a rather strange name).
@EloyRD Yeah, I think a fully working example would help. If it fails in the simple case, then just have a main that only calls the simple case. Fully working but complete example would help others put more eyes on your issue.

if your randomPosition() function (not shown here) produces something out of bounds, then it'll go through Agent::Agent() but not succeed in world->setAt() . The organism count is incremented anyway but the grid at those coordinates remains NULL. It's also not very efficient to keep generating random numbers, "hoping" for an open position instead of keeping a table of available positions and generating randoms from those. What you really want is to choose k of [0..WORLDSIZEX-1], and choose k of [0..WORLDSIZEY-1].

Also this part
1
2
long roll = (long)rand() / (long)RAND_MAX;
					if (roll < _prop) {


looks like it will be true 100% of the time. You attempt to make a fraction and compare to the float input parameter, but that first line is basically integer division and would set roll to 0 all the time. Did you mean
double roll = rand() / (double)RAND_MAX; ?
@coder777 @icy1 @Peter87
Thanks for your comments. It helped me to identify a lot of my mistakes.
I also took an online tutorial about debugging my program in Visual Studio and was able to solve many of my syntax issues. (random generation)

I opted for using regular casting instead of dynamic casting, as I had already checked about the sub-type of `Agent` present in the grid.

If I have more lines of codes of the available ammount of characters, should I post a link to another tool? Or to an online repo?

Thanks!
I opted for using regular casting instead of dynamic casting
Not a good idea. dynamic_cast is safe whereas the other is not.

If I have more lines of codes of the available ammount of characters, should I post a link to another tool? Or to an online repo?
What are you mean?
 
What are you mean?


I'm referring to the forum environment. If I try to post my working program it exceeds the limit of 8192 characters.

In that case: should I post a link to my online repo or to another code sharing site?
The best thing to do is to create a minimal, compilable codeset that demonstrates whatever the problem is that you want help with. Hopefully, that mimimal code will be small enough to post here.

Failing that, feel free to post a link to an online repo, although be prepared for the fact that some users here can't, or won't, click through to other sites, especially one's that they don't know to be trustworthy.

Topic archived. No new replies allowed.