# [TGG et QBO] Projet IAT ###### tags: `IAT`, `Projet 1`, `TGG ET QBO` [Le sujet](https://moodle.insa-lyon.fr/pluginfile.php/280283/mod_resource/content/1/Projet-IAT-MasterMind-2021.pdf) ## Résolution du Mastermind par méta-heuristiques ### Etape 1 : Modélisation du problème #### Question 1 Chaque combinaison de pions sera représentée par un tableau de 4 chiffres allant de 1 à 8 où chaque chiffre correspond à une couleur (ex : 1 = rouge, 2 = bleu, 3 = vert, etc…) Exemple de représentation de combinaison: [1,6,3,8] #### Question 2 Pour une combinaison de N pions avec k couleurs, il existe $k^N$ solutions. Pour 4 pions de 8 couleurs on a donc $8^4=4096$ solutions #### Question 3.1 Soit X le rapport entre le poid des éléments placés correctement et mal placés. Différentes valeurs de ce rapport seront testés pour voir quel est le rapport le plus optimal ```python= int X def score (p, m): if (p<0 or m<0): exit(1) elif (p==0 and m==0): return 0 else: return (p*X + m) ``` #### Question 3.2 **Fonction compare** ```python= def compare(c,j): m,p = 0,0 c_b = [] j_b = [] for i in range(len(c)): if c[i]==j[i]: p=p+1 else: c_b.append(c[i]) j_b.append(j[i]) for j in range(len(c_b)): for k in range(len(j_b)): if j_b[k]==c_b[j]: m+=1 c_b[j]=0 return m,p ``` **Fonction eval** ```python= def eval(c, j): j_p, j_m = compare(j,A_DEVINER) v_p, v_m = compare (c,j) j_score=score(j_p, j_m) v_score=score(v_p,v_m) return abs(j_score-v_score) ``` #### Question 3.3 **Fonction fitness** ```python= def fitness(c): fitness = 0 for j in range (len(historique)): fitness = fitness + eval(c, historique[j]) return fitness / len(historique) ``` ### Etape 2 : Résolution par algorithme génétique #### Question 1 Pour sélectionner les m "meilleurs" candidats pour la nouvelle génération, nous pouvons calculer la "Fitness" de tous les candidats d'une génération puis les normaliser. On peut ensuite les ordonner en fonction de leur Fitness et l'on gardera les m candidats avec la Fitness la plus faible. On pourrait aussi faire un tirage au sort biaisé par la fitness où les éléments avec la probabilité d'être tiré au sort est proportionelle à 1/Fitness. **Fonction meilleur_candiat** ```python def meilleur_candidat(generation): liste_candidat = [] for i in range(TAILLE_POPULATION): valeur_fitness = fitness(generation[i]) liste_candidat.append((valeur_fitness, generation[i])) # on trie la liste et on extrait la fitness dans une nouvelle liste liste_candidat = sorted(liste_candidat, key=lambda element: element[0]) return liste_candidat[0][1] ``` #### Question 2 Une opération simple de mutation pourrait être de choisir aléatoirement un des 4 pions de la solution, puis de tirer au sort une nouvelle couleur et de lui affecter. On peut également interchanger aléatoirement deux pions d'une même solution. **Fonction mutation** ```python def mutation(c): index_aleatoire = random.randint(0,3) couleur_aleatoire = random.randint(1,8) #tant que la couleur n'est pas différente de la précédente on retire au sort while couleur_aleatoire==c[index_aleatoire]: couleur_aleatoire = random.randint(1,8) c[index_aleatoire] = couleur_aleatoire return c ``` **Fonction croisement** ```python def croisement(parent1, parent2): enfant = [-1, -1, -1, -1] index_aleatoire = random.randint(0,3) for i in range (2): enfant[(index_aleatoire+i)%4]=parent1[(index_aleatoire+i)%4] enfant[(index_aleatoire+i+2)%4]=parent2[(index_aleatoire+i+2)%4] return enfant ``` #### Question 3 Une opération de croisement pourrait être de garder 2 pions de la première solution et deux de la deuxième afin de constituer une nouvelle solution candidate valide. 0) Générer 1 première solution aléatoire que l'on joue 1) Générer une populations de 100 solutions aléatoires 2) Selectionner les 50 meilleures (celles avec la fitness la plus faible) 3) Les faire muter et croiser pour recréer une génération de 100 solutions 4) Chosir le meilleur individu et le jouer 5) Recommencer à l'étape 2 a partir des 100 solutions de la nouvelle génération et ce jusqu'a obtenir le bon code **Fonction nouvelle_generation** ```python def nouvelle_generation(generation): liste_candidat = [] new_generation = [] # On trie les la génération de candidats for i in range(TAILLE_POPULATION): valeur_fitness = fitness(generation[i]) liste_candidat.append((valeur_fitness, generation[i])) liste_candidat = sorted(liste_candidat, key=lambda element: element[0]) #On garde la première moitié des meilleurs candidats for i in range (0,50): new_generation.append(liste_candidat[i][1]) new_generation[i]=mutation(new_generation[i]) #On mute chacun des candidats #On croise pour former les candidats restant de la nouvelle generation for i in range (0,25): parent1 = new_generation[i] parent2 = new_generation[i+24] parent3 = new_generation[49-i] new_generation.append(croisement(parent1,parent2)) new_generation.append(croisement(parent1,parent3)) return new_generation ``` #### Implémentation Notre code est disponible dans l'archive déposée **Fonction Main** ```python if __name__ == '__main__': print("Tirage du code à trouver effectué -> c'est parti !") A_DEVINER= initialisation_code() # Tirage au sort du code a déviner premier_candidat = initialisation_code() # Tirage au sort du premier candidat nombre_iteration=1 # Définition du compteur d'itérations candidats=[] # Définition d'une liste pour stocker les candidats m,p = compare(premier_candidat,A_DEVINER) #On test le premier candidat historique.append(premier_candidat) #On l'ajoute à l'historique generation=initialisation_population(100) #Tirage aléatoire de la première génération print("coup n°:", nombre_iteration, " : " , premier_candidat, " -> ", p , "bien placés et ", m , " mal placés") while p !=4: # tant que le code n'est pas le même nombre_iteration = nombre_iteration+1 generation = nouvelle_generation(generation) solution_retenue = meilleur_candidat(generation) m,p = compare(solution_retenue, A_DEVINER) #on joue le candidat retenu historique.append(solution_retenue) #On ajoute le candidat joué à l'historique print("coup n°",nombre_iteration, ":" , solution_retenue, " -> ", p , "bien placés et ", m , " mal placés") print("=============") print("Code trouvé en ", nombre_iteration ," itérations") print("La solution était ", A_DEVINER) print("====================") ``` #### Résultats Après des tests sur plusieurs centaines de codes différents notre algorithme génétique résout le Mastermind en environ 7 coups en moyenne (6.88 pour être exact)