// /* * Classe statique contenant les fonctions nécessaires pour la recherche de chemin des fantômes * Créer par Charles Lachance */ using System.Drawing; namespace PathFinder { public static class PathFinder { /// /// Trouve le chemin le plus court entre le point "from" et le point "to" situé sur la grille /// de jeu "maze" en considérent les obstacles /// /// Le niveau actuel du jeu Pacman /// Le point à partir du quel on souhaite trouver un chemin /// Le point que l'on souhaite atteindre /// Retourne la direction vers laquelle on doit se diriger pour atteindre le point "to" /// en utilisant le chemin le plus court ou Direction.Undefined dans le cas où aucun chemin /// existe entre les point "to" et "from" /// public static Direction FindShortestPath(PacmanGrid maze, Point from, Point to) { //Si le point "from" est valide (dans le range du tableau)... if (from.X < maze.GetWidth() && from.Y < maze.GetHeight() && from.X >= 0 && from.Y >= 0) { //On crée un tableau représentant les distances pour se rendre à chaque point du niveau int[,] costs = new int[maze.GetWidth(), maze.GetHeight()]; //Pour chaque colonne du tableau... for (int i = 0; i < maze.GetWidth(); i++) { //Pour chaque ligne du tableau... for (int j = 0; j < maze.GetHeight(); j++) { //On initialise la distance pour se rendre à ce point à une valeur très grande costs[i, j] = int.MaxValue; } } //On initialise le point de départ avec une distance de 0 costs[from.X, from.Y] = 0; //On construit le tableau des distances BuildPaths(maze, from, costs); //On trouve la prochaine direction à emprunter pour atteindre le point "to" return RecurseFindDirection(costs, from, to); } else { return Direction.Undefined; } } /// /// Construit un tableau des distances minimales à parcourir pour atteindre une case /// quelconque du niveau à partir de la case situé au point "from". /// Prend en compte les obstacles. /// /// Le niveau dans lequel on cherche la liste des distances /// Le point à partir du quel on doit calculer les distances /// Le tableau des distances public static void BuildPaths(PacmanGrid maze, Point from, int[,] costs) { //Si la case à droite de la case actuelle fait partie du tableau et que cette case n'est pas un obstacle et que // l'on a pas déjà trouvé de chemin plus court pour atteindre cette case... if (from.X + 1 < maze.GetWidth() && maze.GetMazeElementAt(from.X + 1, from.Y) != PacmanElement.Wall && maze.GetMazeElementAt(from.X + 1, from.Y) != PacmanElement.Ghost && costs[from.X, from.Y] + 1 < costs[from.X + 1, from.Y]) { //On met à jour la distance minimal à parcourir pour atteindre cette case costs[from.X + 1, from.Y] = costs[from.X, from.Y] + 1; //On continue la recherche à partir de cette case BuildPaths(maze, new Point(from.X + 1, from.Y), costs); } //Si la case à gauche de la case actuelle fait partie du tableau et que cette case n'est pas un obstacle et que // l'on a pas déjà trouvé de chemin plus court pour atteindre cette case... if (from.X - 1 >= 0 && maze.GetMazeElementAt(from.X - 1, from.Y) != PacmanElement.Wall && maze.GetMazeElementAt(from.X - 1, from.Y) != PacmanElement.Ghost && costs[from.X, from.Y] + 1 < costs[from.X - 1, from.Y]) { //On met à jour la distance minimal à parcourir pour atteindre cette case costs[from.X - 1, from.Y] = costs[from.X, from.Y] + 1; //On continue la recherche à partir de cette case BuildPaths(maze, new Point(from.X - 1, from.Y), costs); } //Si la case en bas de la case actuelle fait partie du tableau et que cette case n'est pas un obstacle et que // l'on a pas déjà trouvé de chemin plus court pour atteindre cette case... if (from.Y + 1 < maze.GetHeight() && maze.GetMazeElementAt(from.X, from.Y + 1) != PacmanElement.Wall && maze.GetMazeElementAt(from.X, from.Y + 1) != PacmanElement.Ghost && costs[from.X, from.Y] + 1 < costs[from.X, from.Y + 1]) { //On met à jour la distance minimal à parcourir pour atteindre cette case costs[from.X, from.Y + 1] = costs[from.X, from.Y] + 1; //On continue la recherche à partir de cette case BuildPaths(maze, new Point(from.X, from.Y + 1), costs); } //Si la case en haut de la case actuelle fait partie du tableau et que cette case n'est pas un obstacle et que // l'on a pas déjà trouvé de chemin plus court pour atteindre cette case... if (from.Y - 1 >= 0 && maze.GetMazeElementAt(from.X, from.Y - 1) != PacmanElement.Wall && maze.GetMazeElementAt(from.X, from.Y - 1) != PacmanElement.Ghost && costs[from.X, from.Y] + 1 < costs[from.X, from.Y - 1]) { //On met à jour la distance minimal à parcourir pour atteindre cette case costs[from.X, from.Y - 1] = costs[from.X, from.Y] + 1; //On continue la recherche à partir de cette case BuildPaths(maze, new Point(from.X, from.Y - 1), costs); } } // // /// /// Cette fonction essaie de déterminer la première direction que le fantôme doit prendre /// pour se rendre au pacman. /// /// Tableau de distances /// Position du fantôme /// Position de la destination /// La première direction qui mène au chemin le plus court //Je n'ai pas le choix de mettre la fonction public pour faire les tests unitaires. public static Direction RecurseFindDirection(int[,] costs, Point from, Point to) { //Condition de fin: Il n'existe pas de chemin possible dans ce cas. if (costs[from.X, from.Y] == int.MaxValue) { return Direction.None; } else if (to.X < costs.GetLength(0) && to.Y < costs.GetLength(1) && to.X >= 0 && to.Y >= 0) { //Condition de fin: le fantôme est adjacent au pacman if (costs[to.X, to.Y] != 1) { //(Pour les 4 cas) Si la distance est plus petite lorsqu'on change de case, on rappèle la fonction en cette case. if (costs[to.X, to.Y] > costs[to.X + 1, to.Y]) { to.X = to.X + 1; return RecurseFindDirection(costs, from, to); } else if (costs[to.X, to.Y] > costs[to.X, to.Y + 1]) { to.Y = to.Y + 1; return RecurseFindDirection(costs, from, to); } else if (costs[to.X, to.Y] > costs[to.X - 1, to.Y]) { to.X = to.X - 1; return RecurseFindDirection(costs, from, to); } else if (costs[to.X, to.Y] > costs[to.X, to.Y - 1]) { to.Y = to.Y - 1; return RecurseFindDirection(costs, from, to); } } } //Il faut trouver la bonne case adjacente. if (to.X == from.X + 1 && to.Y == from.Y) { return Direction.East; } else if (to.X == from.X && to.Y == from.Y + 1) { return Direction.South; } else if (to.X == from.X - 1 && to.Y == from.Y) { return Direction.West; } else if (to.X == from.X && to.Y == from.Y - 1) { return Direction.North; } //Condition de fin selement pour que le code compile. return Direction.Undefined; } } // }