Le dilemme du prisonnier itératif

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

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 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 de coopérer 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},
		nc=Count[lui,C];
		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 Francois

Mon programme regarde les 20 premier coups de son adversaire (en jouant donnant donnant) puis suivant la stratégie de l'autre il se met soit à trahir (BonnePoire, ToujoursSeul) soit a continuer a jouer donnant donnant

Coup1Francois := {C};
Francois[lui_, moi_] := 
	Module[{nc,mc,nt,mt,long,c1,c2,coup=X},
		nc=Count[lui,C];
		mc=Count[moi,C];
		nt=Count[lui,T];
		mt=Count[moi,T];
		long=Length[lui];
		If[long>20 && nc/long>0.75,coup=T];
		If[mt>1 && long>20 && nt/mt>0.9,coup=T];
		If[long>20 && nc/mc==0,coup=T];
		If[nc>1 && long>20 && 0.4<nt/nc && nt/nc<0.6,coup=T];
		If[SameQ[coup,X], DonnantDonnant[lui,moi],Append[moi,coup]]
	];

Joueur Simon

Si l'adversaire accepte 80% des coups après 5 rondes, Simon refusera toujours. Il jouera de la même manière si l'adversaire n'accepte que 10% au bout de 5 coups. Si l'adversaire accepte entre 39 et 61% après 10 coups, Simon jouera comme Aléatoire. (Il joue comme un certain Benoît)

Coup1Simon := {C};
Simon[lui_, moi_] := 
	Module[{na,nr,long,coup=H},
	na=Count[lui,C];
	nr=Count[lui,T];
	long=Length[lui];
	If[na/long>0.79 && long>4,coup=T];
	If[na/long<0.11 && long>9,coup=T];
	If[na/long>0.39 && na/long<0.61 && long>9 || SameQ[coup,H],
	  Aleatoire[lui,moi],Append[moi,coup]]
	];

Joueur Jeremie

Mon programme , si le joueur adverse a plus de 75% de coopération (après 9 coups), coopère 
                                                si le joueur adverse a plus de 50% de trahison (après 9 coups), trahie
                                                            si le joueur adverse a 100% de coopération (après 10 coups), trahie
Dans les autres cas, il applique la stratégie donnant-donnant

\(\(Coup1Jeremie := {T};\)

      \ \ \ \ \ \(Jeremie[lui_, moi_] := 
	
          Module[{nc, nt, long, coup = C}, 
	\ \ \ \ nc = 
              Count[lui, C]; 
		nt = Count[lui, T]; 
		\ \ long = 
              Length[lui]; 
	\ \ \ \ \ \ \ \ \ \ \ \ \ \ If[
              SameQ[Last[lui], C], \ coup = C, coup = T]; 
		If[
              nc/long > 0.75\  && \ long > 9, coup = C]; 
	\ \ \ \ If[
              nt/long > 0.5\  && \ long > 9, coup = T]; 
\ 
            If[nc/long == 1\  && \ long > 10, coup = T]; 

            Append[moi, coup]
		];\)\)

Joueur Nicolas

Mon programme regarde ce que l'autre à fait lors des huits derniers coups afin de dénicher les bonnes poires ou les "toujours trahir" afin de toujours les trahir et ainsi engrenger un maximum de points.Si l'autre est dans une fourchette au milieux,mon programme coopérera toujours.

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

Joueur Florie

je coopère 2 fois puis si l'autre joueur coopère a plus de 50% je trahi,si son taux de coopération est de 30à 50% je coopère et si il coopère a moins de 30% je le trahi.si il coopère 3 fois de suite je le trahi egalement.

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

Joueur Patrice

Le best (Benoît).

Coup1Patrice := {C};
Patrice[lui_, moi_] := 
	Module[{na,nr,long,coup=H},
	na=Count[lui,C];
	nr=Count[lui,T];
	long=Length[lui];
	If[na/long>0.79 && long>4,coup=T];
	If[na/long<0.11 && long>9,coup=T];
	If[na/long>0.39 && na/long<0.61 && long>9 || SameQ[coup,H],
	  Aleatoire[lui,moi],Append[moi,coup]]
	];

Joueur Florent

Pour commencer je trahis.
     Ensuite j'ai des variables {nt,ntm,ncm,nc,long,coup=X} qui me permette de savoir ou j'en suis dans le jeu.
     Si l'adversaire n'a pas encore trahis, je le trahis.
     S'il m'as trahis plus de 2 fois le nombre de ses collaborations(nt>2*nc),je trahis.
    " If[long>1,nt>0&&ncm==(nc+1)&&(ntm+1)==nt,coup=C];" sert a détecter les donnant donnant.
     Si c'est un donnant donnant, je collabore pour faire plus de points.

Coup1Florent := {T};
Florent[lui_, moi_]:=
Module[{nt, ntm, ncm, nc, long, coup = X},
nc = Count[lui, C];

              ncm = Count[moi, C];

              ntm = Count[moi, T];

              nt = Count[lui, T];

              long = Length[lui];
If[nt == 0, coup = T];
If[nt > 2*nc, coup = T];
If[long > 1, 
                nt > 0 && 
                  ncm == (nc + 1) && (ntm + 1) == nt, 
                coup = C];
If[SameQ[coup, X], DonnantDonnant[lui, moi], 
                Append[moi, coup]]
];

Joueur MarieB

Je refuse de coopérer si l'adversaire accepte 80% des coups après 5 rondes. Je joue de la même manière si l'adversaire n'accepte que 15% au bout de 10 coups. Si l'adversaire accepte entre 25 et 75% après 10 coups, je joue comme Aléatoire.

Coup1MarieB := {C};
MarieB[lui_, moi_] := 
        Module[{nc, long, coup = H}, 
nc = 
            Count[lui, C]; 
long = 
            Length[lui]; 
If[nc/long > 0.79 && long > 4, 
            coup = T]; 
If[nc/long < 0.16 && long > 9, 
            coup = T]; 
If[
            nc/long > 0.24 && nc/long < 0.76 && long > 9 || SameQ[coup, H], 
            Aleatoire[lui, moi], 
            Append[moi, coup]]
];

Joueur David

Fais l'inverse de ce qu'a fait l'adversaire au coup précedent.

Coup1David := {C};
David[lui_, moi_] := 
	Module[{},
		If[UnsameQ[Last[lui],C], Append[moi,C], Append[moi,T]]];

Joueur Sven

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.

phase=normal;
phase2=auLit;
Coup1Sven := {C};
Sven[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,
				{
					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] = 8;  (* 8 pour le dilemme de l'ascenseur *)
gain[T,C] = 0;
gain[T,T] = 1;

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=15;
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] = "Calculateur (3)";
Coup1[3] = Coup1Calculateur;
Strategie[3][lui_,moi_] := Calculateur[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] = "François (6)";
Coup1[6] = Coup1Francois;
Strategie[6][lui_,moi_] := Francois[lui, moi];

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

Nom[8] = "Jérémie (8)";
Coup1[8] = Coup1Jeremie;
Strategie[8][lui_,moi_] := Jeremie[lui, moi];

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

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

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

Nom[12] = "Florent (12)";
Coup1[12] = Coup1Florent;
Strategie[12][lui_,moi_] := Florent[lui, moi];

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

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

Nom[15] = "Sven (15)";
Coup1[15] = Coup1Sven;
Strategie[15][lui_,moi_] := Sven[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]]];
		aux1 = joueur1;
		aux2 = joueur2;
		Do[			
			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]]],
			{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]]]