Le dilemme du prisonnier itératif avec renoncement

Dans le jeu du "dilemme du prisonnier", deux détenus sont emprisonnés dans des cellules séparées. La police fait à chacun des deux le même marché :
"Tu as le choix entre dénoncer ton complice ou non. Si tu le dénonces et qu'il te dénonce aussi, vous aurez une remise de peine d'un an tous les deux. Si tu le dénonces et que ton complice te couvre, tu auras une remise de peine de 5 ans, mais ton complice tirera le maximum. Mais si vous vous couvrez mutuellement, vous aurez tous les deux une remise de peine de 3 ans."
Dans cette situation, il est clair que si les deux s'entendent, ils s'en tireront globalement mieux que si l'un des deux dénonce l'autre. Mais alors l'un peut être tenté de s'en tirer encore mieux en dénonçant son complice. Craignant cela, l'autre risque aussi de dénoncer son complice pour ne pas être le dindon de la farce. Le dilemme est donc : "faut-il coopérer avec son complice (et donc le couvrir) ou non ?"
Le dilemme du prisonnier devient plus intéressant et plus réaliste lorsque la durée de l'interaction n'est pas connue. On peut alors envisager de se souvenir du comportement d'un joueur à son égard et développer une stratégie en rapport. Par exemple, si je sais que mon adversaire ne coopère jamais, mon intérêt de ne pas coopérer non plus, sous peine d'être systématiquement grugé. Par contre, si je sais que mon adversaire coopérera toujours quoi qu'il arrive, j'aurai intérêt à être vicieux et ne jamais coopérer pour maximiser mon gain.
Le but de cet exercice est de définir une stratégie, i.e. de définir un comportement pour savoir quand accepter et quand refuser la coopération avec un autre joueur. La seule information connue est la liste des coups qu'il a joués jusque-là.

Prérequis

voir dans le menu Help :
	- la rubrique Lists and Matrices et les sous-rubriques concernant les listes
	- la rubrique Programming et ses sous-rubriques :
		Tests
		Logical Operations
		Flow Control
		Scoping : Module
voir aussi les exemples donnés sous le paragraphe Les joueurs ci-dessous.

Le jeu

Conventions

moi : liste des coups du joueur courant
lui : liste des coups de l'adversaire
C : coopère
T : trahit
R : renonce
Tous les modules doivent avoir deux paramètres (lui_, moi_) désignant respectivement la liste des coups de l'adversaire et celle du joueur courant, même si seule la liste moi_ est nécessaire à la stratégie utilisée. Le fait d'utiliser ce format s'avérera très pratique pour écrire un programme simple permettant de simuler un tournoi avec autant de joueurs que l'on veut.

Idée du travail

L'idée est de travailler avec des listes, structures de données que Mathematica  gère particulièrement  bien  et qui sont relativement faciles à manipuler. Chaque liste représentera tous les coups joués : C veut dire que le joueur accepte de coopérer, T signifie qu'il refuse, R qu'il renonce.

Gains

gain[lui,moi] : gain du joueur courant en fonction de son coup (moi) et du coup de l'adversaire (lui).

gain[C,C] = 3;
gain[C,T] = 5;
gain[T,C] = 0;
gain[T,T] = 1;
gain[T,R] = 2;
gain[C,R] = 2;
gain[R,R] = 2;
gain[R,C] = 2;
gain[R,T] = 2;

Renoncer

Renonce à jouer.

Renonce[moi_] := 
	Module[{},
		Append[moi, R]
	];

Les joueurs

Joueur ToujoursSeul

Ne coopère jamais.

Coup1ToujoursSeul := {T};
ToujoursSeul[lui_, moi_] := 
	Module[{},
		Append[moi, T]
	];

Joueur BonnePoire

Coopère toujours.

Coup1BonnePoire := {C};
BonnePoire[lui_, moi_] := 
	Module[{},
		Append[moi, C]
	];

Joueur Aléatoire

Joue avec une probabilité égale C (coopère) ou T (trahit).

Coup1Aleatoire := {C};
Aleatoire[lui_, moi_] := 
	Module[{},
		Append[moi, {C,T}[[Random[Integer,{1,2}]]]]
	];

Joueur DonnantDonnant

Coopère seulement si l'autre joueur a coopéré au coup précédent.

Coup1DonnantDonnant := {C};
DonnantDonnant[lui_, moi_] := 
	Module[{},
		If[SameQ[Last[lui],C], Append[moi,C], Append[moi,T]]
	];

Joueur Calculateur

Refuse de coopérer si l'adversaire a plus de 60% d'acceptation après 10 coups. Il essaie ainsi de repérer les "bonnes poires" pour les exploiter. Le vilain... D'un autre côté, quand l'adversaire ne refuse pas systématiquement la coopération (20% de oui au moins), il accepte les coups dont le numéro se termine par 1 et 2. Dans les autres cas, il applique la stratégie "donnant donnant".

Coup1Calculateur := {C};
Calculateur[lui_, moi_] := 
	Module[{nc,long,coup=X},   (* variables locales *)
		nc=Count[lui,C]; (* compte le nombre de C de l'adversaire *)
		long=Length[lui];
		If[nc/long>0.6 && long>9,coup=T];
		If[(Mod[long,10]==1 || Mod[long,10]==2) && nc/long>0.2, coup=C];
		If[SameQ[coup,X], DonnantDonnant[lui,moi], Append[moi,coup]]
	];

Joueur Renonce40

Donnant Donnant, mais renonce si l'adversaire joue plus de 40% de T.

Coup1Renonce40 := {C};
Renonce40[lui_, moi_] := 
	Module[{nt,long,coup=X},  
		nt=Count[lui,T];
		long=Length[lui];
		If[nt/long>0.4 && long>9,coup=R];
		If[SameQ[coup,X], DonnantDonnant[lui,moi], Append[moi,coup]]
	];

Le programme principal

Le tournoi

Le petit programme ci-dessous simule la rencontre entre deux joueurs. Une partie se joue en 200 coups. A la fin de la partie, le résultat des deux protagonistes est affiché.
En rouge, les bouts de code à modifier.

joueur1 = Coup1Aleatoire;
joueur2 = Coup1Renonce40;

gain1 = gain[Last[joueur2],Last[joueur1]];
gain2 = gain[Last[joueur1],Last[joueur2]];
fin = (SameQ[Last[joueur1],R] || SameQ[Last[joueur2],R]);
aux1 = joueur1;
aux2 = joueur2;
Do[
	If[fin,{aux1=Renonce[joueur1], aux2=Renonce[joueur2]},
		{
		aux1 = Aleatoire[joueur2,joueur1];
		aux2 = Renonce40[joueur1,joueur2];	
		}
	];
	joueur1 = aux1;
	joueur2 = aux2;
	gain1 += gain[Last[joueur2],Last[joueur1]];
	gain2 += gain[Last[joueur1],Last[joueur2]];
	fin = (SameQ[Last[joueur1],R] || SameQ[Last[joueur2],R]),
	{199}
];
Print["Coups et gain total du joueur 1"]
joueur1
gain1
Print["Coups et gain total du joueur 2"]
joueur2
gain2

Travail à effectuer

1. Programmez 2 ou 3 stratégies. Chaque stratégie doit être encapsulée dans un module que vous nommerez par exemple Prénom. Il faut aussi définir une variable Coup1Prénom pour indiquer le premier coup (C, T ou R). N'oubliez pas non plus le commentaire qui décrira succinctement votre stratégie.
2. Testez ces stratégies en modifiant le programme présenté sous la rubrique tournoi ci-dessus et choisir la meilleure.
3. Rendez votre stratégie préférée au professeur en vue du tournoi qui mettra en lice tous les élèves de la classe. Le nom de cette stratégie sera votre prénom.