Le dilemme du prisonnier itératif avec renoncement

Pour le maître

Ce fichier permettra au maître d'organiser facilement le tournoi entre les stratégies développées par les élèves.

Le tournoi

Conventions

moi : liste des coups du joueur courant
lui : liste des coups de l'adversaire
C : cooère
T : trahit
R : renonce

Les joueurs

Il faut mettre dans cette partie toutes les stratégies avec (éventuellement) leur descriptif. Ces stratégies seront numérotées plus tard pour le tournoi. Chaque module doit avoir comme paramètres lui_ et moi_ (dans cet ordre) et seulement ces deux paramètres. lui_ n'est pas forcément utilisé (voir p. ex. ToujoursSeul ou Aleatoire), mais il doit être présent dans la liste des paramètres pour simplifier l'écriture du code du tournoi proprement dit.

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 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]]
	];

Jérémy

Coup1Jer := {T};
Jer[lui_, moi_]:=
Module[{nt, nc, long, coup = X, i}, 
nt = 
            Count[lui, T]; 
nc = 
            Count[lui, C]; 
long = 
            Length[moi] + 1; 
If[long == 2, 
            coup = C]; 
If[long == 3, 
            coup = T]; 
If[long == 4, 
            coup = C]; 
If[long == 5, 
            coup = T]; 
If[long == 6, 
            coup = C]; 
If[long == 7, 
            coup = T]; 
If[long == 8, 
            coup = C]; 
If[long == 9, 
            coup = T]; 
If[long == 10, 
            coup = C]; 
If[long ≤ 10, 
            jouer = True, jouer = False]; 
i = 
            1; 
While[
            i < long - 1 && 
              long > 10, 
{
If[
                SameQ[Part[moi, i], 
                  Part[lui, i + 1]], {If[SameQ[Last[lui], C], coup = C, 
                    coup = T]; 
                  jouer = 
                    True}]; 
i++
}
]; 
If[
            nt/(long - 1) == 1 && long > 10, {coup = R; 
              jouer = True}]; 
If[
            nc/(long - 1) == 1 && long > 10, {coup = T; 
              jouer = True}]; 
If[
            jouer == False  &&  long > 10, 
            coup = T]; 
Append[moi, coup]
];

Nicolas

Coup1Nicolas := {T};
Nicolas[lui_, moi_]:=
Module[{nc, long, coup = X},
nc = Count[lui, C];
long = Length[lui];
If[nc/long > 0.7 && long > 2, coup = T];
If[nc/long < 0.4 && long > 2, 
              coup = T];
If[SameQ[coup, X], Renonce40[lui, moi], 
              Append[moi, coup]]]

Joueur Simon

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

Joueur David

Fais l'inverse de ce qu'a fait l'adversaire au coup précedent, mais renonce si l'adversaire joue plus de 49% de T.

Coup1David := {C};
David[lui_, moi_] := 
	Module[{nt,long,coup=X},  
		nt=Count[lui,T];
		long=Length[lui];
		If[nt/long>0.49 && long>9,coup=R];
		If[UnsameQ[Last[lui],C], Append[moi,C], Append[moi,T]]
	];

Joueur Tristan

Toujours Seul: La stratégie gagne-reste/perd des points-arrete.

Coup1Tristan := {T};
\(\(Tristan[lui_, 
          moi_] := 
Module[{}, 
	
          If[SameQ[Last[lui], C], Append[moi, T], 
            Append[moi, R]]
];\)\)

Joueur Francois

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

Coup1Francois := {C};
Francois[lui_, moi_] := 
	Module[{nc,mc,nt,mt,long,coup=X},
		nc=Count[lui,C];
		mc=Count[moi,C];
		nt=Count[lui,T];
		mt=Count[moi,T];
		long=Length[lui];
		If[long>20 && nt/long>0.8,coup=R];
		If[long>20 && nc/long==1,coup=T];
		If[long>24 && SameQ[moi[[22]],T] && SameQ[moi[[23]],T]  && SameQ[lui[[22]],C] && SameQ[lui[[23]],T],coup=C];
		If[SameQ[coup,X], DonnantDonnant[lui,moi],Append[moi,coup]]
			];

Joueur MarieB

Mon programme regarde après 20 coups si mon adversaire trahit à plus de 80%, si oui je renonce. Il regarde aussi s'il a toujours coopérer et si c'est le cas je trahis.
Après 24 coups je regarde si, lorsque je le trahis 2x de suite, il me trahit aussi, si c'est le cas je coopère.

\(\(\(
\)\(\(Coup1MarieB := {C};\)

    \(MarieB[lui_, moi_] := 
        Module[{nc, mc, nt, mt, long}, 
          nc = Count[lui, C]; 
mc = 
            Count[moi, C]; 
nt = 
            Count[lui, T]; 
mt = 
            Count[moi, T]; 
long = 
            Length[lui]; 
If[long > 20 && nt/long > 0.8, 
            coup = R]; 
If[long > 20 && nc/long == 1, 
            coup = T]; 
If[
            long > 24 && SameQ[moi[\([22]\)], T] && SameQ[moi[\([23]\)], T] && 
              SameQ[lui[\([22]\)], C] && SameQ[lui[\([23]\)], T], 
            coup = C]; 
If[SameQ[Last[lui], C], 
            coup\  = \ T]; 
Append[moi, 
            coup]
];\)

    \)\)\)

Joueur Florie

je regarde les 3 coups précédents. Si mon adversaire et moi avons coopéré tous les deux, je continue de coopérer.Si mon adversaire continue de coopérer malgré que je le trahisse, je continue a trahir.Si mon adversaire me trahi toujours alors que je coopère depuis 2 fois, je renonce et si cela fait 3 fois qu'on se trahi mutuellement, je renonce également.

Coup1Florie := {C};
Florie[lui_, moi_] := 
        Module[{nc, long, coup = X, }, 
nc = 
            Count[lui, C]; 
long = 
            Length[lui]; 
If[long < 3, 
            Append[moi, C]]; 
If[
            long > 3 && SameQ[lui[[long]], C] && 
              SameQ[lui[[long - 1]], C] && SameQ[lui[[long - 2]], C] && 
              SameQ[moi[[long - 2]], C], coup = C]; 
If[
            long > 3 && SameQ[lui[[long]], C] && 
              SameQ[lui[[long - 1]], C] && SameQ[lui[[long - 2]], C] && 
              SameQ[moi[[long - 2]], T], coup = T]; 
If[
            long > 3 && SameQ[lui[[long]], T] && 
              SameQ[lui[[long - 1]], T] && SameQ[lui[[long - 2]], T] && 
              SameQ[moi[[long - 1]], C] && SameQ[moi[[long - 2]], C], 
            coup = R]; 
If[
            long > 3 && SameQ[lui[[long]], T] && 
              SameQ[lui[[long - 1]], T] && SameQ[lui[[long - 2]], T] && 
              SameQ[lui[[long - 3]], T] && SameQ[moi[[long - 1]], T] && 
              SameQ[moi[[long - 2]], T] && SameQ[moi[[long - 3]], T], 
            coup = R]; 
If[SameQ[coup, X], 
            DonnantDonnant[lui, moi], 
            Append[moi, coup]]
];

Joueur Marie W.

Je commence par coopérer deux fois de suite. Ensuite, si l'adversaire trahit, alors je trahis plusieurs fois de suite, le nombre différant en fonction de l'avancement du jeu : si l'on est en début de partie (de 0 à 60 tours), je trahis deux fois le nombre de trahisons de l'adveraire ; en milieu de partie (de 61 à 140 tours), je trahis trois fois le nombre de trahisons de l'adveraire (le nombre de trahisons représente le nombre de trahisons faites par l'adversaire lorsque (j'entre et que) je suis en phase de trahison) ; et en fin de partie (de 141 à la fin), je trahis quatre fois le nombre de trahisons de l'adversaire. Une fois le nombre de trahisons "remboursées" (donc une fois que j'ai trahis 2n, 3n ou 4n fois, avec n représentant le nombre de trahisons de l'adversaire), je coopère quoi qu'il arrive (sauf si l'ordi plante...) deux fois consécutivement, puis je coopère jusqu'à ce que l'adversaire trahisse encore une fois.
Aux dixième et vingtième tours, je regarde le pourcentage de coopération de l'adversaire, et s'il a coopéré au moins 97% du temps, alors j'entre dans la phase de recherche de bobet, et au tour suivant, si l'adversaire a trahi, je quitte cette phase car l'adversaire n'est pas un bobet (enfin...), au contraire, s'il a continé de coopéré, alors je continue de trahir jusqu'à ce qu'il trahisse et que je quitte cette phase de recherche. À l'inverse, si au dixième ou vingtième tour il a trahi au moins 97% du temps, j'entre dans la phase de recherche de fou dans laquelle je trahis systématiquement, et qui s'interrompt lorsqu'il coopère. Il en va de même pour voir si l'adevrsaire joue les mêmes coups que moi, si c'est le cas, j'entre dans la phase de recherche de recopieur.
remarque: a partir du 150ème coup, c'est donnant-donnant

phase=normal;
phase2=auLit;
Coup1MarieW := {C};
MarieW[lui_, moi_] := 
	Module[{tour,coup,trahisonsAdversaire=0},
		t=0;
		u=0;
		tour=Length[moi];
		Which[
			tour<=1,coup=C,
			tour>197,coup=T,
			SameQ[Last[lui],C] && SameQ[phase,normal],coup=C,
			SameQ[Last[lui],T] && SameQ[phase,normal],
			Which[
				tour<=50,
				{
					trahisonsAdversaire=trahisonsAdversaire+1;
					trahisons=3*trahisonsAdversaire;
					phase=trahison;
					coup=T
				},
				tour>50 && tour<=150,
				{
					trahisonsAdversaire=trahisonsAdversaire+1;
					trahisons=4*trahisonsAdversaire;
					phase=trahison;
					coup=T
				},
				tour>150,If[Count[lui,T]/tour>=0.5,coup=R,coup=DonnantDonnant]
				{
					trahisonsAdversaire=trahisonsAdversaire+1;
					trahisons=5*trahisonsAdversaire;
					phase=trahison;
					coup=T
				}
			],
			SameQ[phase,trahison],
			{
				trahisons=trahisons-1;
				If[trahisons==0,{phase=cooperation;cooperations=2}];
				If[SameQ[Last[lui],T],trahisonsAdversaire=trahisonsAdversaire+1];
				coup=T
			},
			SameQ[phase,cooperation],
			{
				cooperations=cooperations-1;
				If[cooperations==0,{phase=normal}];
				If[SameQ[Last[lui],T],trahisonsAdversaire=trahisonsAdversaire+1];
				coup=C
			}
		];
		If[tour>=2,
			While[u<=N[tour],{
				If[SameQ[{Part[moi],u},{Part[toi],u+1}],t=t+1];
				u=u+1
				}
			]
		];
		Which[
			SameQ[phase2,rechercheBobet],
				If[SameQ[Last[lui],T],phase2=pasBobet,coup=T],
			SameQ[phase2,rechercheFou],
				If[SameQ[Last[lui],C],phase2=pasFou,coup=T],
			SameQ[phase2,rechercheRecopieur] && t!=tour-1,
				phase2=pasRecopieur
		];
		If[SameQ[phase2,auLit],
			Which[
				tour==10 && Count[lui,T]/10==1,
				{
					phase2=rechercheFou;
					coup=T
				},
				tour==20 && Count[lui,T]/20>=0.97,
				{
					phase2=rechercheFou;
					coup=T
				},
				tour==10 && Count[lui,C]/10==1,
				{
					phase2=rechercheBobet;
					coup=T
				},
				tour==20 && Count[lui,C]/20>=0.97,
				{
					phase2=rechercheBobet;
					coup=T
				},
				tour==5 && SameQ[t,tour-1],
				{
					phase2=rechercheRecopieur;
					coup=C
				}
			]
		];
		If[UnsameQ[phase2,auLit],
			Which[
				tour==10 && Count[lui,T]/10>=0.97,
				{
					phase2=rechercheFou;
					coup=T
				},
				tour==20 && Count[lui,T]/20>=0.97,
				{
					phase2=rechercheFou;
					coup=T
				},
				tour==10 && Count[lui,C]/10>=0.97,
				{
					phase2=rechercheBobet;
					coup=T
				},
				tour==20 && Count[lui,C]/20>=0.97,
				{
					phase2=rechercheBobet;
					coup=T
				}
			]
		];
		Append[moi,coup]
	];

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]
	];

Le tournoi

Le petit programme ci-dessous simule la rencontre entre toutes les paires de joueurs: tout le monde joue contre tout le monde, y compris soi-même. En rouge, les bouts de code à modifier.
ns est le nombre de stratégies dans le tourmoi.
Il faut ensuite entrer les ns stratégies: d'abord le nom, puis le premier coup à jouer et enfin le nom du module.
SeedRandom est le germe du générateur pseudo-aléatoire que l'on réinitialise au début de chaque rencontre. Ainsi, le joueur Aléatoire jouera exactement les mêmes coups avec tous les joueurs.
Le 199 indique le nombre de coups par rencontre (après le premier coup, donc 200 en tout).

Le classement

Deux résultats sont donnés: le résultat des rencontres et le classement final.
Le premier tableau se lit ligne par ligne. Dans notre exemple, la 1ère ligne se lit comme suit : Bonne Poire (stratégie no 1) a marqué 600 points contre lui-même, 600 contre Donnant donnant (stratégie no 2), 144 contre Calculateur (stratégie no 3), 0 contre Toujours seul (stratégie no 4) et contre 318 Aléatoire (stratégie no 5). Total: 1662.
Le classement final est obtenu en additionnant les résultats ligne par ligne du tableau précédent. Les stratégies sont ensuite triées de la meilleure à la moins bonne (le nombre entre parenthèses indique le numéro de la stratégie).
Note: les résultats peuvent varier sensiblement selon la partie du joueur Aléatoire.

ClearAll[Score];
ns=14;
Array[Nom,ns];
Array[Coup1,ns];
Str=Array[Strategie,ns];
Resultats=Array[Score,{ns,ns}];

Nom[1] = "Bonne Poire (1)";
Coup1[1] = Coup1BonnePoire;
Strategie[1][lui_,moi_] := BonnePoire[lui, moi];

Nom[2] = "Donnant donnant (2)";
Coup1[2] = Coup1DonnantDonnant;
Strategie[2][lui_,moi_] := DonnantDonnant[lui, moi];

Nom[3] = "Renonce 40% (3)";
Coup1[3] = Coup1Renonce40;
Strategie[3][lui_,moi_] := Renonce40[lui, moi];

Nom[4] = "Toujours seul (4)";
Coup1[4] = Coup1ToujoursSeul;
Strategie[4][lui_,moi_] := ToujoursSeul[lui, moi];

Nom[5] = "Aléatoire (5)";
Coup1[5] = Coup1Aleatoire;
Strategie[5][lui_,moi_] := Aleatoire[lui, moi];

Nom[6] = "Jérémy (6)";
Coup1[6] = Coup1Jer;
Strategie[6][lui_,moi_] := Jer[lui, moi];

Nom[7] = "Nicloas (7)";
Coup1[7] = Coup1Nicolas;
Strategie[7][lui_,moi_] := Nicolas[lui, moi];

Nom[8] = "Simon (8)";
Coup1[8] = Coup1Simon;
Strategie[8][lui_,moi_] := Simon[lui, moi];

Nom[9] = "David (9)";
Coup1[9] = Coup1David;
Strategie[9][lui_,moi_] := David[lui, moi];

Nom[10] = "Tristan (10)";
Coup1[10] = Coup1Tristan;
Strategie[10][lui_,moi_] := Tristan[lui, moi];

Nom[11] = "Francois (11)";
Coup1[11] = Coup1Francois;
Strategie[11][lui_,moi_] := Francois[lui, moi];

Nom[12] = "Marie W. (12)";
Coup1[12] = Coup1MarieW;
Strategie[12][lui_,moi_] := MarieW[lui, moi];

Nom[13] = "Marie B. (13)";
Coup1[13] = Coup1MarieB;
Strategie[13][lui_,moi_] := MarieB[lui, moi];

Nom[14] = "Florie (14)";
Coup1[14] = Coup1Florie;
Strategie[14][lui_,moi_] := Florie[lui, moi];


For[i=1, i<=ns,
	For[j=i, j<=ns,
		SeedRandom[999];
		joueur1 = Coup1[i];
		joueur2 = Coup1[j];
		Score[i,j] = gain[Last[joueur2],Last[joueur1]];
		If [i!=j, Score[j,i] = 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 = Strategie[i][joueur2,joueur1];
				aux2 = Strategie[j][joueur1,joueur2];
				}
			];	
			joueur1 = aux1;
			joueur2 = aux2;
			Score[i,j] += gain[Last[joueur2],Last[joueur1]];
			If [i!=j, Score[j,i] += gain[Last[joueur1],Last[joueur2]]];
			fin = (SameQ[Last[joueur1],R] || SameQ[Last[joueur2],R]),
			{199}
		];
		j++
	];
	i++
];

Print["Résultats des rencontres"]
TableForm[Resultats]

Print["Classement"]
ClearAll[Somme];
Classement=Array[Somme,{ns,2}];
For[i=1, i<=ns,
	Somme[i,1]=0;
	Somme[i,2]=Nom[i];
	For[j=1,j<=ns,Somme[i,1] += Score[i,j]; j++];
i++];
TableForm[Reverse[Sort[Classement]]]