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).

Arbre de probabilité

Arbre de probabilité

Si on fait la somme des probabilités, on peut voir qu’on a :

  • X/4 + Y/4 + (1-X-Y)/2 -> +0
  • X/2 + Y/4 + (1-X-Y)/4 -> +1
  • X/4 + Y/2 + (1-X-Y)/4-> -1

Cela demande un petit peu d’interprétation : L’espérance vaut la probabilité d’un évenement multiplié par son gain, en d’autres termes, on peut espérer gagner :

(X/4 + Y/4 + (1-X-Y)/2) * 0 + (X/2 + Y/4 + (1-X-Y)/4) * 1 - (X/4 + Y/2 + (1-X-Y)/4) * 1 = X/4 - Y/4

On déduit l’espérance = X/4 - Y/4.

Concrètement, cela implique que l’on doit optimiser X et minimiser Y afin d’obtenir l’espérance la plus élevée, notre algorithme devrait donc retourner (1, 0, 0).

Notre adversaire à un biais sur les ciseaux, pour le contrer, on doit logiquement adopter le comportement qui consiste à minimiser son point fort (notre feuille), et maximiser notre point fort(le caillou).

Quand le joueur A n’est pas biaisé

On passe maintenant au cas où A est un joueur non biaisé, c’est-à-dire qu’il joue uniformément (que son vecteur vaut désormais (0.33,0.33,0.33)).

#Joueur A non biaisé
p_pierre_a = 0.33;
p_feuille_a = 0.33;
p_ciseaux_a = 0.33;
vecRefA = tirerVecteurCoups(p_pierre_a, p_feuille_a, p_ciseaux_a, n);

#Joueur B biaisé
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.46
#plot(X);

On se retrouve ici dans le même cas que quand A était biaisé et que B ne l’était pas. Le schéma des probabilités est le même, ainsi, notre espérance est censée être de 0.

#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] 0
print("La moyenne vaut : "); mean(X);
## [1] "La moyenne vaut : "
## [1] 0.46
#plot(X);
Arbre de probabilité

Arbre de probabilité

Si on fait la somme des probabilités, on peut voir qu’on a :

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

A noter que on peut logiquement espérer atteindre des gains (ou des pertes) avoisinant les 0, même sur un ensemble très grand, deux joueurs ayant les mêmes probabilités de faire des coups qui s’égalisent…

On peut alors se poser la question de si un joueur équilibré peut perdre : toute tentative d’augmenter une des fréquences entrainera nécéssairement un gain mais aussi une perte (et un nul mais on a pas besoin de le considérer dans l’équation) qui s’annuleront.

optimalValues = getXandYandZ(vecRefA,n,n2);
print("optimalValues :");
## [1] "optimalValues :"
print("X :");
## [1] "X :"
print(optimalValues[1]);
## [[1]]
## [1] 0.2
print("Y :");
## [1] "Y :"
print(optimalValues[2]);
## [[1]]
## [1] 0.8
print("Z :");
## [1] "Z :"
print(optimalValues[3]);
## [[1]]
## [1] 1.110223e-16

Ici, on peut noter que quelque soit le nombre de fois où on va lancer cette séquence, on aura des séquences différentes. Cela peut s’expliquer assez facilement.

Arbre de probabilité

Arbre de probabilité

Si on fait la somme des probabilités, on peut voir qu’on a :

  • 0.33X + 0.33Y + 0.33 * (1-X-Y) -> +0
  • 0.33X + 0.33Y + 0.33 * (1-X-Y) -> +1
  • 0.33X + 0.33Y + 0.33 * (1-X-Y) -> -1

On comprend alors pourquoi aucune solution n’est à privilégier pour contrer un joueur équilibré.

Un joueur parfaitement équilibré peut-il ni perdre ni gagner ?

Si on se cantonne aux fréquences d’apparitions, oui, mais on peut également considérer que certaines séquences vont apparaître, par exemple, le fait de commencer par “pierre”, ou le fait de jouer un “papier” au deuxième coup pour contrer la “pierre” apparue précédemment…

Question 2 : Apprentissage

Dans l’idée, on va compter le nombre de pierre, feuille , ciseaux sortis en face à tout instant, afin de paramétrer un choix “optimal”.

# Renvoie un des deux coup c1 c2 (50% entre les deux)
coupRandom = function(c1,c2){
  temp = runif(1, min= 0 , max = 1);
  if(temp < 0.5){
    return(c1);
  }
  else{
    return(c2);
  }
}

#Tweaks -> pas d'effet de bord
#aims to return a counter to a move seen by the 3 piles.
#not a direct counter so no cheat
#seems to work meh

#Cependant, sans connaitre les fréquences adverses à l'avance, on ne peux pas arriver avec un algorithme optimal qui pourrait décider de quel coup jouer.On assume donc un adversaire à (0.33 0.33 0.33)
coupChoisiOptimal = function (pileP,pileF,pileC){
  total = pileC + pileF + pileP;
  if(total == 0){
    return(tirerUniqueCoup(0.33,0.33,0.33));
  }
  
  else{
    if(pileF < pileC){
      if(pileF < pileP){
        return("c");
      }
      else{
        if(pileF == pileP){
          return(coupRandom("c","f"));
        }
        else{
          return("f");
        }
      }
    }
    else{#pileF>=pileC
      if(pileF == pileC){
        if(pileF<pileP){# F = C < P
          return(coupRandom("c","p"));
        }
        else if (pileF == pileP){
          return(tirerUniqueCoup(0.33,0.33,0.33));
        }
        else{
          return("f");
        }
      }
      else{#pileF>pileC
        if(pileC<pileP){
          return("p");
        }
        else if(pileC == pileP){
          return(coupRandom("p","f"));
        }
        else{
          return("f");
        }
      }
    }
  }
}

#IDentical as the previous function, but in this one, in the case were you can return 2 choices,
#we keep only the ones that gives us an equality or a win, instead of keeping both choices that
#could makes us loose in some cases. On a few cases, we will stay more on the 0 rather on the +1 -1, but
# in a lot of cases, we will tend to earn more.
coupChoisiOptimal2 = function (pileP,pileF,pileC){
  total = pileC + pileF + pileP;
  if(total == 0){
    return(tirerUniqueCoup(0.33,0.33,0.33));
  }
  
  else{
    if(pileF < pileC){
      if(pileF < pileP){# F < C && F < P
        return("c");
      }
      else{ # F < C && F >= P
        return("f");
      }
    }
    else{#pileF>=pileC
      if(pileF == pileC){
        if(pileF<pileP){# F = C < P -> better return c, it kills the f and draw the c
          return("c");
        }
        else if (pileF == pileP){ # F = C && F = P
          return(tirerUniqueCoup(0.33,0.33,0.33));
        }
        else{# F = C && F > P
          return("f");
        }
      }
      else{#pileF>pileC
        if(pileC<pileP){ # F > C && C < P
          return("p");
        }
        else if(pileC == pileP){ # F > C && C = P
          return("p");
        }
        else{ # F > C && C > P
          return("f");
        }
      }
    }
  }
}
#On suppose que l'on compare toujours à un vecteur de reférence refA.
#retourne le vecteur optimisé par rapport à vecRefA
processNTurn = function(n,vecRefA){
  pileP = 0; #compteur de Pierre
  pileF = 0; #compteur de Feuille
  pileC = 0; #compteur de Ciseaux
  
  leReturn = vector(mode="character", length = n);
  for(i in 1 :n){
    leReturn[i] = coupChoisiOptimal2(pileP,pileF,pileC);
    if(vecRefA[i] == "p"){
      pileP = pileP + 1;
    }
    else if(vecRefA[i] == "c"){
      pileC = pileC + 1;
    }
    else{
      pileF = pileF + 1;
    }
  }
  return(leReturn);
}

#Compare deux vecteurs, puis retourne le résultat de la comparaison entre le vecteur deux et le vecteur un
calculWhole3 = function(vecA,vecB,n){
  
  #We compare B to A, so B is first in our func
  compared = comparaison(vecB,vecA,n);
  return(resultat(compared,n));
}

#Creer un vecteur de taille n2 , appelant n2 fois les fonctions précédentes.
createFinalVector3 = function(vecRefA,n,n2){
  X = integer(length = n2);
  for(i in 1:n2){
    X[i] = calculWhole3(vecRefA,processNTurn(n,vecRefA),n);
  }
  return(X);
}

X = createFinalVector3(vecRefA,n,n2);

print("La médiane vaut pour l'algo optimisé : "); median(X);
## [1] "La médiane vaut pour l'algo optimisé : "
## [1] 4
print("La moyenne vaut pour l'algo optimisé : "); mean(X);
## [1] "La moyenne vaut pour l'algo optimisé : "
## [1] 3.9
#Joueur B
ppb = 0.33;
pfb = 0.33;
pcb = 0.33;
X = createFinalVector2(vecRefA,ppb,pfb,pcb,n,n2);

print("La médiane vaut pour un joueur équilibré sans mémoire: "); median(X);
## [1] "La médiane vaut pour un joueur équilibré sans mémoire: "
## [1] -0.5
print("La moyenne vaut pour un joueur équilibré sans mémoire: "); mean(X);
## [1] "La moyenne vaut pour un joueur équilibré sans mémoire: "
## [1] -0.12

Cette proposition ne semble pas fonctionner correctement, et nous ne pouvons pas expliquer pourquoi.

  • Première proposition :
#Joueur A
#On passe dans le cas où il est biaisé.
ppa = 0.25;
pfa = 0.25;
pca = 0.5;
vecRefA = tirerVecteurCoups(ppa,pfa,pca,n);

X = createFinalVector3(vecRefA,n,n2);

print("La médiane vaut pour l'algo optimisé : "); median(X);
## [1] "La médiane vaut pour l'algo optimisé : "
## [1] -10
print("La moyenne vaut pour l'algo optimisé : "); mean(X);
## [1] "La moyenne vaut pour l'algo optimisé : "
## [1] -9.86
#Joueur B
ppb = 0.33;
pfb = 0.33;
pcb = 0.33;
X = createFinalVector2(vecRefA,ppb,pfb,pcb,n,n2);

print("La médiane vaut pour un joueur équilibré sans mémoire: "); median(X);
## [1] "La médiane vaut pour un joueur équilibré sans mémoire: "
## [1] 1
print("La moyenne vaut pour un joueur équilibré sans mémoire: "); mean(X);
## [1] "La moyenne vaut pour un joueur équilibré sans mémoire: "
## [1] 1.22
#plot(X);

Ici, il semble logique que l’on obtienne des résultats défavorables : notre algorithme cherche à gagner contre un joueur de vecteur (0.33, 0.33, 0.33), or, notre adversaire sortira plus de ciseaux que de pierres ou de feuilles (vecteur (0.25, 0.25, 0.5)).

Cela veut dire que lorsque notre adversaire aura joué autant de pierres, que de feuilles, que de ciseaux, notre algorithme rendra un coup aléatoire, alors que le coup optimal aurait été de jouer une pierre. En effet, notre adversaire aura deux fois plus de chances de jouer un ciseau qu’une pierre ou une feuille. Cela explique les écarts avec un joueur rendant simplement des coups aléatoires (avec un vecteur (0.33, 0.33, 0.33)).

  • Deuxième proposition :

Le principe reste le même, mais le code semble plus clair.

#Joueur A, jeu biaisé.
ppa = 0.25;
pfa = 0.25;
pca = 0.5;
n = 100;
vec_coups_joueur = tirerVecteurCoups(ppa,pfa,pca,n);

# vecteur de coups sans "intelligence" pour comparer
vec_coups_alea_machine = tirerVecteurCoups(0.33, 0.33, 0.33, n);

# vecteur coups joués avec "intelligence"
vec_coups_intelligents = vector(mode="character", length = n);


freq_pierre = 0;
freq_papier = 0;
freq_ciseaux = 0;

nb_pierre = 0;
nb_papier = 0;
nb_ciseaux = 0;

for(i in 1 :n){

  # max
  coup_freq_max = 'p';
  if(freq_ciseaux > freq_pierre){
    coup_freq_max = 'c';
    if(freq_papier > freq_ciseaux){
      coup_freq_max = 'f';
    }
  }
  if(freq_papier > freq_pierre){
      coup_freq_max = 'f';
  }
  
  #print("Freq max :");print(coup_freq_max);
  
  # joue coup complémentaire du coup de l'adversaire le plus fréquent
  if(coup_freq_max == 'f'){
    vec_coups_intelligents[i] = 'c';
  }
    
  if(coup_freq_max == 'c'){
     vec_coups_intelligents[i] = 'p';
  }
     
  if(coup_freq_max == 'p'){
    vec_coups_intelligents[i] = 'f';
  }
      
  
  if(vec_coups_joueur[i] == 'f'){
    nb_papier = nb_papier + 1;
  }
  if(vec_coups_joueur[i] == 'p'){
    nb_pierre = nb_pierre + 1;
  }  
  if(vec_coups_joueur[i] == 'c'){
    nb_ciseaux = nb_ciseaux + 1;
  }
  
  freq_pierre = nb_pierre / i;
  freq_papier = nb_papier / i;
  freq_ciseaux = nb_ciseaux / i;

}

# compare les gains
## jeu non intelligent
print(resultat(comparaison(vec_coups_joueur, vec_coups_alea_machine, n), n));
## [1] -18
## jeu intelligent
print(resultat(comparaison(vec_coups_joueur, vec_coups_intelligents, n), n));
## [1] 5

On remarquera qu’il est parfois possible de perdre plus avec cet algorithme qu’avec un jeu purement aléatoire.