Bonjour la communauté,
je ne sais pas si je suis sur le bon forum! mais si c'est le cas bien vouloir me réorienter svp!!
Je suis étudiant en Sécurité-Réseau-Système, et j'ai dans mon programme un cours appelé Recherche Opérationnelle et Intelligence Artificielle (gros nomm!!!!) bref!! j'ai un programme sur lequel je bosse et j'ai le code( car fourni par le prof) avec un détail selon lequel il faudrait modifier manuellement une variable pour appliquer voir le comportement des algo tels que le "recherche local naive" "recuit simulé" "algo génétique" sur 6 problèmes différents.
Etant donné que je ne suis pas un ange du code, j'aimerai savoir s'il est possible de modifier le code de tel sorte qu'il y'ait un compteur et que je n'ai plus à faire varier les valeurs manuellement dans le mainscript voir ligne rouge !!
Merci
Voici le code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System;
/// <summary>
/// Classe principale à utiliser pour implémenter vos algorithmes
/// Si vous souhaitez utiliser plusieurs scripts (1 par algorithme),
/// vous le pouvez aussi.
/// </summary>
public class MainScript : MonoBehaviour
{
/// <summary>
/// Indique si un algorithme est en cours d'exécution
/// </summary>
private bool _isRunning = false;
/// <summary>
/// Indique si une evaluation de solution est en cours
/// </summary>
private bool _inSimulation = false;
/// <summary>
/// Méthode utilisée pour gérer les informations et
/// boutons de l'interface utilisateur
/// </summary>
public void OnGUI()
{
// Démarrage d'une liste de composants visuels verticale
GUILayout.BeginVertical();
// Affiche un bouton permettant le lancement de la recherche locale naïve
if (GUILayout.Button("DEMARRAGE RECHERCHE LOCALE NAIVE"))
{
// Le bouton est inactif si un algorithme est en cours d'exécution
if (!_isRunning)
{
// Démarrage de la recherche locale naïve en pseudo asynchrone
StartCoroutine("NaiveLocalSearch");
}
}
// Affiche un bouton permettant le lancement de la recherche locale naïve
if (GUILayout.Button("DEMARRAGE RECUIT SIMULE"))
{
// Le bouton est inactif si un algorithme est en cours d'exécution
if (!_isRunning)
{
// Démarrage du recuit simulé en pseudo asynchrone
StartCoroutine("SimulatedAnnealing");
}
}
// Affiche un bouton permettant le lancement de l'algorithme génétique
if (GUILayout.Button("DEMARRAGE ALGORITHME GENETIQUE"))
{
// Le bouton est inactif si un algorithme est en cours d'exécution
if (!_isRunning)
{
// Démarrage de l'algorithme génétique en pseudo asynchrone
StartCoroutine("GeneticAlgorithm");
}
}
// Affiche un bouton permettant le lancement de l'algorithme de Djikstra
if (GUILayout.Button("DEMARRAGE DJIKSTRA"))
{
// Le bouton est inactif si un algorithme est en cours d'exécution
if (!_isRunning)
{
// Démarrage de l'algorithme de Djikstra en pseudo asynchrone
StartCoroutine("Djikstra");
}
}
// Affiche un bouton permettant le lancement de l'algorithme A*
if (GUILayout.Button("DEMARRAGE A*"))
{
// Le bouton est inactif si un algorithme est en cours d'exécution
if (!_isRunning)
{
// Démarrage de l'algorithme A* en pseudo asynchrone
StartCoroutine("AStar");
}
}
// Fin de la liste de composants visuels verticale
GUILayout.EndVertical();
}
/// <summary>
/// Initialisation du script
/// </summary>
void Start()
{
// Pour faire en sorte que l'algorithme puisse continuer d'être actif même
// en tâche de fond.
Application.runInBackground = true;
}
/// <summary>
/// Implémentation possible de la recherche locale naïve
/// sous forme de coroutine pour le mode pseudo asynchone
/// </summary>
/// <returns></returns>
public IEnumerator NaiveLocalSearch()
{
// Indique que l'algorithme est en cours d'exécution
_isRunning = true;
// Génère une solution initiale au hazard (ici une séquence
// de 42 mouvements)
var currentSolution = new PathSolutionScript(42);
// Récupère le score de la solution initiale
// Sachant que l'évaluation peut nécessiter une
// simulation, pour pouvoir la visualiser nous
// avons recours à une coroutine
var scoreEnumerator = GetError(currentSolution);
yield return StartCoroutine(scoreEnumerator);
float currentError = scoreEnumerator.Current;
// Nous récupérons l'erreur minimum atteignable
// Ceci est optionnel et dépendant de la fonction
// d'erreur
var minimumError = GetMinError();
// Affichage de l'erreur initiale
Debug.Log(currentError);
// Initialisation du nombre d'itérations
int iterations = 0;
// Tout pendant que l'erreur minimale n'est pas atteinte
while (currentError != GetMinError())
{
// On obtient une copie de la solution courante
// pour ne pas la modifier dans le cas ou la modification
// ne soit pas conservée.
var newsolution = CopySolution(currentSolution);
// On procède à une petite modification de la solution
// courante.
RandomChangeInSolution(newsolution);
// Récupère le score de la nouvelle solution
// Sachant que l'évaluation peut nécessiter une
// simulation, pour pouvoir la visualiser nous
// avons recours à une coroutine
var newscoreEnumerator = GetError(newsolution);
yield return StartCoroutine(newscoreEnumerator);
float newError = newscoreEnumerator.Current;
// On affiche pour des raisons de Debug et de suivi
// la comparaison entre l'erreur courante et la
// nouvelle erreur
Debug.Log(currentError + " - " + newError);
// Si la solution a été améliorée
if (newError <= currentError)
{
// On met à jour la solution courante
currentSolution = newsolution;
// On met à jour l'erreur courante
currentError = newError;
}
// On incrémente le nombre d'itérations
iterations++;
// On rend la main au moteur Unity3D
yield return 0;
}
// Fin de l'algorithme, on indique que son exécution est stoppée
_isRunning = false;
// On affiche le nombre d'itérations nécessaire à l'algorithme pour trouver la solution
Debug.Log("CONGRATULATIONS !!! Solution Found in " + iterations + " iterations !");
}
// Coroutine à utiliser pour implémenter l'algorithme de Djikstra
public IEnumerator Djikstra()
{
var matrix = MatrixFromRaycast.CreateMatrixFromRayCast();
//TODO
yield return null;
}
// Coroutine à utiliser pour implémenter l'algorithme d' A*
public IEnumerator AStar()
{
//TODO
yield return null;
}
// Coroutine à utiliser pour implémenter l'algorithme du recuit simulé
public IEnumerator SimulatedAnnealing()
{
DateTime start = DateTime.Now;
// Génère une solution initiale au hazard (ici une séquence
// de 42 mouvements)
var currentSolution = new PathSolutionScript(12);
// Récupère le score de la solution initiale
// Sachant que l'évaluation peut nécessiter une
// simulation, pour pouvoir la visualiser nous
// avons recours à une coroutine
var scoreEnumerator = GetError(currentSolution);
yield return StartCoroutine(scoreEnumerator);
float currentError = scoreEnumerator.Current;
///Initialisation de la température à une valeur 'plutot basse'.
float temperature = 2f;
// Nous récupérons l'erreur minimum atteignable
// Ceci est optionnel et dépendant de la fonction
// d'erreur
float minimumError = GetMinError();
// Affichage de l'erreur initiale
Debug.Log("Erreur initiale : " + currentError);
// Affichage de l'erreur initiale
Debug.Log("Min erreur : " + minimumError);
///Initialisation de la valeur de 'stagnation' qui si elle dépasse un
///certain seuil provoquera l'augmentation de la température.
float stagnation = 0.001f;
// Initialisation du nombre d'itérations
int iterations = 0;
///Tout pendant que l'erreur n'est pas nulle
while (currentError != minimumError /*GetMinError()*/)
{
// On obtient une copie de la solution courante
// pour ne pas la modifier dans le cas ou la modification
// ne soit pas conservée.
var newsolution = CopySolution(currentSolution);
// On procède à une petite modification de la solution
// courante.
RandomChangeInSolution(newsolution);
// Récupère le score de la nouvelle solution
// Sachant que l'évaluation peut nécessiter une
// simulation, pour pouvoir la visualiser nous
// avons recours à une coroutine
var newscoreEnumerator = GetError(newsolution);
yield return StartCoroutine(newscoreEnumerator);
float newError = newscoreEnumerator.Current;
// On affiche pour des raisons de Debug et de suivi
// la comparaison entre l'erreur courante et la
// nouvelle erreur
Debug.Log("currentError : " + currentError + " - newError : " + newError + " - minError : " + GetMinError());
///Tirage d'un nombre aléatoire entre 0f et 1f.
float rdm = UnityEngine.Random.Range(0f, 1f);
///Comparaison de ce nombre à la probabilité d'accepter un changement
///déterminée par le critère de Boltzman.
if (rdm < BoltzmanCriteria(temperature, currentError, newError))
{
// On met à jour la solution courante
currentSolution = newsolution;
// On met à jour l'erreur courante
currentError = newError;
}
///Si l'erreur stagne
if (minimumError == currentError)
{
///On incrémente la stagnation
stagnation *= 1.001f;
}
else
{
///Sinon on la réinitialise
stagnation = 0.001f;
}
///Si l'erreur diminue en deça de la meilleure erreur obtenue
if (currentError < minimumError)
{
///On met à jour la meilleure erreur obtenue
minimumError = currentError;
///On réinitialise la stagnation
stagnation = 0.001f;
}
///On met à jour la temperature à chaque tour de boucle :
/// - si la stagnation est suffisante la temperature va augmenter
/// - sinon la temperature décroit de manière géométrique
temperature *= 0.998f + stagnation;
///Affichage dans la console de Debug du couple temperature stagnation
///pour pouvoir être témoin de l'augmentation de la température lorsque
///l'on se retrouve coincé trop longtemps dans un minimum local.
Debug.Log("Temperature : " + temperature + " - stagnation : " + stagnation);
// On incrémente le nombre d'itérations
iterations++;
// On rend la main au moteur Unity3D
yield return 0;
}
// Fin de l'algorithme, on indique que son exécution est stoppée
_isRunning = false;
TimeSpan dur = DateTime.Now - start;
// On affiche le nombre d'itérations nécessaire à l'algorithme pour trouver la solution
Debug.Log("CONGRATULATIONS !!! Solution Found in " + iterations + " iterations in" + dur.TotalSeconds + " secondes !");
}
/// <summary>
/// Proposition d'implémentation du critère de Bolztman représentant une
/// fonction seuil, renvoyant la probabilité d'accepter une permutation
/// selon la différence entre l'erreur courante et la nouvelle erreur et
/// ainsi qu'en fonction de la temperature courante.
/// </summary>
/// <param name="temperature"></param>
/// <param name="currentError"></param>
/// <param name="newError"></param>
/// <returns></returns>
float BoltzmanCriteria(float temperature, float currentError, float newError)
{
///Si la temperature est nulle
///cas particulier pour éviter une division par zéro
if (temperature == 0)
{
return currentError - newError;
}
///Critère de Boltzman
return Mathf.Exp(((float)(currentError - newError)) / temperature);
}
class ScoredPathIndividual
{
/// <summary>
/// La configuration des chemins (solution)
/// </summary>
public PathSolutionScript pathSolution { get; set; }
/// <summary>
/// L'erreur de la configuration ci-dessus
/// </summary>
public float error { get; set; }
}
// Coroutine à utiliser pour implémenter un algorithme génétique
public IEnumerator GeneticAlgorithm()
{
#region Paramètres
int iteration = 0;
///La taille de la population
int popsize = 50;
///Le nombre d'individus séléctionnés pour la reproduction
///(ici 40% de la taille de la population)
int numSelection = (int)(popsize * 0.4f);
///Le taux de mutation (c.à.d. la chance avec laquelle un
///individu issu du croisement peut subir une mutation)
float mutationRate = 0.4f;
#endregion
#region Initialisation de la population
///Initialisation du tableau contenant notre population initiale
PathSolutionScript[] population = new PathSolutionScript[popsize];
///Pour chaque individu que l'on doit créer dans la population initiale
for (int j = 0; j < popsize; j++)
{
population[j] = new PathSolutionScript(42);
}
float bestError = 1000;
#endregion
///Tout pendant que la configuration optimale n'est pas trouvée
while (bestError != GetMinError())
{
#region Evaluation de la population
///Initialisation du tableau destiné à contenir l'ensemble des
///couples chemin/erreur une fois la population évaluée
var scoredPathIndividuals = new ScoredPathIndividual[popsize];
///Pour chaque individu de notre population à évaluer
for (int i = 0; i < population.Length; i++)
{
var newscoreEnumerator = GetError(population[i]);
yield return StartCoroutine(newscoreEnumerator);
float newError = newscoreEnumerator.Current;
///Création d'un couple configuration/solution et stockage
///du score obtenu pour la configuration évaluée.
scoredPathIndividuals[i] = new ScoredPathIndividual()
{
pathSolution = population[i],
error = newError
};
}
#endregion
#region Selection des reproducteurs
///Récupération de manière concise (grâce à Linq) des reproducteurs :
/// - Tri de l'ensemble des couples configuration/score par ordre
/// croissant de score.
/// - Récupération des meilleurs couples.
/// - Récupération des meilleurs configurations.
/// - Stockage des meilleures configurations dans un tableau
var bestPathSolution = scoredPathIndividuals
.OrderBy((scoredIn) => scoredIn.error)
.Take(numSelection)
.Select((scoredIn) => scoredIn.pathSolution)
.ToArray();
bestError = scoredPathIndividuals
.OrderBy((scoredIn) => scoredIn.error)
.Select((scoredIn) => scoredIn.error).First();
#endregion
#region Affichage du meilleur individu
///Affichage Dans la console de Debug du score du meilleur
///individu.
Debug.Log("Meilleur Score de la génération courante : " + bestError);
#endregion
#region Croisement de la population
///Initialisation de la nouvelle population qui va être générée
///par croisement.
PathSolutionScript[] newpopulation = new PathSolutionScript[popsize];
///Pour chaque enfant que l'on doit générer par croisement
for (int i = 0; i < popsize; i++)
{
///Récupération de deux reproduteurs au hasard
var parent1 = bestPathSolution[UnityEngine.Random.Range(0, bestPathSolution.Length)];
var parent2 = bestPathSolution[UnityEngine.Random.Range(0, bestPathSolution.Length)];
///Création d'un individu à partir du croisement des deux parents
newpopulation[i] = Crossover(parent1, parent2);
}
#endregion
#region Mutation de la population
///Pour chaque individu de la population
for (int i = 0; i < popsize; i++)
{
///Tirage d'un nombre au hasard entre 0f et 1f
float rdm = UnityEngine.Random.Range(0f, 1f);
///Comparaison de ce nombre au taux de mutation
///S'il est inférieur, on procède à la mutation
if (rdm < mutationRate)
{
///Mutation proposée :
// On procède à une petite modification de la solution
// courante.
RandomChangeInSolution(newpopulation[i]);
}
}
#endregion
///Remplacement de l'ancienne population par la nouvelle
population = newpopulation;
iteration++;
// On rend la main au moteur Unity3D
yield return 0;
}
// Fin de l'algorithme, on indique que son exécution est stoppée
_isRunning = false;
// On affiche le nombre d'itérations nécessaire à l'algorithme pour trouver la solution
Debug.Log("CONGRATULATIONS !!! Solution Found in " + iteration + " iterations !");
}
/// <summary>
/// Méthode proposant une méthode pour obtenir une nouvel
/// individu par croisement de deux configurations parentes
/// </summary>
/// <param name="parent1">Le parent 1</param>
/// <param name="parent2">Le parent 2</param>
/// <returns>L'enfant généré par croisement</returns>
PathSolutionScript Crossover(PathSolutionScript parent1, PathSolutionScript parent2)
{
PathSolutionScript child = new PathSolutionScript(42);
for (int i = 0; i < parent1.Actions.Count(); i++)
{
if(i%2 != 0)child.Actions[i].Action = parent1.Actions[i].Action;
else child.Actions[i].Action = parent2.Actions[i].Action;
}
return child;
}
/// <summary>
/// Exemple d'erreur minimum (pas forcément toujours juste) renvoyant
/// la distance de manhattan entre la case d'arrivée et la case de départ.
/// </summary>
/// <returns></returns>
// int GetMinError()
// {
// return (int)(Mathf.Abs(PlayerScript.GoalXPositionInMatrix - PlayerScript.StartXPositionInMatrix) +
// Mathf.Abs(PlayerScript.GoalYPositionInMatrix - PlayerScript.StartYPositionInMatrix));
// }
/// Eemple d'erreur minimum, utilisation de la position de départ et de la position d'arrivé
int GetMinError()
{
return (int)(Mathf.Abs(PlayerScript.StartXPositionInMatrix - PlayerScript.GoalXPositionInMatrix) +
Mathf.Abs(PlayerScript.StartYPositionInMatrix - PlayerScript.GoalYPositionInMatrix));
}
/// <summary>
/// Exemple d'oracle nous renvoyant un score que l'on essaye de minimiser
/// Ici est utilisé la position de la case d'arrivée, la position finale
/// atteinte par la solution. Il est recommandé d'essayer plusieurs oracles
/// pour étudier le comportement des algorithmes selon la qualité de ces
/// derniers
///
/// Parmi les paramètres pouvant être utilisés pour calculer le score/erreur :
///
/// - position de la case d'arrivée : PlayerScript.GoalXPositionInMatrix
/// PlayerScript.GoalYPositionInMatrix
/// - position du joueur : player.PlayerXPositionInMatrix
/// player.PlayerYPositionInMatrix
/// - position de départ du joueur : PlayerScript.StartXPositionInMatrix
/// PlayerScript.StartYPositionInMatrix
/// - nombre de cases explorées : player.ExploredPuts
/// - nombre d'actions exécutées : player.PerformedActionsNumber
/// - vrai si le la balle a touché la case d'arrivée : player.FoundGoal
/// - vrai si le la balle a touché un obstacle : player.FoundObstacle
/// - interrogation de la matrice :
/// € la case de coordonnée (i, j) est elle un obstacle (i et j entre 0 et 49) :
/// player.GetPutTypeAtCoordinates(i, j) == LayerMask.NameToLayer("Obstacle")
/// € la case de coordonnée (i, j) est elle explorée (i et j entre 0 et 49) :
/// player.GetPutTypeAtCoordinates(i, j) == 1
/// € la case de coordonnée (i, j) est elle inexplorée (i et j entre 0 et 49) :
/// player.GetPutTypeAtCoordinates(i, j) == 0
/// </summary>
/// <param name="solution"></param>
/// <returns></returns>
IEnumerator<float> GetError(PathSolutionScript solution)
{
// On indique que l'on s'apprête à lancer la simulation
_inSimulation = true;
// On créé notre objet que va exécuter notre séquence d'action
var player = PlayerScript.CreatePlayer();
// Pour pouvoir visualiser la simulation (moins rapide)
player.RunWithoutSimulation = false;
// On lance la simulation en spécifiant
// la séquence d'action à exécuter
player.LaunchSimulation(solution);
// Tout pendant que la simulation n'est pas terminée
while (player.InSimulation)
{
// On rend la main au moteur Unity3D
yield return -1f;
}
// Calcule la distance de Manhattan entre la case d'arrivée et la case finale de
// notre objet, la pondère (la multiplie par zéro si le but a été trouvé)
// et ajoute le nombre d'actions jouées
var error = (Mathf.Abs(PlayerScript.GoalXPositionInMatrix - player.PlayerXPositionInMatrix)
+ Mathf.Abs(PlayerScript.GoalYPositionInMatrix - player.PlayerYPositionInMatrix))
* (player.FoundGoal ? 0 : 100) +
player.PerformedActionsNumber;
//Debug.Log(player.FoundGoal);
// Détruit l'objet de la simulation
Destroy(player.gameObject);
// Renvoie l'erreur précédemment calculée
yield return error;
// Indique que la phase de simulation est terminée
_inSimulation = false;
}
/// <summary>
/// Execute un changement aléatoire sur une solution
/// ici, une action de la séquence est tirée au hasard et remplacée
/// par une nouvelle au hasard.
/// </summary>
/// <param name="sol"></param>
public void RandomChangeInSolution(PathSolutionScript sol)
{
sol.Actions[UnityEngine.Random.Range(0, sol.Actions.Length)] = new ActionSolutionScript();
}
/// <summary>
/// Fonction utilitaire ayant pour but de copier
/// dans un nouvel espace mémoire une solution
/// </summary>
/// <param name="sol">La solution à copier</param>
/// <returns>Une copie de la solution</returns>
public PathSolutionScript CopySolution(PathSolutionScript sol)
{
// Initialisation de la nouvelle séquence d'action
// de la même longueur que celle que l'on souhaite copier
var newSol = new PathSolutionScript(sol.Actions.Length);
// Pour chaque action de la séquence originale,
// on copie le type d'action.
for (int i = 0; i < sol.Actions.Length; i++)
{
newSol.Actions[i].Action = sol.Actions[i].Action;
}
// Renvoi de la solution copiée
return newSol;
}
}
Partager