basicSize = 50000 ; # Number of games in classic case
plotSize = 10000 ; # Number of games in case of ploting for all x and y in [0;1] by 0.1 steps
On construit une fonction buildBounds(probabiltyVector)
qui crée une liste de vecteurs bounds
de façon à ce que bounds[[i]]
donne un intervalle de taille probabilityVector[i]
et que pour tout i ≠ j, bounds[[i]]
et bounds[[j]]
n’ont aucun élément en commun en dehors, éventuellement, de leur bornes.
L’entrée doit être de la forme (a, b, c)
où a
est la probabilité du premier évènement, b
la probabilité du deuxième évènement et c
la probabilité du troisième évènement. Nécessairement, a + b + c = 1
.
buildBounds = function(probabilityVector){
if (sum(probabilityVector) != 1){
stop("Make sure that ProbabilityVector is really a probability vector (ie sum = 1)");
}
bounds = list();
bounds[[1]] = c(0, probabilityVector[1]);
bounds[[2]] = c(probabilityVector[1], probabilityVector[1] + probabilityVector[2]);
bounds[[3]] = c(probabilityVector[1] + probabilityVector[2], 1);
return(bounds)
}
# Correct case
# buildBounds(c(1/4, 1/4, 1/2));
# sum < 1 case
# buildBounds(c(1/4, 1/4, 1/4));
# sum > 1 case
# buildBounds(c(1/2, 1/2, 1/2));
On construit la fonction fromR01toPFC
qui, à partir d’un réel appartenant à [0;1] et d’une liste de vecteurs bounds
(créée avec buildBounds
), donne le résultat correspondant Pierre
, Feuille
ou Ciseaux
.
Exemple : bounds = [[0 ; 0.5], [0.5 ; 0.75], [0.75 ; 1]]
- x = 0.1 ⇒ fromR01toPFC(x, bounds)
= P
- x = 0.63 ⇒ fromR01toPFC(x, bounds)
= F
- x = 0.98 ⇒ fromR01toPFC(x, bounds)
= C
inInterval = function(x, interval) {
return(x >= interval[1] && x <= interval[2]);
}
fromR01toPFC = function(x, bounds){
if(inInterval(x, bounds[[1]])){
return("P");
}
else if (inInterval(x, bounds[[2]])){
return("F");
}
else if (inInterval(x, bounds[[3]])){
return("C");
}
else {
stop("Decision number not in bounds");
}
}
On construit la fonction buildPlaysVector
qui construit,à partir d’une liste de vecteurs bounds
construite avec buildBounds
et d’un entier size
donnés, le vecteur de taille size
correspondant à size
choix successifs de jeu.
Exemple buildPlaysVector(3, bounds)
= [F, P, C] => le joueur fera 3 parties et jouera successivement Feuille
, Pierre
puis Ciseaux
.
On peut se permettre d’avoir une fonction construisant à priori l’ensemble des choix successifs seulement parce que le choix se fait sans mémoire des choix précédents.
buildPlaysVector = function(size, bounds) {
runifVector = runif(size);
return(sapply(runifVector, fromR01toPFC, bounds = bounds));
}
# Test
# print(buildPlaysVector(10, buildBounds(c(1/3, 1/3, 1/3))));
On construit ensuite une fonction qui renvoie le résultat d’une partie :
L’entrée se fait sous la forme getWinner(A, B)
où A
et B
sont de la forme respectivement "P"
, "F"
ou "C"
si le joueur a joué respectivement Pierre, Feuille ou Ciseaux.
Le résultat est 0
si la partie est nulle, -1
si A
a gagné la partie et 1
si B
a gagné la partie.
getWinner = function(aChoice, bChoice) {
if(aChoice == "P"){
if(bChoice == "P"){return(0);}
else if(bChoice == "F"){return(1);}
else if(bChoice == "C"){return(-1);}
}
else if(aChoice == "F"){
if(bChoice == "P"){return(-1);}
else if(bChoice == "F"){return(0);}
else if(bChoice == "C"){return(1);}
}
else if(aChoice == "C"){
if(bChoice == "P"){return(1);}
else if(bChoice == "F"){return(-1);}
else if(bChoice == "C"){return(0);}
}
}
# Tie case
# getWinner("C", "C");
# A winning case
# getWinner("F", "P");
# B winning case
# getWinner("C", "P");
On construit la fonction buildResultVector(n, Pa, Pb)
qui à partir des vecteurs de probabilités Pa
et Pb
de deux joueurs, donne le vecteur indiquant les résultats de n
parties.
# used https://stackoverflow.com/questions/35352647/r-apply-on-a-function-with-two-variables to know how to use mapply in addition to its documentation
buildResultVector = function(size, Pa, Pb){
apv = buildPlaysVector(size, buildBounds(Pa));
bpv = buildPlaysVector(size, buildBounds(Pb));
return(mapply(getWinner, apv, bpv));
}
# Test
# Pa = c(1/4,1/4,1/2);
# Pb = c(1/4,1/4,1/2);
# cat("Result vector of size 10 given Pa and Pb : [", buildResultVector(10, Pa, Pb), "]");
On construit la fonction expectedValue(n, Pa, Pb)
qui renvoie l’espérance de gain de B jouant n
parties avec le vecteur de probabilité Pb
contre A de vecteur de probabilité Pb
.
expectedValue = function(size, Pa, Pb) {
return(sum(buildResultVector(size, Pa, Pb))/size);
}
# Test
# Pa = c(1/4,1/4,1/2);
# Pb = c(1/4,1/4,1/2);
# print(expectedValue(basicSize, Pa, Pb));
On construit la fonction allPlotsForXYin01(size, Pa)
qui affiche tous les graphes ayant pour ordonnée l’espérance de gain estimée sur size
parties opposant deux joueurs ayant pour vecteurs de probabilité Pa
et (x, y, 1 - x - y) et pour abscisse y, pour x fixé. Elle affiche également (x, y) tels que l’espérance correspondant soit maximale.
allPlotsForXYin01 = function(size, Pa, ymin = -1, ymax = 1){
expectedValueVector = c();
abscissa = c();
maxExpectedValue = -Inf;
maxExpectedValueX = -Inf;
maxExpectedValueY = -Inf;
for(x in 0.1 * (0:10)){
yindex = 1 ;
for(y in 0.1 * (0:10)){
# Assert that (x, y, 1 - x - y) is a valid probability vector
if(x + y <= 1){
abscissa[yindex] = y ;
expectedValueVector[yindex] = expectedValue(size, Pa, c(x, y, 1 - x - y));
# Save x and y s.t. E(X) is maximal
if(expectedValueVector[yindex] > maxExpectedValue){
maxExpectedValue = expectedValueVector[yindex] ;
maxExpectedValueY = y ;
maxExpectedValueX = x ;
}
}
else{
expectedValueVector[yindex] = -Inf ;
}
yindex = yindex + 1 ;
}
plot(x = abscissa, y = expectedValueVector, ylim = c(ymin, ymax), xlab = "y", ylab = "Espérance", main = paste("Esperance en fonction de y pour x = ", x, sep = ""));
}
cat(paste("L'espérance est maximale pour (x = ", maxExpectedValueX, ", y = ", maxExpectedValueY, ") et vaut : ", maxExpectedValue, sep = ""));
}
Pa = c(1/4,1/4,1/2);
Pb = c(1/4,1/4,1/2);
print(expectedValue(basicSize, Pa, Pb));
## [1] 0.00398
Pa = c(1/4,1/4,1/2);
Pb = c(1/3,1/3,1/3);
print(expectedValue(basicSize, Pa, Pb));
## [1] 0.00254
Pa = c(1/4, 1/4, 1/2);
allPlotsForXYin01(plotSize, Pa);
## L'espérance est maximale pour (x = 1, y = 0) et vaut : 0.245
On en déduit que la meilleure stratégie pour le joueur B lorsque \(P^{(A)} = (\frac{1}{4}, \frac{1}{4}, \frac{1}{2})\) est d’adopter le vecteur probabilité \(P^{(A)} = (1, 0, 0)\), soit de jouer systématiquement Pierre
.
Soient \(x_i\) avec \(i \in [\![1 ; 3]\!]\) tels que
\(x_1\) l’évènement “B gagne la partie”,
\(x_2\) l’évènement “la partie se solde par une égalité”,
\(x_3\) l’évènement “B perd la partie”.
Soit \(C\) = {\(Pierre, Feuille, Ciseau\)} l’ensemble des choix possibles et \(J\) = {\(A, B\)} l’ensemble des joueurs. Alors on note “\(c_j\), \((c \in C, j \in J)\) le joueur \(j\) joue le choix \(c\)”.
On a alors pour \(P^{(B)} = (\frac{1}{4}, \frac{1}{4}, \frac{1}{2})\) :
\(P(X = x_1) = Pierre_{B} \times Ciseau_{A} + Feuille_{B} \times Pierre_{A} + Ciseau_{B} \times Feuille_{A} = \frac{1}{4} \times \frac{1}{2} + \frac{1}{4} \times \frac{1}{4} + \frac{1}{2} \times \frac{1}{4} = 0.3125\)
De même,
\(P(X = x_2) = \frac{1}{4} \times \frac{1}{4} + \frac{1}{4} \times \frac{1}{4} + \frac{1}{2} \times \frac{1}{2} = 0.375\)
\(P(X = x_3) = \frac{1}{4} \times \frac{1}{4} + \frac{1}{4} \times \frac{1}{2} + \frac{1}{2} \times \frac{1}{4} = 0.3125\)
Comme on joue en version à somme nulle, on a \(x_{1} = 1\), \(x_{2} = 0\), \(x_{3} = -1\).
On rappelle la formule de l’espérance : \[\mathbb{E}(X) = \sum\limits_{i=1}^3 x_{i} \times P(X=x_{i})\]
On obtient \(\mathbb{E}(X) = \sum\limits_{i=1}^3 x_{i} \times P(X=x_{i}) = 1 \times 0.3125 + 0 \times 0.375 + (-1) \times 0.3125 = 0\).
De même pour \(P^{(B)} = (\frac{1}{3}, \frac{1}{3}, \frac{1}{3})\), on obtient \(\mathbb{E}(X) = 0\).
Pour la formule générale \(P^{(B)} = (x, y, 1-x-y)\), on obtient \(\mathbb{E}(X) = 1 \times (x \times \frac{1}{2} + y \times \frac{1}{4} + (1 - x - y) \times \frac{1}{4}) - 1 \times (x \times \frac{1}{4} + y \times \frac{1}{2} + (1 - x -y) \times \frac{1}{4}) = \frac{1}{4} \times x - \frac{1}{4} \times y\).
Afin de maximiser l’espérance \(\mathbb{E}(X)\), il convient de maximiser \(x\) et de minimiser \(y\).
La valeur extrême pouvant être prise par \((x, y)\) est \((1, 0)\), ce qui correspond à \(P^{(B)} = (1, 0, 0)\).
On peut déduire que pour maximiser ses victoires, le joueur B doit systématiquement jouer Pierre
(ce qui est assez intuitif car le joueur A joue la plupart du temps Ciseau
, qui est battu par Pierre
).
Pa = c(1/3,1/3,1/3);
Pb = c(1/4,1/4,1/2);
print(expectedValue(basicSize, Pa, Pb));
## [1] -0.0013
Pa = c(1/3,1/3,1/3);
Pb = c(1/3,1/3,1/3);
print(expectedValue(basicSize, Pa, Pb));
## [1] -0.0021
Pa = c(1/3, 1/3, 1/3);
allPlotsForXYin01(plotSize, Pa, ymin = -0.2, ymax = 0.2);
## L'espérance est maximale pour (x = 0.3, y = 0.5) et vaut : 0.0164
Il semble que le choix du vecteur de probabilité n’influe pas sur l’espérance de gain et que celle-ci se trouve systématiquement réduite à 0.
On obtient, en tenant un raisonnement analogue au cas où A était biaisé, pour \(P^{(B)} = (\frac{1}{4}, \frac{1}{4}, \frac{1}{2})\) :
\(P(X = x_1) = \frac{1}{4} \times \frac{1}{3} + \frac{1}{4} \times \frac{1}{3} + \frac{1}{2} \times \frac{1}{3} = \frac{1}{3}\)
\(P(X = x_2) = \frac{1}{4} \times \frac{1}{3} + \frac{1}{4} \times \frac{1}{3} + \frac{1}{2} \times \frac{1}{3} = \frac{1}{3}\)
\(P(X = x_3) = \frac{1}{4} \times \frac{1}{3} + \frac{1}{4} \times \frac{1}{3} + \frac{1}{2} \times \frac{1}{3} = \frac{1}{3}\)
Et ainsi, \(\mathbb{E}(X) = 1 \times \frac{1}{3} + 0 \times \frac{1}{3} + (-1) \times \frac{1}{3} = 0\)
De même, avec \(P^{(B)} = (\frac{1}{3}, \frac{1}{3}, \frac{1}{3})\), on obtient :
\(P(X = x_1) = \frac{1}{3} \times \frac{1}{3} + \frac{1}{3} \times \frac{1}{3} + \frac{1}{2} \times \frac{1}{3} = \frac{1}{3}\)
\(P(X = x_2) = \frac{1}{3} \times \frac{1}{3} + \frac{1}{3} \times \frac{1}{3} + \frac{1}{3} \times \frac{1}{3} = \frac{1}{3}\)
\(P(X = x_3) = \frac{1}{3} \times \frac{1}{3} + \frac{1}{3} \times \frac{1}{3} + \frac{1}{3} \times \frac{1}{3} = \frac{1}{3}\)
Et ainsi, \(\mathbb{E}(X) = 1 \times \frac{1}{3} + 0 \times \frac{1}{3} + (-1) \times \frac{1}{3} = 0\)
Pour la formule générale \(P^{(B)} = (x, y, 1-x-y)\), on obtient \(\mathbb{E}(X) = 1 \times (x \times \frac{1}{3} + y \times \frac{1}{3} + (1 - x - y) \times \frac{1}{3}) - 1 \times (x \times \frac{1}{3} + y \times \frac{1}{3} + (1 - x -y) \times \frac{1}{3}) = 0\).
Ainsi, on vérifie mathématiquement les hypothèses de la réponse à la question 4. : l’espérance de gain d’un joueur non biaisé est de 0 quel que soit son adversaire. Un joueur non biaisé ne peut donc espérer gagner ou perdre, à long terme celui-ci verra ses gains réduits à 0.
On construit d’abord quelques fonctions “outils” (assez triviales) utiles plus tard.
learningExpectedValue = function(resultVector){
return(sum(resultVector)/length(resultVector));
}
buildLearningResultVector = function(apv, bpv) {
return(mapply(getWinner, apv, bpv));
}
buildLearningCumulativeVector = function(resultVector){
cumulativeVector = c();
for(i in 1:length(resultVector)){
cumulativeVector[i] = learningExpectedValue(resultVector[1:i]);
}
return(cumulativeVector) ;
}
# gives one game choice given a probabilityVector
nextPlay = function(probabilityVector) {
buildPlaysVector(1, buildBounds(probabilityVector));
}
On fait la supposition qu’un humain est incapable d’équilibrer ses fréquences de jeu et donc qu’à long terme son vecteur de probabilité tendra vers \(P \neq (\frac{1}{3}, \frac{1}{3}, \frac{1}{3})\). Ainsi, il convient d’apprendre ce vecteur (en l’affinant au cours du temps) puis de jouer systématiquement le coup battant le coup le plus utilisé.
On construit la fonction buildSimpleLearningDynamicPlaysVector(opponentPlaysVector, currentIndex)
qui construit le vecteur de probabilité \(P = (1, 0, 0)\) ou \(P = (0, 1, 0)\) ou \(P = (0, 0, 1)\) dont le choix à \(1\) est celui battant le coup de plus grande fréquence dans les currentIndex - 1
premiers jeux de opponentPlaysVector
.
buildSimpleLearningDynamicPlaysVector = function(opponentPlaysVector, currentIndex){
freqP = sum(opponentPlaysVector[1:(currentIndex - 1)] == "P") / (currentIndex - 1) ;
freqF = sum(opponentPlaysVector[1:(currentIndex - 1)] == "F") / (currentIndex - 1) ;
freqC = sum(opponentPlaysVector[1:(currentIndex - 1)] == "C") / (currentIndex - 1) ;
if(freqP >= freqF && freqP >= freqC){
# P is most used --> use F
resultVector = c(0, 1, 0) ;
}
else{
if(freqF >= freqC && freqF >= freqP){
# F is most used --> use C
resultVector = c(0, 0, 1) ;
}
else{
# C is most used --> use P
resultVector = c(1, 0, 0) ;
}
}
return(resultVector);
}
On construit la fonction buildSimpleLearningPlaysVector(learningGameSize, opponentPlaysVector, startingProbabilityVector, firstRelevantIndex)
qui construit le vecteur de jeu avec apprentissage du jeu de opponentPlaysVector
, de taille learningGameSize
. Les firstRelevantIndex
premiers coups sont choisis grâce au vecteur de probabilité startingProbabilityVector
, les suivants selon la méthode décrite précédemment.
buildSimpleLearningPlaysVector = function(learningGameSize, opponentPlaysVector, startingProbabilityVector, firstRelevantIndex){
playsVector = c() ;
P = startingProbabilityVector ;
playsVector[1] = nextPlay(P);
for(i in 2:learningGameSize){
P = buildSimpleLearningDynamicPlaysVector(opponentPlaysVector, i)
if(i >= firstRelevantIndex){
playsVector[i] = nextPlay(P);
}
else{
playsVector[i] = nextPlay(startingProbabilityVector);
}
}
return(playsVector);
}
On construit la fonction simpleLearningGame(learningGameSize, probabilityVector)
construisant le vecteur de résultat d’une partie de taille learningGameSize
entre un joueur de vecteur de probabilité probabilityVector
et une machine apprenante décrite précédemment.
simpleLearningGame = function(learningGameSize, probabilityVector){
startingProbabilityVector = c(1/3, 1/3, 1/3) ;
firstRelevantIndex = 10 ; # Number of learnings before use of learnt playsVector
opponentPlaysVector = buildPlaysVector(learningGameSize, bounds = buildBounds(probabilityVector)) ;
playsVector = buildSimpleLearningPlaysVector(learningGameSize, opponentPlaysVector, startingProbabilityVector, firstRelevantIndex) ;
return(buildLearningResultVector(opponentPlaysVector, playsVector));
}
Enfin, on teste cette machine en faisant learningGameSize
parties contre un joueur de vecteur de probabilité probabilityVector
. On affiche son espérance sur l’ensemble des parties puis l’évolution de cette espérance au cours des parties.
learningGameSize = 2000 ;
probabilityVector = c(1/4, 1/4, 1/2) ;
resultVector = simpleLearningGame(learningGameSize, probabilityVector);
lev = learningExpectedValue(resultVector);
print(lev);
## [1] 0.274
learningCumulativeVector = buildLearningCumulativeVector(resultVector);
plot(learningCumulativeVector, ylim = c(-1, 1), type = "l");
La machine apprenante semble obtenir de bons résultats. Cependant, un humain assez perspicace pourrait aisément déduire au bout de quelques parties le schéma de jeu de celle-ci.
Ainsi, il convient d’utiliser un algorithme qui ne s’adapte pas optimalement de manière déterministe mais conserve une partie de hasard dans son comportement.
On propose le schéma suivant.
On fait la supposition qu’un humain est difficilement capable d’équilibrer les fréquences de jeu à long terme mais qu’il tentera de le faire sur quelques coups successifs. Ainsi, de courtes portion de son historique devraient être équilibrées.
Le principe de l’algorithme est donc d’avoir une fenêtre glissantes de quelques parties sur l’historique du joueur, d’établir grâce à celle-ci son vecteur de probabilité puis d’utiliser celui-ci comme notre vecteur de probabilité.
En effet, pour équilibrer ses choix, le joueur jouera à l’inverse de ses choix précédents donc il inversera son vecteur de probabilité. Or, un vecteur menant à la victoire contre celui-ci son inverse. On suppose qu’avant la phase d’apprentissage, on joue avec un vecteur de probabilité \(P = (\frac{1}{3}, \frac{1}{3}, \frac{1}{3})\).
On construit la fonction buildDynamicProbabilityVector(playsVector, currentIndex, windowSize)
qui construit le vecteur de probabilité à utiliser (déterminé selon la méthode évoquée précédemment) d’après les currentIndex - 1
premiers jeux de playsVector
et selon une taille de fenêtre d’apprentissage de taille windowSize
.
buildDynamicProbabilityVector = function(playsVector, currentIndex, windowSize) {
freqP = 0 ;
freqF = 0 ;
for(j in (currentIndex - windowSize):(currentIndex - 1)){
if(playsVector[j] == "P"){
freqP = freqP + 1 ;
}
else{
if(playsVector[j] == "F"){
freqF = freqF + 1 ;
}
}
}
freqP = freqP / windowSize ;
freqF = freqF / windowSize ;
return(c(freqP, freqF, 1 - (freqP + freqF)));
}
On construit la fonction buildLearningPlaysVector(learningGameSize, learningWindowSize, opponentPlaysVector, probabilityVector)
qui construit le vecteur de jeu de la nouvelle machine apprenante de taille learningGameSize
d’après le vecteur de jeu opponentPlaysVector
de l’adversaire, une taille de fenêtre d’apprentissage learningWindowSize
et un vecteur de probabilité initial probabilityVector
.
buildLearningPlaysVector = function(learningGameSize, learningWindowSize, opponentPlaysVector, probabilityVector) {
playsVector = c() ;
playsVector[1] = nextPlay(probabilityVector) ;
for(i in 2:learningGameSize){
if(i > learningWindowSize){
# probabilityVector becomes the vector used in the `learningWindowSize` last plays
probabilityVector = buildDynamicProbabilityVector(opponentPlaysVector, i, learningWindowSize);
}
playsVector[i] = nextPlay(probabilityVector) ;
}
return(playsVector)
}
On construit la fonction learningGame(learningGameSize, learningWindowSize, probabilityVector)
construisant le vecteur de résultat d’une partie de taille learningGameSize
entre un joueur de vecteur de probabilité probabilityVector
et une machine apprenante décrite précédemment, de taille de fenêtre d’apprentissage learningWindowSize
.
learningGame = function(learningGameSize, learningWindowSize, probabilityVector){
startingProbabilityVector = c(1/3, 1/3, 1/3);
opponentPlaysVector = buildPlaysVector(learningGameSize, bounds = buildBounds(probabilityVector)) ;
playsVector = buildLearningPlaysVector(learningGameSize, learningWindowSize, opponentPlaysVector, startingProbabilityVector) ;
return(buildLearningResultVector(opponentPlaysVector, playsVector));
}
Enfin, on teste cette machine en faisant learningGameSize
parties contre un joueur de vecteur de probabilité probabilityVector
, avec une fenêtre d’apprentissage learningWindowSize
. On affiche son espérance sur l’ensemble des parties puis l’évolution de cette espérance au cours des parties.
learningGameSize = 2000 ;
learningWindowSize = 10 ;
probabilityVector = c(1/4, 1/4, 1/2) ;
learningResultVector = learningGame(learningGameSize, learningWindowSize, probabilityVector);
lev = learningExpectedValue(learningResultVector);
print(lev);
## [1] -0.006
learningCumulativeVector = buildLearningCumulativeVector(learningResultVector);
plot(learningCumulativeVector, ylim = c(-0.3, 0.3), type = "l");
On observe que cette machine obtient de moins bons résultats.
Cependant, ce résultat s’explique aisément par la nature de cette machine et de ce test : pour élaborer celle-ci, des suppositions sur la nature humaine ont été formulées et le test est conduit sur une machine de vecteur de probabilité statique et non sur un humain.
En se basant sur la publication Social cycling and conditional responses in the Rock-Paper-Scissors game de Zhijian Wang, Bin Xu et Hai-Jun Zhou, illustrée dans la vidéo Winning at Rock Paper Scissors de la chaine YouTube Numberphile, on établit la stratégie non aléatoire suivante :
Si l’on pert une partie, le coup suivant sera celui n’ayant pas été joué durant la partie précédente,
Si l’on gagne une partie, le coup suivant sera celui de l’adversaire durant la partie précédente,
Si une partie se solde par un match nul, le coup suivant sera choisi aléatoirement avec un vecteur de probabilité \(P = (\frac{1}{3}, \frac{1}{3}, \frac{1}{3})\).
Il s’agit d’une version simplifiée de la première machine apprenante, on reprend donc le code de cette dernière, en dehors de la fonction buildSimpleLearningDynamicPlaysVector
.
notPlayed = function(a, b) {
if(a == "P"){
if(b == "F"){
# (P, F) --> C
result = c(0, 0, 1);
}
else{
# (P, C) --> F
result = c(0, 1, 0);
}
}else{
if(a == "F"){
if(b == "P"){
# (F, P) --> C
result = c(0, 0, 1);
}
else{
# (F, C) --> P
result = c(1, 0, 0);
}
}
else{
if(b == "P"){
# (C, P) --> F
result = c(0, 1, 0);
}
else{
# (C, F) --> P
result = c(1, 0, 0);
}
}
}
return(result);
}
played = function(c) {
if(c == "P"){
result = c(1, 0, 0);
}
else{
if(c == "F"){
result = c(0, 1, 0);
}
else{
result = c(0, 0, 1);
}
}
}
buildDeterministicLearningDynamicPlaysVector = function(opponentPlaysVector, playsVector, currentIndex){
lastPlay = playsVector[currentIndex - 1] ;
lastOpponentPlay = opponentPlaysVector[currentIndex - 1] ;
result = getWinner(lastPlay, lastOpponentPlay) ;
# Tie case
if(result == 0){
resultVector = c(1/3, 1/3, 1/3) ;
}
else {
# Lose case
if(result == 1){
resultVector = notPlayed(lastPlay, lastOpponentPlay);
}
# Win case
else{
resultVector = played(lastOpponentPlay) ;
}
}
return(resultVector);
}
buildDeterministicLearningPlaysVector = function(learningGameSize, opponentPlaysVector){
playsVector = c() ;
P = c() ;
playsVector[1] = nextPlay(c(1/3, 1/3, 1/3));
for(i in 2:learningGameSize){
P = buildDeterministicLearningDynamicPlaysVector(opponentPlaysVector, playsVector, i)
playsVector[i] = nextPlay(P);
}
return(playsVector);
}
deterministicLearningGame = function(learningGameSize, probabilityVector){
startingProbabilityVector = c(1/3, 1/3, 1/3) ;
firstRelevantIndex = 10 ; # Number of learnings before use of learnt playsVector
opponentPlaysVector = buildPlaysVector(learningGameSize, bounds = buildBounds(probabilityVector)) ;
playsVector = buildDeterministicLearningPlaysVector(learningGameSize, opponentPlaysVector) ;
return(buildLearningResultVector(opponentPlaysVector, playsVector));
}
learningGameSize = 2000 ;
probabilityVector = c(1/4, 1/4, 1/2) ;
resultVector = deterministicLearningGame(learningGameSize, probabilityVector);
lev = learningExpectedValue(resultVector);
print(lev);
## [1] 0.0025
learningCumulativeVector = buildLearningCumulativeVector(resultVector);
plot(learningCumulativeVector, ylim = c(-1, 1), type = "l");
On remarque qu’il suffit d’adapter les fonctions buildBounds
, fromR01toPFC
(rebaptisée fromR01toPFCLS
) et getWinner
pour obtenir la version Pierre-Feuille-Ciseaux-Lézard-Spock :
buildBounds = function(probabilityVector){
if (sum(probabilityVector) != 1){
stop("Make sure that ProbabilityVector is really a probability vector (ie sum = 1)");
}
bounds = list();
bounds[[1]] = c(0, probabilityVector[1]);
bounds[[2]] = c(probabilityVector[1], probabilityVector[1] + probabilityVector[2]);
bounds[[3]] = c(probabilityVector[1] + probabilityVector[2], probabilityVector[1] + probabilityVector[2] + probabilityVector[3]);
bounds[[4]] = c(probabilityVector[1] + probabilityVector[2] + probabilityVector[3], probabilityVector[1] + probabilityVector[2] + probabilityVector[3] + probabilityVector[4]);
bounds[[5]] = c(probabilityVector[1] + probabilityVector[2] + probabilityVector[3] + probabilityVector[4], 1);
return(bounds)
}
inInterval = function(x, interval) {
return(x >= interval[1] && x <= interval[2]);
}
fromR01toPFCLS = function(x, bounds){
if(inInterval(x, bounds[[1]])){
return("P");
}
else if (inInterval(x, bounds[[2]])){
return("F");
}
else if (inInterval(x, bounds[[3]])){
return("C");
}
else if (inInterval(x, bounds[[4]])){
return("L");
}
else if (inInterval(x, bounds[[5]])){
return("S");
}
else {
stop("Decision number not in bounds");
}
}
buildPlaysVector = function(size, bounds) {
runifVector = runif(size);
return(sapply(runifVector, fromR01toPFCLS, bounds = bounds));
}
getWinner = function(aChoice, bChoice) {
if(aChoice == "P"){
if(bChoice == "P"){return(0);}
else if(bChoice == "F" || bChoice == "S"){return(1);}
else if(bChoice == "C" || bChoice == "L"){return(-1);}
}
else if(aChoice == "F"){
if(bChoice == "P" || bChoice == "S"){return(-1);}
else if(bChoice == "F"){return(0);}
else if(bChoice == "C" || bChoice == "L"){return(1);}
}
else if(aChoice == "C"){
if(bChoice == "P" || bChoice == "S"){return(1);}
else if(bChoice == "F" || bChoice == "L"){return(-1);}
else if(bChoice == "C"){return(0);}
}
else if(aChoice == "L"){
if(bChoice == "P" || bChoice == "C"){return(1);}
else if(bChoice == "F" || bChoice == "S"){return(-1);}
else if(bChoice == "L"){return(0);}
}
else if(aChoice == "S"){
if(bChoice == "F" || bChoice == "L"){return(1);}
else if(bChoice == "P" || bChoice == "C"){return(-1);}
else if(bChoice == "S"){return(0);}
}
}