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 ?