#include "solve.h" #include #include #include "data_queue.h" #include "error.h" #include "maze.h" /***************************************************/ /*+ Définition et manipulation d'un chemin simple **/ /***************************************************/ sim_path* sim_emptypath(int start) { sim_path* chm = malloc(sizeof(sim_path)); if (chm == NULL) { ERROR("Erreur d'allocation de mémoire pour le chemin \n"); return NULL; } chm->start = start; chm->end = start; chm->length = 0; chm->moves = NULL; return chm; } void sim_addtopath(maze* m, move dir, sim_path* chm) { // Création d'un nouveau mouvement sim_move_seq* new_move = malloc(sizeof(sim_move_seq)); if (new_move == NULL) { ERROR("Erreur d'allocation de mémoire pour le mouvement \n"); return; } new_move->direction = dir; new_move->next = chm->moves; // Met à jour le chemin chm->moves = new_move; // Met à jour la cellule de départ int new_start = get_adj_maze(m, chm->start, (cardinal)((dir + 2) % 4)); if (new_start != -1) { chm->start = new_start; chm->length++; } else { ERROR("Mouvement invalide \n"); // Restaure le chemin et libère la mémoire chm->moves = new_move->next; free(new_move); } } sim_path* sim_copypath(sim_path* chm) { sim_path* new_chm = malloc(sizeof(sim_path)); if (new_chm == NULL) { ERROR("Erreur d'allocation de mémoire pour le chemin \n"); return NULL; } new_chm->start = chm->start; new_chm->end = chm->end; new_chm->length = chm->length; new_chm->moves = NULL; sim_move_seq* current = chm->moves; sim_move_seq* new_current = NULL; while (current != NULL) { sim_move_seq* new_move = malloc(sizeof(sim_move_seq)); if (new_move == NULL) { ERROR("Erreur d'allocation de mémoire pour le mouvement \n"); return NULL; } new_move->direction = current->direction; new_move->next = NULL; if (new_current == NULL) { new_chm->moves = new_move; } else { new_current->next = new_move; } new_current = new_move; current = current->next; } return new_chm; } void sim_freepath(sim_path* chm) { if (chm == NULL) { return; } // Libérer chaque nœud de la liste chaînée des mouvements sim_move_seq* current = chm->moves; while (current != NULL) { sim_move_seq* next = current->next; free(current); current = next; } // Libérer la structure sim_path free(chm); } /**********************************************/ /*+ Définition et manipulation d'un parcours **/ /**********************************************/ const char* salgo_names[ALG_SIZE] = {"BFS", "DFS", "A*"}; const char* sgoal_names[GOA_SIZE] = {"Trésor", "Bombe", "Poly d'algo", "Sortie"}; sim_search* sim_create_search(sim_algorithm algo, sim_goal goal) { // Allouer de la mémoire pour la nouvelle structure sim_search sim_search* search = malloc(sizeof(sim_search)); if (search == NULL) { ERROR("Erreur d'allocation de mémoire pour sim_search\n"); return NULL; } // Initialiser les champs de la structure search->algo = algo; search->goal = goal; search->search = create_dyn(); // Créer un tableau dynamique pour les sommets visités if (search->search == NULL) { ERROR("Erreur d'allocation de mémoire pour le tableau dynamique\n"); free(search); return NULL; } // Le chemin sera calculé plus tard search->path = NULL; return search; } void sim_free_search(sim_search* search) { if (search == NULL) { return; } // Libérer le tableau dynamique des sommets visités if (search->search != NULL) { free_dyn(search->search); } // Libérer le chemin trouvé if (search->path != NULL) { sim_freepath(search->path); } // Libérer la structure sim_search free(search); } /******************************************************************/ /*+ Les heuristiques (uniquement utilisées par l'algorithme A*) **/ /******************************************************************/ // Tableau des noms de stratégies (pour l'affichage) const char* heu_names[HEU_SIZE] = {"Neutre", "Manhattan", "Eviter les minotaures"}; // Tableau des fonctions heuristiques int (*heu_funs[HEU_SIZE])(game*, int, dynarray*) = {&astar_none, &astar_manhattan, &astar_runaway}; int astar_none(game* g, int cell, dynarray* goals) { // Ce comporte deja comme un BFS a cause de esti qui es incrementer de 1 a chaque fois par cost donc la priorité est assuré par le cout // Pour eviter les warnings int x1 = cell % g->m->hsize; int y1 = cell / g->m->hsize; size_dyn(goals); return y1 * 0 + x1 * 0; } int astar_manhattan(game* g, int cell, dynarray* goals) { // Récupérer les coordonnées de la cellule actuelle int x1 = cell % g->m->hsize; int y1 = cell / g->m->hsize; int min_distance = g->m->hsize * g->m->vsize; // Parcourir toutes les cellules cibles pour trouver la plus proche for (int i = 0; i < size_dyn(goals); i++) { int goal = goals->array[i]; int x2 = goal % g->m->hsize; int y2 = goal / g->m->hsize; // Calculer la distance de Manhattan int distance = abs(x1 - x2) + abs(y1 - y2); // Mettre à jour la distance minimale if (distance < min_distance) { min_distance = distance; } } return min_distance; } int astar_runaway(game* g, int cell, dynarray* goals) { // Utiliser astar_manhattan pour calculer la distance de Manhattan à la cellule cible la plus proche int min_distance = astar_manhattan(g, cell, goals); // Récupérer les coordonnées de la cellule actuelle int x1 = cell % g->m->hsize; int y1 = cell / g->m->hsize; // Ajouter une pénalité pour les minotaures proches int penalty = 0; for (int i = 0; i < g->m->nb_minotaurs; i++) { if (g->minotaurs_alive[i]) { int minotaur = g->m->minotaurs[i]; int x2 = minotaur % g->m->hsize; int y2 = minotaur / g->m->hsize; // Calculer la distance de Manhattan au minotaur int distance = abs(x1 - x2) + abs(y1 - y2); // Ajouter une pénalité inversement proportionnelle à la distance if (distance > 0) { penalty += 10 / distance; } } } return min_distance + penalty; } /*********************************/ /*+ Les algorithmes de parcours **/ /*********************************/ sim_search* (*salgo_funs[ALG_SIZE])(game*, int, sim_goal, int(heu_fun)(game*, int, dynarray*), bool) = {&sim_bfs, &sim_dfs, &sim_astar}; // Fonction pour reconstituer le chemin menant à une cellule static sim_path* reconstruct_path(maze* m, int start, int end, int* parents) { // Alloue de la mémoire pour le chemin sim_path* chm = malloc(sizeof(sim_path)); chm->start = end; chm->end = end; chm->length = 0; chm->moves = NULL; // Reconstitue le chemin en remontant depuis la cellule de fin jusqu'à la cellule de départ int current = end; while (current != start) { int parent = parents[current]; move direction; // Détermine la direction du mouvement en comparant la cellule actuelle avec son parent if (parent == current - 1) { direction = M_EAST; } else if (parent == current + 1) { direction = M_WEST; } else if (parent == current - m->hsize) { direction = M_SOUTH; } else if (parent == current + m->hsize) { direction = M_NORTH; } // Ajoute le mouvement au début du chemin sim_addtopath(m, direction, chm); current = parent; } return chm; } // Fonction de conversion de sim_goal à object static sim_goal convert_object_to_goal(object obj) { switch (obj) { case SMALLT: case MEDT: case LARGET: return GOA_TREASURE; case BOMB: return GOA_BOMB; case POLY: return GOA_POLY; case EXIT: return GOA_EXIT; default: return GOA_SIZE; // Utiliser une valeur spéciale pour indiquer qu'il n'y a pas d'objet } } sim_search* sim_bfs(game* g, int start, sim_goal goal, int (*heu_fun)(game*, int, dynarray*), bool consider_minotaurs) { maze* m = g->m; // Crée une file vide et y enfile la cellule de départ queue* q = create_queue(); enqueue(start, q); // Initialise un tableau pour garder trace des cellules visitées bool* visited = malloc(m->hsize * m->vsize * sizeof(bool)); for (int i = 0; i < m->hsize * m->vsize; i++) { visited[i] = false; } // Initialise un tableau pour garder trace des parents de chaque cellule int* parents = malloc(m->hsize * m->vsize * sizeof(int)); for (int i = 0; i < m->hsize * m->vsize; i++) { parents[i] = -2; } // Initialise un tableau dynamique pour garder l'ordre des cellules visitées dynarray* search_order = create_dyn(); sim_path* found_path = NULL; // Utilisation du paramètre heu_fun pour éviter l'avertissement de compilation if (heu_fun != NULL) { heu_fun(g, start, search_order); } // Boucle principale du DFS while (!is_empty_queue(q)) { // Défile une cellule de la file int cell = dequeue(q); // Si la cellule a déjà été visitée, passe au tour de boucle suivant if (!visited[cell]) { // Ajoute la cellule à l'ordre des cellules visitées push_dyn(cell, search_order); // Si la cellule contient l'objet recherché, reconstitue le chemin et termine le parcours if (convert_object_to_goal(get_object_maze(m, cell)) == goal) { found_path = reconstruct_path(m, start, cell, parents); break; } // Marque la cellule comme visitée visited[cell] = true; // Enfile toutes les cellules accessibles depuis la cellule actuelle for (int dir = 0; dir < 4; dir++) { int adj = get_adj_maze(m, cell, (cardinal)dir); if (adj != -1 && !visited[adj] && !has_wall_maze(m, cell, (cardinal)dir) && valid_maze(m, adj) && is_reach_maze(m, adj)) { if (((consider_minotaurs == false) || (consider_minotaurs == true && has_minotaur_maze(m, adj) == -1))) { enqueue(adj, q); parents[adj] = cell; } } } } } // Crée un objet sim_search pour stocker les résultats du parcours sim_search* result = (sim_search*)malloc(sizeof(sim_search)); result->algo = ALG_BFS; result->goal = goal; result->search = search_order; result->path = found_path; // Libère la mémoire allouée pour les tableaux visited et parents, et la file q free(visited); free(parents); delete_queue(q); return result; } sim_search* sim_dfs(game* g, int start, sim_goal goal, int (*heu_fun)(game*, int, dynarray*), bool consider_minotaurs) { maze* m = g->m; // Crée une file vide et y enfile la cellule de départ dynarray* s = create_dyn(); push_dyn(start, s); // Initialise un tableau pour garder trace des cellules visitées bool* visited = malloc(m->hsize * m->vsize * sizeof(bool)); for (int i = 0; i < m->hsize * m->vsize; i++) { visited[i] = false; } // Initialise un tableau pour garder trace des parents de chaque cellule int* parents = malloc(m->hsize * m->vsize * sizeof(int)); for (int i = 0; i < m->hsize * m->vsize; i++) { parents[i] = -2; } // Initialise un tableau dynamique pour garder l'ordre des cellules visitées dynarray* search_order = create_dyn(); sim_path* found_path = NULL; // Utilisation du paramètre heu_fun pour éviter l'avertissement de compilation if (heu_fun != NULL) { heu_fun(g, start, search_order); } // Boucle principale du BFS while (!is_empty_dyn(s)) { // Défile une cellule de la file int cell = pop_dyn(s); // Si la cellule a déjà été visitée, passe au tour de boucle suivant if (!visited[cell]) { // Ajoute la cellule à l'ordre des cellules visitées push_dyn(cell, search_order); // Si la cellule contient l'objet recherché, reconstitue le chemin et termine le parcours if (convert_object_to_goal(get_object_maze(m, cell)) == goal) { found_path = reconstruct_path(m, start, cell, parents); break; } // Marque la cellule comme visitée visited[cell] = true; // Enfile toutes les cellules accessibles depuis la cellule actuelle for (int dir = 0; dir < 4; dir++) { int adj = get_adj_maze(m, cell, (cardinal)dir); if (adj != -1 && !visited[adj] && !has_wall_maze(m, cell, (cardinal)dir) && valid_maze(m, adj) && is_reach_maze(m, adj)) { if (((consider_minotaurs == false) || (consider_minotaurs == true && has_minotaur_maze(m, adj) == -1))) { push_dyn(adj, s); parents[adj] = cell; } } } } } // Crée un objet sim_search pour stocker les résultats du parcours sim_search* result = (sim_search*)malloc(sizeof(sim_search)); result->algo = ALG_DFS; result->goal = goal; result->search = search_order; result->path = found_path; // Libère la mémoire allouée pour les tableaux visited et parents, et la file q free(visited); free(parents); free_dyn(s); return result; } // Structure pour stocker les informations nécessaires pour chaque cellule dans le tas binaire typedef struct { int cell; // Numéro de la cellule int cost; // Coût pour atteindre cette cellule int esti; // Estimation du coût total (cost + heuristique) } astar_node; // Fonction de comparaison pour le tas binaire static int compare_astar_nodes(void* a, void* b) { astar_node* node_a = (astar_node*)a; astar_node* node_b = (astar_node*)b; return node_a->esti - node_b->esti; } sim_search* sim_astar(game* g, int start, sim_goal goal, int (*heuristique)(game*, int, dynarray*), bool consider_minotaurs) { maze* m = g->m; // Crée un tas binaire vide binheap* open_set = create_binheap(compare_astar_nodes); // Initialise un tableau pour garder trace des cellules visitées bool* visited = malloc(m->hsize * m->vsize * sizeof(bool)); // Initialise un tableau pour garder trace des parents de chaque cellule int* parents = malloc(m->hsize * m->vsize * sizeof(int)); // Initialise un tableau pour garder trace des coûts pour atteindre chaque cellule int* cost = malloc(m->hsize * m->vsize * sizeof(int)); for (int i = 0; i < m->hsize * m->vsize; i++) { visited[i] = false; parents[i] = -2; cost[i] = m->hsize * m->vsize; } // Initialise un tableau dynamique pour garder l'ordre des cellules visitées dynarray* search_order = create_dyn(); sim_path* found_path = NULL; dynarray* goals = create_dyn(); for (int i = 0; i < m->hsize * m->vsize; i++) { if (convert_object_to_goal(get_object_maze(m, i)) == goal) { push_dyn(i, goals); } } // Initialisation de la cellule de départ astar_node* start_node = malloc(sizeof(astar_node)); start_node->cell = start; start_node->cost = 0; start_node->esti = heuristique(g, start, goals); push_binheap(open_set, start_node); cost[start] = 0; // Parcours du labyrinthe while (!isempty_binheap(open_set)) { astar_node* current_node = popmin_binheap(open_set); int current = current_node->cell; free(current_node); if (!visited[current]) { // Ajoute la cellule à l'ordre des cellules visitées push_dyn(current, search_order); visited[current] = true; // Vérifie si l'objectif est atteint if (convert_object_to_goal(get_object_maze(m, current)) == goal) { found_path = reconstruct_path(m, start, current, parents); break; } // Exploration des cellules adjacentes for (int dir = 0; dir < 4; dir++) { int adj = get_adj_maze(m, current, (cardinal)dir); if (adj != -1 && !visited[adj] && !has_wall_maze(m, current, (cardinal)dir) && valid_maze(m, adj) && is_reach_maze(m, adj)) { if (((consider_minotaurs == false) || (consider_minotaurs == true && has_minotaur_maze(m, adj) == -1))) { int new_cost = cost[current] + 1; if (new_cost < cost[adj]) { cost[adj] = new_cost; parents[adj] = current; astar_node* adj_node = malloc(sizeof(astar_node)); adj_node->cell = adj; adj_node->cost = new_cost; adj_node->esti = new_cost + heuristique(g, adj, goals); push_binheap(open_set, adj_node); } } } } } } sim_search* search_result = sim_create_search(ALG_AST, goal); search_result->path = found_path; search_result->search = search_order; free(visited); free(cost); free(parents); delete_binheap(open_set); return search_result; }