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 ?