Game Theory Simulation: Reciprocal altruism: Issue

The Partridge Family were neither partridges nor a family. Discuss.
Post Reply
Zedtho_
Posts: 9
Joined: May 28th, 2020, 9:27 am

Game Theory Simulation: Reciprocal altruism: Issue

Post by Zedtho_ » May 28th, 2020, 9:59 am

Hey everyone! It's been a while, just made a new account because I got locked out of my old one :)

I'm working on a Game Theory simulation to evolutionarily explain reciprocal altruism. (https://github.com/Zedtho/Simulation-Re ... ree/master) (the branch on which this issue is based is the test-branch "Score")

If you want a general idea of what this program is supposed to do:
Spoiler:
This program is supposed to be a simulation to test certain game-theoretic approaches to the evolution of reciprocal altruism.

In its current stage it contains three strategies that an "agent" (in nature rather an organism) can have: It can be a Cooperator, meaning it will always cooperate. It can be a Tit-For-Tatter (shortened TFT in the program), meaning it will cooperate and subsequently copy its opponent's action against that opponent. (in other words, if Agents cooperate, TFT will cooperate back, if not, it will deflect as a form of vengeance.) It can also be a Deflector, meaning it will always deflect.

Every round, agents will randomly meet each other and decide which strategy they will use. Depending on who deflects/cooperates, the different agents receive different punishments/rewards. A negative score for an agent means it dies; A score larger than some reproductionscore means it reproduces.

It is expected that the TFT strategy is most effective, and that as such it will be the largest ratio at the end of the game. As such, reciprocal altruism can be shown to be an effective strategy.

This theory is one I wanted to check with a program for a biological paper I am writing for school.
The problem I have currently is the following. The Tit-for-tat strategy is characterized by the fact that the agent remembers what the last action towards them was of other agents. To do this, I gave the Tit-for-tat strategists a pointer vector which allows them to store a pointer to everyone who has deflected against them (in the program, this is called the Naughtylist and can be found in Agent.h). The code for how an agent is added to a Naughtylist looks like this:

Code: Select all

void AddNaughty(Agent* agent)
	{
		bool AlreadyNaughty = false;
		for (unsigned int i = 0; i < Naughtylist.size(); ++i)
		{
			if (Naughtylist[i] = agent)
			{
				AlreadyNaughty = true;
			}
		}
		if (AlreadyNaughty = false)
		{
			Naughtylist.push_back(agent);
		}
	}
However, when one debugs (and plays the simulation with 0 cooperators, 1 TFT-er, and 1 Deflector) and one puts a break point after AddNaughty is called, one sees that though this function is called, nothing is added to the Naughtylist of the TFT-er.
2020-05-28 11_55_26-Window.png
(35.41 KiB) Not downloaded yet
2020-05-28 11_55_26-Window.png
(35.41 KiB) Not downloaded yet
(as one can see, the Naughtylist is empty).

Thus, the Tit-For-Tat strategist never remembers who has deflected against them and acts passive, which defeats the point.
I am new to pointers and think this is why it is acting so weird. Would any of you perhaps know what is going on?

Thanks a bunch in advance!
Attachments
2020-05-27 21_22_03-Simulation Reciprocal Altruism (Debugging) - Microsoft Visual Studio.png
(7.71 KiB) Not downloaded yet

Slidy
Posts: 80
Joined: September 9th, 2017, 1:19 pm

Re: Game Theory Simulation: Reciprocal altruism: Issue

Post by Slidy » May 28th, 2020, 1:56 pm

I cloned your project and tested this out and the Deflector seems to get added to the naughty list as expected.

Something to note is that sine TFT is always added after Deflector, TFT will be the second candidate. In your screenshot debugging has stopped execution after the first candidate has been tested for being a TFT, but before the second has been. If you keep stepping a little more it'll go into that second check and add the Deflector to the naughty list.

I also noticed that you're missing a break on those 2 switches, so when you try to remove an agent from the naughty list, it'll be added right back due to case below.

Finally, I noticed this pattern a lot in your code:

Code: Select all

(*AliveAgents[FirstCandidateNumber]).Score += CooperateValue;
This pattern of dereferencing a pointer followed by accessing a member is so common in C/C++ that there's a shorthand for it:

Code: Select all

AliveAgents[FirstCandidateNumber]->Score += CooperateValue;
Using that will make your code a lot cleaner.

Zedtho_
Posts: 9
Joined: May 28th, 2020, 9:27 am

Re: Game Theory Simulation: Reciprocal altruism: Issue

Post by Zedtho_ » May 28th, 2020, 2:11 pm

Thanks a lot for checking out my code! Sorry, I forgot to set the default branch to "Score" as I mentioned in my text, the branch "Score" has those improvements! I've reset the default now.
But yes you're completely right, the -> makes everything much more legible! Thanks for your first point, perhaps the problem does not lie in AddNaughty then... I thought because FirstCandidate and SecondCandidate were determined randomly that would make TFT and Deflector switch positions now and then, but can well be I made a mistake! Interesting though that the older Master branch does work but my supposedly fixed branch Score does not haha

Slidy
Posts: 80
Joined: September 9th, 2017, 1:19 pm

Re: Game Theory Simulation: Reciprocal altruism: Issue

Post by Slidy » May 28th, 2020, 3:27 pm

Sorry, I missed the part about the Score branch. I had a look at your code on that branch and I found the issue:

Code: Select all

bool AlreadyNaughty = false;
for (unsigned int i = 0; i < Naughtylist.size(); ++i)
{
	if (Naughtylist[i] = agent)
	{
		AlreadyNaughty = true;
	}
}
if (AlreadyNaughty = false)
{
	Naughtylist.push_back(agent);
}
Here you are using = instead of ==, big difference.
= is the assignment operator -- it takes the value of the RHS (right hand side) and places it into the thing on the LHS (left hand side) then returns the new value of the LHS
== is the equality operator -- it compares the values on the LHS and the RHS and returns true if they're equal

So when you do:

Code: Select all

if (AlreadyNaughty = false)
What your code is really doing is:

Code: Select all

AlreadyNaughty = false;
if (AlreadyNaughty)
Which obviously will never evaluate to true.

Zedtho_
Posts: 9
Joined: May 28th, 2020, 9:27 am

Re: Game Theory Simulation: Reciprocal altruism: Issue

Post by Zedtho_ » May 28th, 2020, 5:00 pm

Ohhh I completely forgot about that, thank you very much! That's extremely helpful!

Edit: I went over the program again, and I think it is working! I will still have to check.
In the case where there is no mistake, an interesting factoid I have learnt is that Deflectors in this arrangement generally did best. (considering equal amounts of Cooperators, TFTers and Deflectors), though TFT was generally quite close, with Cooperators considerably lagging behind.
Doing some of my own math, this does not make much sense (see image), so I will have to check.
However, this can still be true. Tit for tat should do well not because it is best against every strategy (such a strategy does not exist), but rather because it is best on average against strategies. I'll do a bit of reading and report back!

Further edit: I have also learnt that it is spelled Defector and not Deflector, please don't pick up that mistake from this post!

Post Reply