Nous allons voir comment implémenter un MasterMind en Python, mais uniquement en mode console.

MasterMind en Python: le jeu
Le jeu du MasterMind consiste deviner une combinaison de couleurs.
À chaque proposition, votre adversaire doit vous dire:
- combien de couleurs sont au(x) bon(s) endroit(s), sans indiquer lesquelles;
- combien de couleurs sont bonnes mais pas au bon endroit.
Vous l’aurez compris, votre adversaire, ce sera l’ordinateur… C’est lui qui décidera la combinaison de couleurs à trouver.
MasterMind en Python: implémentation
Préliminaires
Nous allons décider du fait que les couleurs seront représentées par des lettres capitalisées de l’alphabet latin: A, B, C, D, …
Nous allons donc écrire une première fonction qui retourne l’alphabet sur lequel s’appuyer pour générer une combinaison de couleurs à trouver:
from string import ascii_uppercase
def gen_colors(code_size):
if code_size <= 26:
return ascii_uppercase[:code_size]
else:
return ascii_uppercase
Cette fonction retourne donc les n premières lettres de l’alphabet si n< 27, et retourne l’alphabet complet si n > 26.
>>> gen_colors(5)
'ABCDE'
>>> gen_colors(200)
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
Il nous faut maintenant une fonction retournant une combinaison aléatoire de n couleurs prises parmi toutes celles de ce nouvel ensemble.
from random import choice
def gen_code(code_size, colors):
r = ''
for _ in range(code_size):
r += choice(colors)
>>> gen_code(5,'ABCDEFGH')
'GCBAG'
Vérifications
Nous allons dans un premier temps écrire une fonction check_guess(guess, code_size, colors) qui vérifie si, d’une part, la longueur de « guess » est la même que code_size, et d’autre part si une couleur présente dans « guess » est aussi dans « colors »:
def check_guess(guess, code_size, colors):
present_colors = [ i in colors for i in guess ]
return len( guess ) == code_size and False not in present_colors
J’ai opté pour cette solution, mais il y a d’autres possibilités.
Ici, je construis une liste de booléens correspondants au fait que chaque élément de « guess » se trouve ou non dans « colors ». Si il n’y a aucun « False » dans cette liste, cela signifie que toutes les couleurs proposées sont dans l’alphabet autorisé.
Si la longueur de « guess » est la même que celle de « colors » ET si aucun « False » est dans la liste construite, la fonction renvoie « True ».
Renvoi du nombre de bonnes couleurs
def score_guess(code, guess):
n_good_position = 0
n_false_position = 0
if len( code ) == len( guess ):
for i in range( len(code) ):
if code[i] == guess[i]:
n_good_position += 1
elif guess[i] in code:
n_false_position += 1
return n_good_position , n_false_position
>>> score_guess('ABCD' , 'AABC')
(1, 3)
Il y a en effet 1 bonne lettre (à sa place) et en tout 3 lettres correctes mais pas à la bonne place (le second ‘A’ est à la deuxième position et non à la 1ère, le ‘B’ et le ‘C’ sont bonnes mais pas au bon endroit).
Le jeu
def play(code_size, nb_colors):
print(f'Les différentes couleurs possibles sont: {gen_colors(nb_colors)}.')
print(f'Le code à trouver est de longueur {code_size}.')
n = 0
to_find = gen_code(code_size, gen_colors(nb_colors)) # combinaison à trouver
while True:
guess = input(f'{n} --> ').upper()
if not check_guess(guess, code_size, gen_colors(nb_colors)):
print('Mauvaise taille ou couleur...')
elif guess != to_find:
print( score_guess(to_find, guess) )
n += 1
else:
print( f'Félicitations, vous avez trouvé après {n+1} essais!' )
break
>>> play(4,4)
Les différentes couleurs possibles sont: ABCD.
Le code à trouver est de longueur 4.
0 --> ABCD
(1, 2)
1 --> ABBB
(0, 1)
2 --> CBCC
(1, 2)
3 --> BBDD
(1, 1)
4 --> DBCB
(0, 2)
Bon, je ne sais pas pour vous mais c’est assez frustrant de ne pas trouver après un certain nombre d’essais.
Je vais donc ajouter un argument à ma fonction pour limiter le nombre de coups maximum.
def play(code_size, nb_colors , nb_max = None):
print(f'Possible colors are: {gen_colors(nb_colors)}.')
print(f'Code size is {code_size}.')
n = 0
count = 0
to_find = gen_code(code_size, gen_colors(nb_colors)) # combinaison à trouver
while True:
if nb_max != None and n <= nb_max or nb_max == None:
guess = input(f'{n} --> ').upper()
if not check_guess(guess, code_size, gen_colors(nb_colors)):
print('Mauvaise taille ou couleur...')
elif guess != to_find:
print( score_guess(to_find, guess) )
n += 1
else:
print( f'Félicitations, vous avez trouvé après {n+1} essais!' )
break
else:
print(f'Il fallait trouver: {to_find}')
break
>>> play(4,4,3)
Possible colors are: ABCD.
Code size is 4.
0 --> aaaa
(1, 3)
1 --> bbbb
(1, 3)
2 --> cccc
(1, 3)
3 --> abcd
(1, 3)
Il fallait trouver: DACB
Mémorisation des propositions
Dans la pratique, il est possible de s’y perdre avec toutes les propositions que l’on a faites.
Si l’on souhaite ne pas compter les coups identiques, on peut mémoriser tous les coups dans une liste L.
def play(code_size, nb_colors , nb_max = None):
print(f'Possible colors are: {gen_colors(nb_colors)}.')
print(f'Code size is {code_size}.')
n = 0
count = 0
to_find = gen_code(code_size, gen_colors(nb_colors)) # combinaison à trouver
L = []
while True:
if nb_max != None and n <= nb_max or nb_max == None:
guess = input(f'{n} --> ').upper()
if not check_guess(guess, code_size, gen_colors(nb_colors)):
print('Mauvaise taille ou couleur...')
elif guess in L:
print('Proposition déjà faite avant...')
elif guess != to_find:
print( score_guess(to_find, guess) )
L.append( guess )
n += 1
else:
print( f'Félicitations, vous avez trouvé après {n+1} essais!' )
break
else:
print(f'Il fallait trouver: {to_find}')
break
>>> play(4,4,3)
Possible colors are: ABCD.
Code size is 4.
0 --> abcd
(0, 4)
1 --> abcd
Proposition déjà faite avant...
1 --> aabb
(0, 4)
2 --> bbbb
(1, 3)
3 --> dcba
(2, 2)
Il fallait trouver: BCDA
Bon, là, on est pas mal non ?