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.

Advertisements
This entry was posted in Uncategorized. Bookmark the permalink.

2 Responses to Dilemma

  1. Gautam says:

    A very interesting article, so true. However in a real world situations, it is hardly ever apparent who is a black hat, so task for TitForTat becomes difficult.

    • ravenspoint says:

      There is no need to recognize a black hat in the real world. TitForTat co-operates with everyone at first. If betrayed, TitForTat will then also betray. For this strategy to succeed, all that is needed is for the player to recognize previous opponents, and remember how they behaved before.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s