La belle-mère et la bataille

Ce soir, les enfants voulaient lancer une partie de bataille dix minutes avant de souper. Devant mon peu d’enthousiasme (on ne sait jamais trop quand ce genre de parties finissent), ma belle-mère a suggéré qu’au lieu de jouer à deux (comme le voulaient les deux grands), on devrait jouer à quatre, et comme ça, ça irait plus vite.

Et si ma belle-mère avait raison ? et si elle avait tort ? Car je veux bien croire que le temps moyen d’une partie (une partie se finissant quand il y a un vainqueur, c’est à dire quelqu’un qui a collecté toutes les cartes du jeu), voire le temps médian d’une partie, soit fonction du nombre de joueurs. A priori j’aurais toutefois tendance à croire qu’au contraire, ça va rallonger. En effet, intuitivement, si on joue à trois, au bout d’un moment, un joueur aura perdu toutes ses cartes, et – en moyenne – le paquet est alors partagé entre les deux joueurs qui restent. Et on revient alors à une partie avec deux joueurs. Donc plus y a de joueurs, plus ça devrait durer. Maintenant, on peut aussi noter qu’avec 30 cartes, et 10 joueurs, il est possible que le même joueur gagne 3 fois de suites (de l’ordre d’une chance sur 1000 avec un jeu simplifié, certes), au tout début, et donc en 3 coups, la partie est finie. Ce qui ne peut pas arriver avec 5 joueurs par exemple. Donc avec beaucoup de joueurs, il serait possible de finir une partie beaucoup plus vite. Damned… c’est pénible les belles-mères !

N’ayant pas le courage de faire des maths (il restait dix minutes avant le souper), je me suis contenter de faire un petit programme pour faire des simulations. Bon, j’avoue, je considère une version très simplifiée de la bataille. On a  joueurs, et  cartes, avec  multiple de . On distribue toutes les cartes, soit  cartes par joueur, au premier tour. Je vais alors supposer qu’il n’y a pas de bataille. C’est un peu nul, j’en conviens (puisque c’est le nom du jeu). Autrement dit, on tire au hasard un vainqueur parmi les joueurs. Et de manière équiprobable, c’est à dire qu’un joueur avec beaucoup de cartes n’a pas – a priori – de meilleures cartes que les autres. Le vainqueur prend alors les  cartes, et -1 joueurs (les perdants) perdent une carte.

Une partie se simule alors de la manière suivante,

Time=function(np=2,nc=36){
t=0
N=rep(nc/np,np)
VN=N
P=1:np
while(sum(N==0)<(np-1)){
i=sample(P,1)
N=pmax(N-1,0)
N[i]=N[i]+length(P)
P=which(N!=0)
t=t+1
VN=rbind(VN,N)
}
return(list(time=t,traj=VN))
}

Par exemple, avec deux joueurs, on a l’évolution suivante du nombre de cartes par joueur (sur un jeu contenant =60 cartes),

set.seed(1)
T=Time()
barplot(t(T$traj),col=c("blue","red"),border=NA)

et avec trois joueurs (et toujours nos 60 cartes)

T=Time(np=3,nc=36)
barplot(t(T$traj),col=c("blue","red","green"),border=NA)

 Ici, il semble que la partie avec trois joueurs ait duré beaucoup plus longtemps que celle avec deux. Et que lorsque le troisième joueur a perdu toutes ses cartes, les deux joueurs avaient autant de cartes l’un que l’autre (et on était alors revenu à la position initiale). Mais est-ce toujours le cas ? Faisons quelques simulations pour se faire une idée plus précise… On peut utiliser

game=function(npsim=2,ns=5000,ncsim=60){
T=rep(NA,ns)
for(i in 1:ns){T[i]=Time(np=npsim,nc=ncsim)$time}
return(T)
}

Et on peut regarder des simulations avec 2, 3, 4, 5, etc joueurs,

G1=game(np=2,nc=60)
G2=game(np=3,nc=60)
G3=game(np=4,nc=60)
G4=game(np=5,nc=60)
G5=game(np=6,nc=60)
G6=game(np=10,nc=60)
G7=game(np=12,nc=60)
G8=game(np=15,nc=60)
G9=game(np=20,nc=60)
G10=game(mp=30,nc=60)
G=data.frame(G1,G2,G3,G4,G5,G6,G7,G8,G9,G10)
boxplot(G,names=c(2,3,4,5,6,10,12,15,20,30))

Si on regarde le dessin brut, on a

autrement dit, il semble que la distribution du temps d’une partie soit indépendante du nombre de joueurs (ici en abscisse). On peut regarder les temps moyens

> trunc(apply(G,2,mean))
 G1  G2  G3  G4  G5  G6  G7  G8  G9 G10 
896 925 929 922 919 918 913 908 909 873

avec éventuellement un intervalle de confiance à 95% sur le temps moyen d’une partie, avec 60 cartes

> rbind(trunc(apply(G,2,mean)-2/sqrt(5000)*apply(G,2,sd)),
+ trunc(apply(G,2,mean)),
+ trunc(apply(G,2,mean)+2/sqrt(5000)*apply(G,2,sd)))
      G1  G2  G3  G4  G5  G6  G7  G8  G9 G10
[1,] 875 904 908 901 898 897 893 887 888 852
[2,] 896 925 929 922 919 918 913 908 909 873
[3,] 917 946 950 943 940 939 934 929 930 894

ou encore les quartiles

> trunc(apply(G,2,quantile))
       G1   G2   G3   G4   G5   G6   G7   G8   G9  G10
0%     46   48   50   42   38   33   36    4    3    2
25%   379  403  410  409  407  402  393  391  388  358
50%   681  706  720  710  701  701  706  692  709  660
75%  1192 1233 1249 1209 1203 1221 1229 1194 1195 1178
100% 6746 6927 5656 8135 7392 6377 7926 8542 7528 7062

Histoire de se conforter, on notera que les dernières valeurs minimales ont du sens, compte tenu de notre remarque initiale. Visuellement on obtient

avec en rouge la valeur moyenne (et l’intervalle de confiance autour), toujours en fonction du nombre de joueurs (et toujours le même nombre de cartes). Avec 5 fois plus de simulations, on a la même histoire, avec le temps moyen suivant

> rbind(trunc(apply(G,2,mean)-2/sqrt(25000)*apply(G,2,sd)),
+ trunc(apply(G,2,mean)),
+ trunc(apply(G,2,mean)+2/sqrt(25000)*apply(G,2,sd)))
      G1  G2  G3  G4  G5  G6  G7  G8  G9 G10
[1,] 893 909 917 912 911 912 902 899 895 860
[2,] 902 919 926 922 920 922 911 908 904 869
[3,] 912 928 936 931 930 931 920 918 913 878

Personnellement, malgré la très légère concavité que l’on peut observer sur le graphique ci-dessus (à peine significatif) j’ai l’impression que l’on a toujours la même distribution, quel que soit le nombre de joueurs. Étonnant, non ? J’attends que quelqu’un m’explique que ce résultat est intuitif, et pourrait se trouver simplement, en faisant des maths (je ne dis pas que le temps moyen est le même, je dis que la distribution de la durée d’une partie est indépendante du nombre de joueurs, ce qui me semble plus compliqué à établir). En attendant, je vais essayer d’améliorer mon modèle pour modéliser les batailles. Ou expliquer à mes enfants que leur père est un nerd, car il préfère taper des codes sur son ordinateur plutôt que de jouer à la bataille… Honte à moi !



Cite this blog post
Arthur Charpentier (2013, May 4). La belle-mère et la bataille. Freakonometrics. Retrieved June 24, 2024, from https://doi.org/10.58079/ouqb

2 thoughts on “La belle-mère et la bataille”

  1. Bonjour Arthur,
    Merci pour ce poste (très intéressant comme toujours).
    Je me suis amusé à développer un petit jeu qui code vraiment la bataille (le fait d’avoir des bonnes ou mauvaises cartes en main + prend en compte les batailles).

    Les conclusions sont légèrement différentes de celles que tu proposes :

    – la durée moyenne d’une partie dépend très légèrement du nombre de joueurs présents, mais (surtout) très fortement des cartes non distribuées (tous les joueurs ayant le même nombre de cartes) :
    [1] “nb joueurs : 2 duree moyenne : 439 cartes non distribuees : 0”
    [1] “nb joueurs : 3 duree moyenne : 419 cartes non distribuees : 1”
    [1] “nb joueurs : 4 duree moyenne : 430 cartes non distribuees : 0”
    [1] “nb joueurs : 5 duree moyenne : 383 cartes non distribuees : 2”
    [1] “nb joueurs : 6 duree moyenne : 354 cartes non distribuees : 4”
    [1] “nb joueurs : 7 duree moyenne : 370 cartes non distribuees : 3”
    [1] “nb joueurs : 8 duree moyenne : 348 cartes non distribuees : 4”
    [1] “nb joueurs : 9 duree moyenne : 309 cartes non distribuees : 7”
    [1] “nb joueurs : 10 duree moyenne : 371 cartes non distribuees : 2”
    [1] “nb joueurs : 11 duree moyenne : 283 cartes non distribuees : 8”
    [1] “nb joueurs : 12 duree moyenne : 341 cartes non distribuees : 4”
    [1] “nb joueurs : 13 duree moyenne : 395 cartes non distribuees : 0”
    On note que quand le même nombre de cartes est distribué (par exemple toutes les cartes pour 2, 4 et 13 joueurs), la durée moyenne diminue en fonction du nombre de joueurs présents…

    – idem pour les parties les plus longues (les données sont plus bruitées, donc autant la relation avec la taille du jeu est très nette, autant la décroissance en fonction du nombre de joueurs l’est beaucoup moins).

    – enfin, pour les parties les plus courtes, on retrouve bien une dépendance au nombre de joueurs.
    [1] “nb joueurs : 2 1er decile : 123; 1er centile : 59 cartes non distribuees : 0”
    [1] “nb joueurs : 3 1er decile : 120; 1er centile : 56 cartes non distribuees : 1”
    [1] “nb joueurs : 4 1er decile : 125; 1er centile : 56 cartes non distribuees : 0”
    [1] “nb joueurs : 5 1er decile : 105; 1er centile : 48 cartes non distribuees : 2”
    [1] “nb joueurs : 6 1er decile : 96; 1er centile : 42 cartes non distribuees : 4”
    [1] “nb joueurs : 7 1er decile : 97; 1er centile : 42 cartes non distribuees : 3”
    [1] “nb joueurs : 8 1er decile : 90; 1er centile : 37 cartes non distribuees : 4”
    [1] “nb joueurs : 9 1er decile : 77; 1er centile : 32 cartes non distribuees : 7”
    [1] “nb joueurs : 10 1er decile : 96; 1er centile : 38 cartes non distribuees : 2”
    [1] “nb joueurs : 11 1er decile : 68; 1er centile : 25 cartes non distribuees : 8”
    [1] “nb joueurs : 12 1er decile : 83; 1er centile : 31 cartes non distribuees : 4”
    [1] “nb joueurs : 13 1er decile : 96; 1er centile : 35 cartes non distribuees : 0”

    – dernier point (pas forcement évident ) : le fait d’avoir modélisé des vrais parties des batailles rend la partie plus rapide (moyenne à 440 coups vs. 900 dans le programme que tu proposes). Ce n’est pas évident du tout (pour prendre un as dans le vrai jeu il faut attendre qu’il passe dans une bataille… ce qui peut être long).

    Je ne vois pas comment joindre des fichiers à mon commentaire ; j’ajoute donc le code dans le corps du texte (c’est lourd !) :

    init.df <- function(header, nrows){
    df<-data.frame(matrix(rep(NA,length(header) * nrows),nrows, length(header)))
    colnames(df)<-header
    return(df)
    }

    empty.df<- function(header){
    df<-data.frame(matrix(matrix(rep(1,length(header)),1),1))
    colnames(df)<-header
    return(df[NULL,])
    }

    nbCartes <- 13
    nbCouleurs <- 4

    donneJeu <- function(nbJoueurs){
    jeu <- rep(1:nbCartes, nbCouleurs)

    cartes <- list()

    for(joueurI in 1:nbJoueurs){
    cartes[[joueurI]] <- sample(jeu, length(jeu) / nbJoueurs)
    }
    return(cartes)
    }

    poseCartes <- function(mains, cartesPosees){
    dernieresCartesPosees <- c()
    for(joueurI in 1:length(mains)){
    if(length(mains[[joueurI]]) == 0){
    dernieresCartesPosees <- c(dernieresCartesPosees, 0)
    } else {
    dernieresCartesPosees 1){
    mains[[joueurI]] <- mains[[joueurI]][2:length(mains[[joueurI]])]
    } else{
    mains[[joueurI]] <- integer(0)
    }
    }
    cartesPosees <- c(cartesPosees, dernieresCartesPosees)
    cartesPosees <- cartesPosees[cartesPosees != 0]
    if(max(dernieresCartesPosees) == 0)
    dernieresCartesPosees[1] <- 1
    cartesPosees <- cartesPosees[sample.int(length(cartesPosees))]

    return(list( mains = mains,
    cartesPosees = cartesPosees,
    dernieresCartesPosees = dernieresCartesPosees))
    }

    joueCartes <- function(mains, cartesPosees){

    resultatPose <- poseCartes(mains, cartesPosees)
    mains = resultatPose[['mains']]
    cartesPosees = resultatPose[['cartesPosees']]
    dernieresCartesPosees = resultatPose[['dernieresCartesPosees']]

    joueursRestants 1){
    resultatPose <- poseCartes(mains[joueursRestants], cartesPosees)
    mains[joueursRestants] = resultatPose[['mains']]
    cartesPosees = resultatPose[['cartesPosees']]

    mains[joueursRestants] <- joueCartes(mains[joueursRestants], cartesPosees)
    } else {
    mains[[joueursRestants]] <- c(mains[[joueursRestants]], cartesPosees)
    }

    return(mains)
    }

    nbTests = 10000
    nbJoueurs = 2:13
    resultat <- init.df(paste(nbJoueurs, "_joueurs", sep=''), nbTests)

    for(nbJoueursI in nbJoueurs){
    resultatI <- c()

    for(i in 1:nbTests){
    print(paste(nbJoueursI, " joueurs ; partie ", i, sep=''))
    tailleJeux <- empty.df(c(paste("joueur_", 1:nbJoueursI)))
    cartes <- donneJeu(nbJoueursI)
    cartesIni <- cartes
    for(tour in 1:20000){
    tailleJeuxI <- sapply(cartes, length)
    tailleJeux <- rbind(tailleJeux, tailleJeuxI)

    cartes 0) == 1)
    break
    }
    resultatI <- c(resultatI, nrow(tailleJeux))
    }
    resultat[[paste(nbJoueursI, "_joueurs", sep='')]] <- resultatI
    }
    boxplot(resultat, names = paste(nbJoueurs, "_joueurs", sep=''))

    barplot(t(as.matrix(tailleJeux)),col=c("blue","red", "green"),border=NA)

    for(joueurI in 2:13){
    # print(paste("nb joueurs : ", joueurI, " duree moyenne : ", round(mean(resultat[[paste(joueurI, "_joueurs", sep='')]])), " cartes non distribuees : ", (52 %% joueurI), sep=""))
    print(paste("nb joueurs : ", joueurI, " 1er decile : ", resultat[[paste(joueurI, "_joueurs", sep='')]][order(resultat[[paste(joueurI, "_joueurs", sep='')]])][1000], "; 1er centile : ", resultat[[paste(joueurI, "_joueurs", sep='')]][order(resultat[[paste(joueurI, "_joueurs", sep='')]])][100], " cartes non distribuees : ", (52 %% joueurI), sep=""))
    }

    plot(52 %% 2:13, mean(resultat), col= "white")
    text(52 %% 2:13, mean(resultat), 2:13)

    1. Merci Guillaume ! C’est génial ton truc !

      Ma première remarque quand j’ai mis le billet en ligne était que ce résultat devait être connu, voire trivial, et que j’enfonçai des portes ouvertes… Ce n’est peut être pas le cas ! Et je vais creuser ce que tu évoques quand tu dis que dans un vrai jeu, les parties sont – en moyenne (si j’ai bien compris) – plus courtes qu’avec mon jeu !

      Sinon je continue en ce moment (j’ai des gros codes qui tournent sur un petit serveur) toujours sur des histoires de jeux de cartes, mais très différent (je crois) de ce que j’évoquais dans cette histoire de jeu de bataille ! A suivre !

      Merci en tous les cas pour le code !

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.