Dilemma
Dilemma
Not logged in

A simulation of an evolving collection of players of the iterated prisoner's dilemma.

When people co-operate, things usually turn out well. If no-one co-operates, then everyone usually suffers. So the game plan is obvious, right?

Wrong! If everyone else is co-operating, then some clever fellow will come along and take advantage. So now the game plan is obvious, refuse to co-operate and take advantage of everyone who is naively co-operating.

Stupid! This is so obvious that no-one will co-operate, will they? And, yes, everyone will suffer.

This is clever?

This is a famous dilemma, with all sorts of significant analogies in the real world from Mutually Assured Destruction to the Tragedy of the Commons to the evolution of ethics in a godless universe.

There is a very simple solution, though it only becomes perfectly obvious by using mathematical simulation.

The Nice Guy

Let's build a player who always co-operates - returns true whenever asked for a response.

class cPlayerNiceGuy : cPlayerBase
{
public:
	cPlayerNiceGuy() : cPlayerBase( L"NiceGuy" ) {}
	bool Response( int , const cHistory& )
	{
		return true;
	}
		cPlayerBase * Reproduce() { return new cPlayerNiceGuy; }

};

Now build an ecology composed only of nice guys.

	cEcology ecology;
	ecology.Add( (cPlayerBase *)new cPlayerNiceGuy() );
	ecology.Add( (cPlayerBase *)new cPlayerNiceGuy() );
	ecology.Add( (cPlayerBase *)new cPlayerNiceGuy() );
	ecology.Add( (cPlayerBase *)new cPlayerNiceGuy() );
	ecology.Add( (cPlayerBase *)new cPlayerNiceGuy() );
	ecology.Add( (cPlayerBase *)new cPlayerNiceGuy() );
	ecology.Add( (cPlayerBase *)new cPlayerNiceGuy() );

The result of this simulation is:

NiceGuy scored 1260
NiceGuy scored 1260
NiceGuy scored 1260
NiceGuy scored 1260
NiceGuy scored 1260
NiceGuy scored 1260
NiceGuy scored 1260

As you would expect, everyone does equally well.

The Black Hat

Now lets introduce someone who tries to take advantage of all these nice guys, someone who always return false when asked for a response

class cPlayerBlackHat : cPlayerBase
{
public:
	cPlayerBlackHat() : cPlayerBase( L"BlackHat" ) {}
	bool Response( int , const cHistory& )
	{
		return false;
	}
	cPlayerBase * Reproduce() { return new cPlayerBlackHat; }

};

	cEcology ecology;
	ecology.Add((cPlayerBase *)new cPlayerNiceGuy() );
	ecology.Add((cPlayerBase *)new cPlayerNiceGuy() );
	ecology.Add((cPlayerBase *)new cPlayerNiceGuy() );
	ecology.Add((cPlayerBase *)new cPlayerNiceGuy() );
	ecology.Add((cPlayerBase *)new cPlayerNiceGuy() );
	ecology.Add((cPlayerBase *)new cPlayerNiceGuy() );
	ecology.Add((cPlayerBase *)new cPlayerBlackHat() );

BlackHat scored 2100
NiceGuy scored 1080
NiceGuy scored 1080
NiceGuy scored 1080
NiceGuy scored 1080
NiceGuy scored 1080
NiceGuy scored 1080

Well now, the Black Hat has gained enormously at the expense of all the nice guys!

Darwin

So now we introduce a little 'Darwinian Evolution' - we kill off the lowest scoring player and allow the highest scorer to reproduce.

After a few generations, we have these results:

BlackHat scored 1140
BlackHat scored 1140
BlackHat scored 1140
BlackHat scored 1140
BlackHat scored 1140
NiceGuy scored 360
NiceGuy scored 360

The non co-operative players have multiplied like rabbits and the few remaining co-operators are having a perfectly miserable time of it.

But notice that there are so few co-operators left to take advantage of that the Black Hats are now scoring LESS than the co-operators did when they were the only players.

Tit For Tat

Someone who initially co-operates, and then returns whatever the other did last time, tit for tat

class cPlayerTitForTat : cPlayerBase
{
public:
	cPlayerTitForTat() : cPlayerBase( L"TitForTat" ) {}
	bool Response( int MyRole, const cHistory& history )
	{
		if( ! history.size() )
			return true;
		return history.GetPartnerLastRespone( MyRole );
	}
	cPlayerBase * Reproduce() { return new cPlayerTitForTat; }

};
<verbatim>

Start with an even mixture of all the different players

<verbatim>
	ecology.Add( (cPlayerBase *)new cPlayerRandom() );
	ecology.Add((cPlayerBase *)new cPlayerBlackHat() );  
	ecology.Add((cPlayerBase *)new cPlayerTitForTat() );
	ecology.Add((cPlayerBase *)new cPlayerNiceGuy() );
	ecology.Add( (cPlayerBase *)new cPlayerRandom() );
	ecology.Add((cPlayerBase *)new cPlayerBlackHat() );  
	ecology.Add((cPlayerBase *)new cPlayerTitForTat() );
	ecology.Add((cPlayerBase *)new cPlayerNiceGuy() );

The results after a few generations

TitForTat scored 650
TitForTat scored 650
TitForTat scored 650
BlackHat scored 444
BlackHat scored 444
BlackHat scored 444
BlackHat scored 444

The naive co-operators go quickly extinct, but the tit-for-tats are easily dominating the black hats. A few more generations and the Black hats will be exinct and the tit-for-tats will settle into a pure co-operative strategy, scoring as much as the naive co-operators ever did.

It is fun to try different combinations of starting populations and try to come up with a strategy that does better than tit-for-tat in a mixed population.

To do this, you will need a basic understanding of C++ programming and the code you can download from this repository.

Download

The Fossil Repository

The fossil repository is a single file containing the complete history of the code source along with the documentation and tickets.

Fossil software configuration management provides self-hosting source control, documentation and ticketing all in one executable file obtained from here

Clone the dilemma fossil repository with the command

 fossil clone http://open:all@66.199.140.183/cgi-bin/dilemma.cgi dilemma.fossil

Extract the source code for the latest version

 fossil open dilemma.fossil

Zip File of v1.0 source code

A zip file containing just the source code for v1.0.0 can dowloaded from here

Other Code

Other open source code by James Bremner, Raven's Point Consulting