Le Mastermind est un jeu de société pour deux joueurs : un des joueurs (le codemaker) invente une combinaison secrète composée de 4 pions de couleurs et son adversaire (le codebreaker) doit la trouver par déductions successives, i.e. déterminer la couleur et la position exacte de chaque pion du code en 12 coups maximum.
Il s'agit d'un jeu de réflexion et de déduction, inventé par Mordecai Meirowitz, expert en télécommunications israëlien, dans les années 1970. Le jeu se base sur un jeu plus ancien (bulls and cows) qui se jouait avec papier et crayon et des nombres au lieu de couleurs.
A l'origine, le codemaker disposait de 6 couleurs différentes (rouge, bleu, noir, jaune, vert, blanc et bleu) pour composer une combinaison de 4 pions. Cependant, des variantes au jeu existent permettant au codemaker d'utiliser jusqu'à 12 couleurs différentes pour composer des combinaisons de 8 pions.
Le codemaker peut utiliser plusieurs fois la même couleur dans sa combinaison secrète, voire laisser certains emplacements vides (sans pion), ce qui permet de rendre le décodage plus compliqué. Le codebreaker propose à chaque tour une combinaison de pions, qu'il pense être la solution. Le codemaker est alors chargé de noter la combinaison proposée en fonction de la combinaison secrète de la façon suivante :
On suppose que la combinaison secrète est **RR****V****B**.
import random
import sys
import itertools
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.patches
#on définit nos variables : elles peuvent changer en fonction de la difficulté de jeu choisie par les joueurs.
LENGTH = 4
COLORS = ['R', 'V', 'B', 'J', 'N', 'M']#on définit ici les couleurs disponibles pour jouer.
On crée une fonction choices
permettant de renvoyer une combinaison de façon aléatoire à partir du choix de couleurs disponibles et du nombre de pions autorisés. Cette fonction sera utilisée par le codemaker.
def choices(e, n):
#Renvoie une liste composée de n éléments tirés de e avec remise.
return [random.choice(e) for i in range(n)]
tt = choices(COLORS,LENGTH)
print(tt)
['N', 'B', 'N', 'R']
On se place du côté du codemaker et on crée une fonction evaluation
permettant de noter la combinaison proposée par le codebreaker. La fonction prend en argument l'essai du codebreaker et la combinaison secrète à trouver.
def evaluation(attempt, solution): #Les arguments doivent être des chaînes de caractères
if len(solution) != len(attempt):
sys.exit("Erreur : les deux combinaisons n'ont pas la même longueur")
#on initialise les compteurs
red = 0 #comptera les plots bien placés et de bonne couleur dans l'essai
white = 0 #comptera les plots mal placés mais de bonne couleur dans l'essai
for i in range(len(solution)):
if solution[i] == attempt[i]: #on compte les plots bien placés
red += 1
for i in attempt:
if i in solution:
white += 1 #on compte tous les plots présents dans l'essai, bien ou mal placés par rapport à la solution
solution = solution.replace(i, '', 1) #on remplace une des couleurs dans la solution par des guillemets
#ainsi on évite que 2 plots de même couleur comptent pour 2 plots présents si cette couleur n'est présente
#qu'une fois dans solution
white = white - red
return red, white
ee = evaluation('RBBV','RRBV')
ee
(3, 0)
On crée ensuite une fonction donner_possibles
qui prend en argument l'essai du codebreaker (sous forme d'une chaine de caractères) et l'évaluation de l'essai et affiche un set contenant toutes les combinaisons compatibles avec l'évaluation donnée par le codemaker. Si on note $S$ la solution et $P$ l'essai, on cherche en fait toutes les combinaisons $Q$ différentes de $P$ telles que $evaluation(P,Q) = evaluation(P,S)$.
def donner_possibles(attempt, evaluation_p):
possible = set()
for i in set(itertools.product(COLORS, repeat=LENGTH)):
if evaluation(attempt, ''.join(i)) == evaluation_p: #on cherche dans le set toutes les combinaisons de couleurs
#possibles telles que leur évaluation corresponde à l'évaluation du codemaker
possible.add(i) #on ajoute cette combinaison à l'ensemble des possibles
return possible
On crée une fonction maj_possibles
qui prend en argument le set des combinaisons possibles, l'essai sous forme d'une chaine de caractères, puis l'évaluation de l'essai. Cette fonction va modifier le set des possibles entré en argument en enlevant toutes les combinaisons incompatibles avec l'évaluation donnée. Nous en aurons besoin dans les programmes suivants.
def maj_possibles(possible, attempt, evaluation_p):
impossible = set()
for i in possible:
if evaluation(attempt, ''.join(i)) != evaluation_p: #si l'évaluation de l'essai avec une combinaison du set
#des possibles ne correspond pas à l'évaluation de l'essai donnée par le codemaker
impossible.add(i) #on ajoute cette combinaison de couleurs à l'ensemble des combinaisons impossibles
for i in impossible:
possible.remove(i) #on enlève de l'ensemble des possibles, l'ensemble des combinaisons impossibles
On crée une première version du jeu simplifiée :
def codemaker1_init():
global solution
solution = ''.join(choices(COLORS, LENGTH)) #on choisit une combinaison de couleurs aléatoire qui sera la solution
#print(solution)
def codemaker1(attempt):
global solution
red, white = evaluation(attempt, solution) #on évalue la combinaison proposée par le joueur adverse.
return(red, white)
def codebreaker1_init():
global possible
possible = set(itertools.product(COLORS, repeat=LENGTH))
#on fait le produit cartésien de COLORS par lui même LENGTH fois
#on utilise un type set car l'ordre n'est pas important
def codebreaker1(evaluation_p):
global possible
attempt = possible.pop() #on prend un élément aléatoire --car possible est du type set-- dans possible
return ''.join(attempt) #on renvoie une chaine de 4 caractères
On fait jouer codemaker1 contre codebreaker1
#DECOMMENTER POUR VOIR L'AFFICHAGE DE LA PARTIE
def play1_1():
"""
Fonction principale de ce programme :
Fait jouer ensemble le codebreaker1 et le codemaker1
"""
n_tries = 0
codebreaker1_init()
codemaker1_init()
evaluation_p = None
#print('Combinaisons de taille {}, couleurs disponibles {}'.format(LENGTH, COLORS))
while True:
attempt = codebreaker1(evaluation_p)
(red, white) = codemaker1(attempt)
n_tries += 1
#print("Essai {} : {} ({},{})".format(n_tries, attempt, red, white))
evaluation_p = (red, white)
if red >= LENGTH:
#print("Bravo ! Trouvé {} en {} essais".format(attempt, n_tries))
return n_tries
play1_1()
1091
On pose :
''' Cette fonction assure l affichage de plusieurs parties de Mastermind entre codebreaker1 et codemaker1 '''
nb_parties = 1000 #on décide d'un nombre de parties assez grand
X = []
for i in range(nb_parties):
X.append(play1_1())#on ajoute le nombre d'essais qu'il a fallu pour trouver la solution
Moyenne = sum(X)/nb_parties
print("Moyenne =", Moyenne)#on affiche la moyenne expérimentale pour comparer avec l'espérance théorique
largeur = 100 #on choisit une largeur pour les rectangles de l'histogramme
plt.hist(X, bins = range(0, 3800, largeur), rwidth=0.9)
#on construit un histogramme qui affiche pour chaque partie le nombre d'essais nécessaires pour trouver la combinaison secrète
#on trace les probabilités théoriques
plt.axvline(x = Moyenne, color = 'tab:green', linestyle = 'dashed')
plt.axvline(x = (len(COLORS)**LENGTH + 1)/2, color = 'tab:orange', linestyle = 'dashed')
plt.title("{} parties de codebreaker{} contre codemaker{}".format(nb_parties, 1, 1))
plt.xlabel("Nombre d'essais par partie")
plt.ylabel("Nombre de parties")
plt.legend(["Moyenne (expérimentale)", "Espérance (théorique)", "Valeurs réelles"])
plt.show()
Moyenne = 630.005
Nous allons complexifier le jeu en modifiant légèrement les règles :
def codemaker2_init():
global solution
global possible
solution = ''.join(choices(COLORS, LENGTH))
possible = set(itertools.product(COLORS, repeat=LENGTH)) #le codemaker crée un ensemble possible
#car il n'a pas accès à celui du codebreaker
def codemaker2(attempt):
global solution
global possible
score_solution = score_codemaker2(attempt, solution)
#on place dans score_solution le score de la solution entrée par rapport à l'essai
for i in possible:
i = ''.join(i) #on choisit une combinaison qui fera office de solution provisoire
score_i = score_codemaker2(attempt, i) #on calcule le score de cette solution par rapport à l'essai
if (score_i > score_solution) or (score_i == score_solution and random.randint(0,1) == 1):
#si ce score est supérieur au score de la solution précédente
solution = i #la solution prend la valeur i
score_solution = score_i #le score prend la valeur du nouveau score
#print(solution)
red, white = evaluation(attempt, solution) #on détermine l'évaluation à partir de cette nouvelle solution
maj_possibles(possible, attempt, (red, white)) #on met à jour possible pour le tour suivant
return red, white
def score_codemaker2(attempt, solution): #les arguments sont de type str
'''
Fonction principale de ce programme :
Calculer le nombre de combinaisons compatibles avec une solution entrée en argument
(i.e celles qui ont la même évaluation avec l'essai du codebreaker que cet essai avec la combinaison solution).
Codemaker2 choisit alors la solution qui est compatible avec le maximum d'éléments de 'possible'
'''
global possible
evaluation_p = evaluation(attempt, solution) #on évalue l'essai par rapport à la solution provisoire
count = 0
for i in possible:
if evaluation(attempt, ''.join(i)) == evaluation_p:
#si l'évaluation de la combinaison choisie dans possible correspond à celle donnée précédemment
count += 1 #on compte cette combinaison car elle serait gardée si on utilisait maj_possibles
return count
def codebreaker2_init():
global possible
possible = set(itertools.product(COLORS, repeat=LENGTH))
def codebreaker2(evaluation_p):
global possible
global attempt #on passe l'essai en variable globale pour s'en souvenir au tour suivant et interpréter l'évaluation
if evaluation_p != None: #on a déjà fait un premier essai
maj_possibles(possible, attempt, evaluation_p) #on met à jour les possibles en cohérence avec l'essai
possible = list(possible)
attempt = random.choice(possible) #on choisit le prochain essai
possible.remove(attempt)
possible = set(possible) #on retransforme possible en set pour appliquer la fonction maj_possibles
return ''.join(attempt) #on renvoie un essai de l'ensemble des possibles mis à jour.
Il est temps de faire jouer codemaker2 contre codebreaker2 !
#DECOMMENTER LES LIGNES POUR VOIR L'AFFICHAGE DE LA PARTIE / COMMENTER POUR CONSTRUIRE L'HISTOGRAMME
def play2_2():
"""
Fonction principale de ce programme :
Fait jouer ensemble le codebreaker2 et le codemaker2
"""
n_tries = 0
codebreaker2_init()
codemaker2_init()
evaluation_p = None
#print('Combinaisons de taille {}, couleurs disponibles {}'.format(LENGTH, COLORS))
while True:
attempt = codebreaker2(evaluation_p)
(red, white) = codemaker2(attempt)
n_tries += 1
#print("Essai {} : {} ({},{})".format(n_tries, attempt, red, white))
evaluation_p = (red, white)
if red >= LENGTH:
print("Bravo ! Trouvé {} en {} essais".format(attempt, n_tries))
return n_tries
for p in range(3):
play2_2()
Bravo ! Trouvé OONV en 7 essais Bravo ! Trouvé BOMO en 7 essais Bravo ! Trouvé MOMO en 6 essais
On peut aussi comparer les résultats en faisant jouer codemaker1 contre codebreaker2.
#DECOMMENTER POUR VOIR L'AFFICHAGE DE LA PARTIE
def play1_2():
"""
Fonction principale de ce programme :
Fait jouer ensemble le codebreaker2 et le codemaker1
"""
n_tries = 0
codebreaker2_init()
codemaker1_init()
evaluation_p = None
#print('Combinaisons de taille {}, couleurs disponibles {}'.format(LENGTH, COLORS))
while True:
attempt = codebreaker2(evaluation_p)
(red, white) = codemaker1(attempt)
n_tries += 1
#print("Essai {} : {} ({},{})".format(n_tries, attempt, red, white))
evaluation_p = (red, white)
if red >= LENGTH:
#print("Bravo ! Trouvé {} en {} essais".format(attempt, n_tries))
return n_tries #on renvoie le nb d'essais
play1_2()
4
En comparant les jeux entre codebreaker2 et codemaker 1 ou 2, on obtient un histogramme semblable à celui-ci : on voit que codemaker2 complexifie la résolution du jeu !
'''
Fonction principale du programme :
Afficher le nombre d essais nécesssaires à codebreaker2 lors de chaque partie pour trouver la solution
Comparer les résultats pour des parties contre codemaker1 ou codemaker2.
'''
nb_parties = 50
X = []
Z=[]
for i in range(nb_parties):
X.append(play1_2())
Z.append(play2_2())
#affichage des moyennes
Moyenne_x = sum(X)/nb_parties
print("Moyenne (codemaker1) =", Moyenne_x)
Moyenne_z = sum(Z)/nb_parties
print("Moyenne (codemaker2) =", Moyenne_z)
xmin = 0
xmax = 9
plt.hist(X, bins = np.linspace(xmin - 0.5, xmax + 0.5, xmax - xmin + 2), rwidth=0.9, alpha = 0.5)
plt.hist(Z, bins = np.linspace(xmin - 0.5, xmax + 0.5, xmax - xmin + 2), rwidth = 0.9, alpha = 0.5)
plt.axvline(x = Moyenne_x, color = 'tab:red', linestyle = 'dashed')
plt.axvline(x = Moyenne_z, color = 'tab:green', linestyle = 'dashed')
plt.title("{} parties de codebreaker{} contre codemaker{} ou codemaker{}".format(nb_parties, 2, 1, 2))
plt.xlabel("Nombre d'essais par partie")
plt.ylabel("Nombre de parties")
plt.legend(["Moyenne (codemaker1)", "Moyenne (codemaker2)", "Valeurs (codemaker1)", "Valeurs (codemaker2)"])
plt.show()
Bravo ! Trouvé BBVM en 6 essais Bravo ! Trouvé RMNM en 7 essais Bravo ! Trouvé VNVR en 6 essais Bravo ! Trouvé BRVM en 6 essais Bravo ! Trouvé MVOB en 7 essais Bravo ! Trouvé NBVV en 6 essais Bravo ! Trouvé NBMN en 7 essais Bravo ! Trouvé VNRN en 6 essais Bravo ! Trouvé RBMN en 7 essais Bravo ! Trouvé MNMV en 7 essais Bravo ! Trouvé BVNN en 6 essais Bravo ! Trouvé VRNR en 6 essais Bravo ! Trouvé NONM en 6 essais Bravo ! Trouvé VORR en 6 essais Bravo ! Trouvé NMON en 6 essais Bravo ! Trouvé MMVN en 6 essais Bravo ! Trouvé VRRV en 6 essais Bravo ! Trouvé RMOM en 6 essais Bravo ! Trouvé RNMO en 6 essais Bravo ! Trouvé OVNV en 7 essais Bravo ! Trouvé RRNB en 6 essais Bravo ! Trouvé BBMN en 6 essais Bravo ! Trouvé MMON en 6 essais Bravo ! Trouvé MNMB en 6 essais Bravo ! Trouvé NMRM en 6 essais Bravo ! Trouvé BRMV en 6 essais Bravo ! Trouvé VBMM en 6 essais Bravo ! Trouvé NVNR en 8 essais Bravo ! Trouvé OVBV en 6 essais Bravo ! Trouvé VVBR en 6 essais Bravo ! Trouvé NBBO en 6 essais Bravo ! Trouvé OOVB en 6 essais Bravo ! Trouvé RMNR en 6 essais Bravo ! Trouvé NBON en 6 essais Bravo ! Trouvé BONN en 7 essais Bravo ! Trouvé RVMM en 6 essais Bravo ! Trouvé MORV en 6 essais Bravo ! Trouvé VOOB en 7 essais Bravo ! Trouvé OVOB en 6 essais Bravo ! Trouvé NOVO en 6 essais Bravo ! Trouvé RBBV en 7 essais Bravo ! Trouvé RBOR en 6 essais Bravo ! Trouvé OMBO en 7 essais Bravo ! Trouvé ORNV en 7 essais Bravo ! Trouvé RBNM en 7 essais Bravo ! Trouvé RNON en 6 essais Bravo ! Trouvé MNMB en 7 essais Bravo ! Trouvé MRMR en 6 essais Bravo ! Trouvé MNRN en 6 essais Bravo ! Trouvé OMOB en 6 essais Moyenne (codemaker1) = 4.58 Moyenne (codemaker2) = 6.3
Nous essayons de rendre le décodage de la combinaison secrète optimal, en utilisant l'algorithme de Knuth. David Knuth est à l'origine du Five Guess. Selon lui, il serait possible de décoder toute combinaison secrète en 5 essais maximum en suivant une méthode particulière. Dans son essai de 1977, il démontre sa proposition pour un jeu du Mastermind avec des combinaisons à 4 pions parmi 6 couleurs possibles. Voici l'algorithme. 1) on essaie une première combinaison et on obtient une évaluation. Le choix de la 1ere combinaison a son importance;
2) on met à jour le set des combinaisons compatibles avec cette évaluation, noté maj_possibles
;
3) on parcourt l'ensemble des essais restants, càd les $6^4$ combinaisons moins celles déjà utilisées. Pour chaque essai restant, noté $P$, et pour chaque évaluation possible notée $E$ ((0,1), (0,2), (0,3) etc ...) on calcule le nombre de combinaisons de maj_possibles
dont l'évaluation avec $P$ est exactement $E$. On choisit ensuite le maximum de ces scores intermédiaires. On répète cette opération pour l'ensemble des combinaisons restantes. Ce maximum sera le score de $P$.
4) on choisit la combinaison avec le score miminum : si plusieurs combinaisons ont le même score on peut garder la première combinaison rencontrée. Cette combinaison sera proposée comme essai au prochain tour. La combinaison choisie n'est pas forcément dans maj_possibles
mais l'algorithme fonctionne quand même.
LENGTH = 4
COLORS = ['R', 'V', 'B','N','M','O']
def codebreaker31_init():
global D #on a besoin de variables globales pour D et possible car les sets évoluent au cours de la partie
global possible
global not_tried
possible = set(itertools.product(COLORS, repeat=LENGTH))
D = set(itertools.product(COLORS, repeat=LENGTH)) #D représente l'ensemble des combinaisons inutilisées par codebreaker3
def codebreaker31(evaluation_p):
global D
global possible
global essai
if evaluation_p != None: #s'il n'y a pas d'évaluation c'est qu'on en est au premier tour de jeu
maj_possibles(possible, essai, evaluation_p) #on met à jour les possibles en cohérence avec l'essai précédent
if len(possible) == 1: #si il ne reste qu'une possibilité, on connaît la solution
essai = possible.pop()
return ''.join(essai)
else:
score_attempt = len(possible) #si une combinaison obtient ce score, c'est qu'elle ne donne aucune nouvelle information
for i in D : #cet ensemble est identique à possible en début de partie
score_i = score_codebreaker_31(i)
if (score_i < score_attempt): #on ne garde que la combinaison dont le score est le plus faible
essai = i
score_attempt = score_i
possible.discard(essai) #si la partie continue, alors on saura que cet essai n'est pas possible
D.discard(essai)
return ''.join(essai) #on renvoie l'essai avec le score le plus faible de l'ensemble des possibles mis à jour.
def score_codebreaker_31(essai): #l'argument est un tuple, c'est un élément de l'ensemble de départ
'''Fonction du programme :
Pour chaque élément de possible P, la fonction calcule le nb de combinaisons restantes dans maj_possibles
pour chaque score (rouge,blanc) en supposant que P soit la solution.
Le score de P sera le nb **maximum** de combinaisons restants dans maj_possibles pour une évaluation donnée (rouge,blanc)
'''
global possible
A = [(0,0),(0,1),(0,2),(0,3),(0,4),(1,0),(1,1),(1,2),(1,3),(2,0),(2,1),(2,2),(3,0),(4,0)]
# liste de tous les scores possibles
essai = ''.join(essai)
count_max = 0
for k in range (len(A)) : #on parcourt les évaluations possibles
evaluation_pro = A[k] #on prend une évaluation en particulier
count = 0
for j in possible: #on parcourt tous les essais compatibles avec l'évaluation du codemaker1
if evaluation(essai, ''.join(j)) == evaluation_pro:
count += 1
if count > count_max: #on choisit le maximum qui sera le score associé à un élément de possible
count_max = count
return count_max
On fait jouer codebreaker3 contre codemaker1.
#DECOMMENTER POUR VOIR L'AFFICHAGE DE LA PARTIE
def play1_3():
global essai
"""
Fonction principale de ce programme :
Fait jouer ensemble le codebreaker3 et le codemaker1
"""
n_tries = 0
codebreaker31_init()
codemaker1_init()
evaluation_p = None
#print('Combinaisons de taille {}, couleurs disponibles {}'.format(LENGTH, COLORS))
while True:
if n_tries ==0:
essai = 'RRVV'
else:
essai = codebreaker31(evaluation_p)
(red, white) = codemaker1(essai)
n_tries += 1
#print("Essai {} : {} ({},{})".format(n_tries, essai, red, white))
evaluation_p = (red, white)
if red >= LENGTH:
print("Bravo ! Trouvé {} en {} essais".format(essai, n_tries))
return n_tries #on renvoie le nb d'essais
play1_3()
Bravo ! Trouvé NORO en 4 essais
4
L'algorithme devrait nous donner une moyenne inférieure à 5 essais par partie pour trouver la solution du codemaker. Comme cet algorithme cherche à minimiser le nombre d'essais de codebreaker3, il est assez long : codebreaker3 va parcourir plusieurs fois le set des possibles.
'''
Fonction principale du programme :
Afficher le nombre d essais nécesssaires à codebreaker3 lors de chaque partie contre codemaker1 pour trouver la solution
'''
#ATTENTION : ALGORITHME LONG!
nb_parties = 5 #l'algorithme est long car codebreaker3 va parcourir plusieurs fois le set de possibles
Y = []
for i in range(nb_parties):
Y.append(play1_3())
#affichage des moyennes
Moyenne_y = sum(Y)/nb_parties
print("Moyenne (codebreaker3) =", Moyenne_y)
xmin = 0
xmax = 9
plt.hist(Y, bins = np.linspace(xmin - 0.5, xmax + 0.5, xmax - xmin + 2), rwidth = 0.9)
plt.axvline(x = Moyenne_y, color = 'tab:red', linestyle = 'dashed')
plt.title("{} parties de codebreaker{} contre codemaker{}".format(nb_parties, 3, 1))
plt.xlabel("Nombre d'essais par partie")
plt.ylabel("Nombre de parties")
plt.legend(["Moyenne (codebreaker3)", "Valeurs_1_3"])
plt.show()
Bravo ! Trouvé RNON en 5 essais Bravo ! Trouvé BBOR en 5 essais Bravo ! Trouvé MMBR en 5 essais Bravo ! Trouvé OMVV en 5 essais Bravo ! Trouvé RVVM en 4 essais Moyenne (codebreaker3) = 4.8
Pour s'amuser on peut aussi faire jouer l'humain contre la machine : nous choisissons ici codemaker1 de façon à ce que la solution choisie au départ ne change pas au cours de la partie et donc que les évaluations successives permettent à l'humain de décoder la combinaison secrète.
dico_color = {'R': 'red', 'V':'green', 'B':'blue', 'J':'yellow', 'N':'black', 'M':'brown', 'O':'orange', 'G':'grey'}
def creation_image(dico_essai):
"""Partie graphique du jeu, affichage de chaque essai
"""
figure = plt.figure(figsize = (6,6)) #on crée la grille
axes = plt.gca(title = 'Mastermind')
centres_y = np.linspace(0,1,14)[1:-1] #on crée les centres des cercles
centres_x = np.linspace(0,0.4,6)[1:-1]
for i in range (len(centres_x)):
for j in range (len(centres_y)):
if j in dico_essai: #on affiche le cercle de la bonne couleur
axes.add_artist(matplotlib.patches.Circle((centres_x[i],centres_y[11-j]),0.03, color = f'{dico_color[dico_essai[j][0][i]]}', ec = 'black'))
for a in range(dico_essai[j][1][0]):#on affiche les plots bien placés
axes.add_artist(matplotlib.patches.Circle((centres_x[a]+0.5,centres_y[11-j]),0.01, color = 'red', ec = 'black'))
for b in range(dico_essai[j][1][1]):#on affiche les plots mal placés
axes.add_artist(matplotlib.patches.Circle((centres_x[dico_essai[j][1][0]+b]+0.5,centres_y[11-j]),0.01, color = 'silver', ec = 'black'))
for c in range(dico_essai[j][1][0] + dico_essai[j][1][1], 4):#on affiche les plots restants
axes.add_artist(matplotlib.patches.Circle((centres_x[c]+0.5,centres_y[11-j]),0.01, color = 'white', ec = 'black'))
else:#on affiche les essais restants en blanc
axes.add_artist(matplotlib.patches.Circle((centres_x[i],centres_y[11-j]),0.03, color = 'white', ec = 'black'))
axes.add_artist(matplotlib.patches.Circle((centres_x[i]+0.5,centres_y[11-j]),0.01, color = 'white', ec='black'))
plt.show()#on montre l'image
def play_human_against_codemaker1():
"""
Fonction principale du programme:
Fait jouer l'utilisateur humain (au clavier) dans le role du codebreaker
contre codemaker1
"""
dico_essai = {}
# dico_essai associe au numero de l'essai un tuple de la forme (essai, correction)
n_tries = 0
codemaker1_init()
evaluation_p = None
print('Combinaisons de taille {}, couleurs disponibles {}'.format(LENGTH, COLORS))
while True:
attempt = input("Saisir combinaison: ")
#On entre directement une combinaison au clavier
if len(attempt) != 4:
print("Combinaison invalide (pas la bonne taille)")
continue
(red, white) = codemaker1(attempt)
correction = (red, white)
dico_essai[n_tries] = (attempt,correction)
n_tries += 1
print("Essai {} : {} ({},{})".format(n_tries, attempt, red, white))
creation_image(dico_essai)
evaluation_p = (red, white)
if red >= LENGTH:
print("Bravo ! Trouvé {} en {} essais".format(attempt, n_tries))
break
play_human_against_codemaker1()
Combinaisons de taille 4, couleurs disponibles ['R', 'V', 'B', 'N', 'M', 'O'] Saisir combinaison: BBNN Essai 1 : BBNN (1,1)
Saisir combinaison: BJJB Essai 2 : BJJB (0,0)
Saisir combinaison: NRRN Essai 3 : NRRN (2,0)
Saisir combinaison: NMVN Essai 4 : NMVN (3,0)
Saisir combinaison: NMON Essai 5 : NMON (3,0)
Saisir combinaison: NMMN Essai 6 : NMMN (4,0)
Bravo ! Trouvé NMMN en 6 essais