GENTILLON Loris, SURIER GAROFALO Aurelien

Probabilités et Simulation

DM PIERRE FEUILLE CISEAUX

Dans ce DM, on s’intéresse au jeu de Pierre Papier Ciseaux et on cherche à savoir quelle est la bonne stratégie à avoir. Dans la première partie on s’intéressera à l’estimation de l’espérance de gain dans des cas de jeux biaisés ou non, et dans la seconde partie comment donner un minimum d’intelligence à la machine pour augmenter son espérance de gain contre l’Humain.

Boîte à outils de fonctions

# sémantique : 
# p_pierre : probabilité de jouer pierre
# p_feuille: probabilité de jouer feuille
# p_ciseaux : probabilité de jouer ciseaux

# Probabilités pour le Joueur A (uniforme : non biaisé)
p_pierre_a = 0.33;
p_feuille_a = 0.33;
p_ciseaux_a = 0.33;

# Probabilités pour le Joueur B (uniforme : non biaisé)
p_pierre_b = 0.33;
p_feuille_b = 0.33;
p_ciseaux_b = 0.33;

# Nombre de coup de simulation
n = 100; # Le nombre de fois ou on fait pierre papier ciseaux
n2 = 50; # Le nombre de moyennes que l'on veut calculer -> n2 moyenne pour lisser

# Joue un coup
tirerUniqueCoup = function(ppa, pfa, pca){
  s1 = ppa;
  s2 = ppa + pfa;
  s3 = ppa + pfa + pca;
  temp = runif(1, min= 0 , max = s3);
  if(temp < s1){
    X = 'p';
  }
  else if (temp < s2){
    X = 'f';
  }
  else{
    X = 'c';
  }
  return(X);
}

# créer un vecteur de taille n en répartissant les valeurs p, f et c selon leurs probabilités associées = c'est une simulation de jeu
tirerVecteurCoups = function(ppa, pfa, pca, n){
  s1 = ppa;
  s2 = ppa + pfa;
  s3 = ppa + pfa + pca;
  temp = runif(n, min= 0 , max = s3);
  X = vector(mode="character", length = n);
  for(i in 1:n){
    if(temp[i] < s1){
      X[i] = 'p';
    }
    else if (temp[i] < s2){
      X[i] = 'f';
    }
    else{
      X[i] = 'c';
    }
  }
  return(X);
}

# retourne un vecteur de comparaison (+1 , 0 , -1) entre deux vecteurs coupA, coupB. Tous les vecteurs sont de taille n.
comparaison = function(coupA, coupB, n){
  X = integer(length = n)
  for(i in 1:n){
    if(coupA[i] == 'p' & coupB[i] == 'p' ){
      X[i] = 0;
    }
    else if(coupA[i] == 'p' & coupB[i] == 'f' ){
      X[i] = -1;
    }
    else if(coupA[i] == 'p' & coupB[i] == 'c' ){
      X[i] = 1;
    }
    else if(coupA[i] == 'f' & coupB[i] == 'p' ){
      X[i] = 1;
    }
    else if(coupA[i] == 'f' & coupB[i] == 'f' ){
      X[i] = 0;
    }
    else if(coupA[i] == 'f' & coupB[i] == 'c' ){
      X[i] = -1;
    }
    else if(coupA[i] == 'c' & coupB[i] == 'p' ){
      X[i] = -1;
    }
    else if(coupA[i] == 'c' & coupB[i] == 'f' ){
      X[i] = 1;
    }
    else if(coupA[i] == 'c' & coupB[i] == 'c' ){
      X[i] = 0;
    }
  }
  return(X)
}

# retourne la somme des éléments du vecteur X de taille n.
resultat = function(X,n){
  return(sum(X[1:n]));
}

# créer deux vecteurs, puis retourne la comparaison entre les deux (du point de vue de B)
comparer_jeu = function(ppa,pfa,pca,ppb,pfb,pcb,n){
  coupsA = tirerVecteurCoups(ppa,pfa,pca,n);
  coupsB = tirerVecteurCoups(ppb,pfb,pcb,n);
  
  #We compare B to A, so B is first in our func
  compared = comparaison(coupsB,coupsA,n)
  return(resultat(compared,n))
}

# créer un vecteur de coups, puis le compare avec le vecteur vecA en paramètre (du point de vue de B)
comparer_jeu_a_fixe = function(vecA,ppb,pfb,pcb,n){
  coupsA = vecA;
  coupsB = tirerVecteurCoups(ppb,pfb,pcb,n);
  
  #We compare B to A, so B is first in our func
  compared = comparaison(coupsB,coupsA,n);
  return(resultat(compared,n));
}

# Créer un vecteur de taille n2, appelant n2 fois les fonctions précédentes.
createFinalVector = function(ppa, pfa, pca, ppb, pfb, pcb, n, n2){
  X = integer(length = n2);
  for(i in 1:n2){
    X[i] = comparer_jeu(ppa, pfa, pca, ppb, pfb, pcb, n);
  }
  return(X);
}

# Créer un vecteur de taille n2, appelant n2 fois les fonctions précédentes.
createFinalVector2 = function(vecA, ppb, pfb, pcb, n, n2){
  X = integer(length = n2);
  for(i in 1:n2){
    X[i] = comparer_jeu_a_fixe(vecA, ppb, pfb, pcb, n);
  }
  return(X);
}

# Fonction plus utilisée (bogue)
analyze = function(ppa,pfa,pca,ppb,pfb,pcb,n){
  X = createFinalVector(ppa,pfa,pca,ppb,pfb,pcb,n);
  median(X);
  mean(X);
  plot(X);
}

Question 1

Quand le joueur A est biaisé

  1. Cas où les deux joueurs n’ont pas de mémoire de leurs coups ou de ceux de leurs adversaires.
  • Probabilités de jeu de A : (0.25,0.25,0.5)
  • Probabilité de jeu de B : (0.25,0.25,0.5).
# le joueur A est biaisé, avec plus de chance de jouer ciseaux
p_pierre_a = 0.25;
p_feuille_a = 0.25;
p_ciseaux_a = 0.5;

# coups joués par A
vecRefA = tirerVecteurCoups(p_pierre_a, p_feuille_a, p_ciseaux_a, n);

# le joueur B est également biaisé, avec plus de chance de jouer ciseaux
p_pierre_b = 0.25;
p_feuille_b = 0.25;
p_ciseaux_b = 0.5;

X = createFinalVector2(vecRefA, p_pierre_b, p_feuille_b, p_ciseaux_b, n, n2);
print("La médiane vaut : "); median(X);
## [1] "La médiane vaut : "
## [1] 2
print("La moyenne vaut : "); mean(X);
## [1] "La moyenne vaut : "
## [1] 2.44
#plot(X);
Arbre de probabilité

Arbre de probabilité

Si on fait la somme des probabilités, on obtient :

  • 0.375 -> +0
  • 0.3125 -> +1
  • 0.3125 -> -1

Ainsi, jouer contre un joueur biaisé en ayant le même biais que lui semble donner une espérance de 0.

  1. On a maintenant B de vecteur (0.33,0.33,0.33) (et A toujours de vecteur (0.25,0.25,0.5)).
# Joueur A : on garde le même vecteur que précédemment, afin d'avoir une base de comparaison commune.

# Joueur B
p_pierre_b = 0.33;
p_feuille_b = 0.33;
p_ciseaux_b = 0.33;
X = createFinalVector2(vecRefA, p_pierre_b, p_feuille_b, p_ciseaux_b, n, n2);

print("La médiane vaut : "); median(X);
## [1] "La médiane vaut : "
## [1] -3
print("La moyenne vaut : "); mean(X);
## [1] "La moyenne vaut : "
## [1] -2.84
#plot(X);

arbre2 Lorsque l’on somme les probabilités, on obtient :

  • 0.33 -> +0
  • 0.33-> +1
  • 0.33-> -1

Ainsi, jouer contre un joueur biaisé en n’ayant pas de biais laisse dans ce cas encore espérer d’un gain de 0 (espérance = 0).

  1. On cherche maintenant à optimiser B de vecteur (X,Y,1 - X - Y) (avec A toujours de vecteur (0.25,0.25,0.5)). On veut faire varier X et Y par pas de 0.1, sachant que la somme des composantes du vecteurs devra toujours valoir 1 et que X + Y doit être inférieur ou égal à 1 (sinon la troisième composante peut valoir moins de 1). Logiquement, on a moins d’une centaine de combinaisons à tester : X de 0 à 1 à coup de 0.1 ; puis Y de 0 à 1 -X à coup de 0.1.
# retourne une liste Li composée de 64 vecteurs, chacun avec des valeurs différentes de X, Y et Z.
createAllVector = function (vecRefA,n,n2){
  Li = list();
  indiceLi = 1;
  X = 0;
  Y = 0;
  Z = 1 - X - Y;
  for(i in 1:10){
    for (j in 1:10){
      if (X + Y > 1){
        break;
      }
      temp = createFinalVector2(vecRefA,X,Y,Z,n,n2);
      Li[[indiceLi]] = temp;
      indiceLi = indiceLi + 1;
      Y = Y + 0.1;
    }
    Y = 0;
    X = X + 0.1;
  }
  return(Li);
}

# idem mais créer une liste de moyenne
createAllVectorAndMedian = function (vecRefA,n,n2){
  Li = list();
  indiceLi = 1;
  X = 0;
  Y = 0;
  Z = 1 - X - Y;
  for(i in 1:10){
    for (j in 1:10){
      if (X + Y > 1){
        break;
      }
      temp = mean(createFinalVector2(vecRefA,X,Y,Z,n,n2));
      Li[[indiceLi]] = temp;
      indiceLi = indiceLi + 1;
      Y = Y + 0.1;
    }
    Y = 0;
    X = X + 0.1;
  }
  return(Li);
}

# Return a lsit with the X Y Z of the optimal solution for a particular verRefA
getXandYandZ = function (vecRefA,n,n2){
  Li = integer();
  LX = list();
  LY = list();
  indiceLi = 1;
  X = 0;
  Y = 0;
  Z = 1 - X - Y;
  for(i in 1:10){
    for (j in 1:10){
      if (X + Y > 1){
        break;
      }
      temp = mean(createFinalVector2(vecRefA,X,Y,Z,n,n2));
      Li[[indiceLi]] = temp;
      LX[[indiceLi]] = X;
      LY[[indiceLi]] = Y;
      indiceLi = indiceLi + 1;
      Y = Y + 0.1;
    }
    Y = 0;
    X = X + 0.1;
  }
  #print(max(Li));
  
  #this line from https://stat.ethz.ch/pipermail/r-help/2007-January/123788.html
  #to sort the maximal value
  #print(which(Li == max(Li))); 
  last = which(Li == max(Li));
  lereturn = list();
  lereturn[[1]] = LX[[last]];
  lereturn[[2]] = LY[[last]];
  lereturn[[3]] = 1 - lereturn[[1]] - lereturn[[2]] 
  return(lereturn);
}

optimalValues = getXandYandZ(vecRefA,n,n2);
print("optimalValues :");
## [1] "optimalValues :"
print("X :");
## [1] "X :"
print(optimalValues[1]);
## [[1]]
## [1] 0.9
print("Y :");
## [1] "Y :"
print(optimalValues[2]);
## [[1]]
## [1] 0
print("Z :");
## [1] "Z :"
print(optimalValues[3]);
## [[1]]
## [1] 0.1

Après plusieurs exécutions de la fonction écrite ci-dessus, on trouve, pour le vecteur A de valeurs ( 0.25 , 0.25 , 0.5), des valeurs pour B optimales : (0.9, 0 , 0.1), avec cependants quelques variations (comme (0.8, 0, 0.2) par exemple).