#include "maze_gen.h" #include #include #include "data_queue.h" void (*gen_funs[GEN_SIZE])(maze*) = {&maze_test, &random_maze_ab, &random_maze_wilson, &random_maze_hklinear, &random_maze_hkdfs, &random_maze_hkrandom, &random_maze_prim, &random_maze_kruskal, &random_maze_rec}; const char* gen_names[GEN_SIZE] = {"Test (pas de génération)", "Aldous-Broder", "Wilson", "Hunt & Kill: Linéaire", "Hunt & Kill: DFS", "Hunt & Kill: Aléatoire", "Prim", "Kruskal", "Récursif"}; // FONCTIONS AUXILLIAIRES // Fonction auxilliaire qui retourne vrai si la cellule à au moins une cellule adjacente non visitée accessible static bool has_adj_no_visited(maze* lab, bool* visited, int cell) { for (int i = 0; i < 4; i++) { int adj = get_adj_maze(lab, cell, i); if (adj != -1 && is_reach_maze(lab, adj) && !visited[get_adj_maze(lab, cell, i)]) { return true; } } return false; } // Fonction qui renvoie la hsize (taille horizontale) d'un labyrinthe static int get_hsize(maze* lab) { return lab->hsize; } // Fonction qui renvoie la vsize (taille verticale) d'un labyrinthe static int get_vsize(maze* lab) { return lab->vsize; } // Fonction auxilliaire pour la boucle kill de la génération de labyrinthe hunt & kill et pour le choix de la cellule de Wilson static int boucle_kill_direction(maze* lab, bool* visited, int cell) { int dir[4] = {0, 1, 2, 3}; // Tableau des directions possibles int size = 4; bool found_dir = false; // Tant qu'il reste des directions possibles et qu'on n'en a pas tiré de valide while (size > 0 && !found_dir) { const int r = rand() % size; int c = dir[r]; // On tire une direction aléatoire // On récupère la cellule adjacente dans la direction tirée int new_cell = get_adj_maze(lab, cell, c); // Si on peut se déplacer dans la direction tirée et que la cellule n'a pas été visitée et est accessible if (new_cell != -1 && valid_maze(lab, new_cell) && is_reach_maze(lab, new_cell) && !visited[new_cell]) { // On a trouvé une direction valide found_dir = true; del_wall_maze(lab, cell, c); // On détruit le mur cell = new_cell; // On se déplace } // Sinon on supprime la direction prise du tableau et on décalle les directions pour recommencer avec les directions restantes for (int i = r; i < size - 1; i++) { dir[i] = dir[i + 1]; } size--; } return cell; } // Fonction auxilliaire qui renvoie une cellule de départ aléatoire static int get_start_cell(maze* lab) { // On récupère les dimensions du labyrinthe int hsize = get_hsize(lab); // Taille horizontale int vsize = get_vsize(lab); // Taille verticale // Initialisation de la cellule de départ int cell = rand() % (hsize * vsize); // On vérifie que la cellule est accessible int* tab = malloc(sizeof(int) * hsize * vsize); for (int i = 0; i < hsize * vsize; i++) { tab[i] = i; } int count = hsize * vsize; while (!is_reach_maze(lab, cell)) { // On choisit une cellule aléatoire int ind_rand = rand() % count; cell = tab[ind_rand]; if (!is_reach_maze(lab, cell)) { // On échange la cellule choisie avec la dernière cellule du tableau tab[ind_rand] = tab[count - 1]; count--; } } free(tab); return cell; } // FONCTIONS DE GENERATION DU .h void maze_test(maze* lab) { if (lab == NULL) { ERROR("Pointeur de labyrinthe null"); } } void random_maze_ab(maze* lab) { if (lab == NULL) { ERROR("Pointeur de labyrinthe null"); } // On récupère les dimensions du labyrinthe int hsize = get_hsize(lab); // Taille horizontale int vsize = get_vsize(lab); // Taille verticale // Création d'un tableau repertoriant si une cellule a été visitée bool* visited = malloc(hsize * vsize * sizeof(bool)); for (int i = 0; i < hsize * vsize; i++) { visited[i] = false; } // Création d'un compteur du nombre de cellules visitées int nb_visited = 0; // Initialisation de la cellule de départ int cell = get_start_cell(lab); // Cette cellule est visitée visited[cell] = true; nb_visited++; // Tant que toutes les cellules accessibles n'ont pas été visitées while (nb_visited != lab->nb_reachable) { // On choisit une direction aléatoire int dir = rand() % 4; // On récupère la cellule adjacente dans la direction tirée int cell_adj = get_adj_maze(lab, cell, dir); // Si on peut se déplacer dans la direction tirée et que la cellule n'a pas été visitée et est accessible // On détruit le mur entre les deux cellules if (cell_adj != -1 && valid_maze(lab, cell_adj) && is_reach_maze(lab, cell_adj) && !visited[cell_adj]) { del_wall_maze(lab, cell, dir); visited[cell_adj] = true; // On marque la cellule adjacente comme visitée nb_visited++; // On incrémente le nombre de cellules visitées cell = cell_adj; // On se déplace } // Sinon si la cellule adjacente est accessible mais a déjà été visitée else if (cell_adj != -1 && valid_maze(lab, cell_adj) && is_reach_maze(lab, cell_adj)) { cell = cell_adj; } } free(visited); } void random_maze_wilson(maze* lab) { if (lab == NULL) { ERROR("Pointeur de labyrinthe null"); } // On récupère les dimensions du labyrinthe int hsize = get_hsize(lab); // Taille horizontale int vsize = get_vsize(lab); // Taille verticale // Création d'un tableau repertoriant si une cellule a été visitée bool* visited = malloc(hsize * vsize * sizeof(bool)); for (int i = 0; i < hsize * vsize; i++) { visited[i] = false; } // Création d'une files pour stocker la marche aléatoire queue* queue_walk = create_queue(); // Création d'un compteur du nombre de cellules visitées int nb_visited = 0; // Initialisation de la cellule de départ int cell_start = get_start_cell(lab); // Cette cellule est visitée visited[cell_start] = true; nb_visited++; // Tant que toutes les cellules accessibles n'ont pas été visitées while (nb_visited != lab->nb_reachable) { int cell_walk = cell_start; // On choisit aléatoirement une cellule non visitée while (visited[cell_walk]) { cell_walk = get_start_cell(lab); } enqueue(cell_walk, queue_walk); // On effectue une marche aléatoire jusqu'à atteindre une cellule visitée while (!visited[cell_walk]) { // On choisit une direction aléatoire int dir = rand() % 4; // On récupère la cellule adjacente dans la direction tirée int cell_adj = get_adj_maze(lab, cell_walk, dir); // Si on peut se déplacer dans la direction tirée et que la cellule est accessible if (cell_adj != -1 && valid_maze(lab, cell_adj) && is_reach_maze(lab, cell_adj)) { cell_walk = cell_adj; // On se déplace enqueue(cell_walk, queue_walk); // On ajoute la cellule à la marche aléatoire } } // On élime les cycles de la marche aléatoire on part du début de la marche aléatoire // Pour chaque cellule si elle revient dans la marche aléatoire on supprime les cellules entre les deux // On marque les cellules de la marche aléatoire comme visitées et on casse les murs int cell_che = dequeue(queue_walk); while (!is_empty_queue(queue_walk)) { if (!visited[cell_che]) { visited[cell_che] = true; nb_visited++; } // Si elle revient dans la marche aléatoire ou dequeue toutes les cellules entre les deux bool found_in_walk = false; int count = 0; for (int i = 0; i < getsize_queue(queue_walk); i++) { if (read_queue(queue_walk, i) == cell_che) { found_in_walk = true; count = i; break; } } if (found_in_walk) { while (getsize_queue(queue_walk) != count) { dequeue(queue_walk); } } if (!is_empty_queue(queue_walk)) { // On récupère la cellule suivante et on casse le mur entre les deux int next_cell = dequeue(queue_walk); for (int dir = 0; dir < 4; dir++) { if (get_adj_maze(lab, cell_che, dir) == next_cell) { del_wall_maze(lab, cell_che, dir); } } cell_che = next_cell; } } // On marque la dernière cellule de la marche aléatoire comme visitée if (!visited[cell_che]) { visited[cell_che] = true; nb_visited++; } } free(visited); delete_queue(queue_walk); } void random_maze_hkdfs(maze* lab) { if (lab == NULL) { ERROR("Pointeur de labyrinthe null"); } // Récupération des dimensions du labyrinthe int hsize = get_hsize(lab); // Taille horizontale int vsize = get_vsize(lab); // Taille verticale // Création d'un tableau repertoriant si une cellule a été visitée bool* visited = malloc(hsize * vsize * sizeof(bool)); for (int i = 0; i < hsize * vsize; i++) { visited[i] = false; } // Création d'un compteur du nombre de cellules visitées int nb_visited = 0; // Initialisation d'une pile stockant les cellules visitées pour le DFS dynarray* stack = create_dyn(); // Initialisation de la cellule de départ int cell = get_start_cell(lab); // Cette cellule est visitée visited[cell] = true; nb_visited++; push_dyn(cell, stack); // On empile la cellule // Tant que toutes les cellules accessibles n'ont pas été visitées while (nb_visited != lab->nb_reachable) { // BOUCLE HUNT // On regarde si la dernière cellule visitée à une voisin non visitée jusqu'à trouver une cellule bool found_hunt = false; while (!found_hunt) { int hunt = pop_dyn(stack); // On dépile la cellule // On regarde si une de ses voisines n'a pas été visitée si c'est le cas, on se déplace dans cette cellule if (has_adj_no_visited(lab, visited, hunt)) { cell = hunt; found_hunt = true; } } push_dyn(cell, stack); // On empile la cellule // BOUCLE KILL // Détruire un mur aléatoire tant que toutes les cellules voisines n'ont pas été visitées pour créer un long couloir/chemin while (has_adj_no_visited(lab, visited, cell)) { // Tant que cell a une voisine non visitée // On recupère une cellule adjacente non visitée cell = boucle_kill_direction(lab, visited, cell); visited[cell] = true; nb_visited++; push_dyn(cell, stack); } } free(visited); free_dyn(stack); } void random_maze_hkrandom(maze* lab) { if (lab == NULL) { ERROR("Pointeur de labyrinthe null"); } // Récupération des dimensions du labyrinthe int hsize = get_hsize(lab); // Taille horizontale int vsize = get_vsize(lab); // Taille verticale // Création d'un tableau repertoriant si une cellule a été visitée bool* visited = malloc(hsize * vsize * sizeof(bool)); for (int i = 0; i < hsize * vsize; i++) { visited[i] = false; } // Création d'un tableau repetoriant les cellules visitées et un compteur qui permet de savoir combien de cellules ont été visitées et avec ou sans // Voisins adjacents int* visited_cells = malloc(hsize * vsize * sizeof(int)); int nb_visited = 0; int nb_visited_total = 0; // Initialisation de la cellule de départ int cell = rand() % (hsize * vsize); // On vérifie que la cellule est accessible int* tab = malloc(sizeof(int) * hsize * vsize); for (int i = 0; i < hsize * vsize; i++) { tab[i] = i; } int count = hsize * vsize; while (!is_reach_maze(lab, cell)) { // On choisit une cellule aléatoire int ind_rand = rand() % count; cell = tab[ind_rand]; if (!is_reach_maze(lab, cell)) { // On échange la cellule choisie avec la dernière cellule du tableau tab[ind_rand] = tab[count - 1]; count--; } } free(tab); // Cette cellule est visitée visited[cell] = true; visited_cells[0] = cell; nb_visited++; nb_visited_total++; // Tant que toutes les cellules n'ont pas été visitées while (nb_visited_total != lab->nb_reachable) { // BOUCLE HUNT // On choisit une cellule aléatoire parmis les cellules visitées bool found_hunt = false; while (!found_hunt) { // On tire une cellule aléatoire parmis le tableau des cellules // Visitées int ind_hunt = rand() % nb_visited; // On regarde si une de ses voisines n'a pas été visitée si c'est le // Cas, on se déplace dans cette cellule if (has_adj_no_visited(lab, visited, visited_cells[ind_hunt])) { cell = visited_cells[ind_hunt]; found_hunt = true; } else { // La cellule n'a pas de voisins non visités // On la retire du tableau des cellules visitées car on n'a plus besoin de la tester visited_cells[ind_hunt] = visited_cells[nb_visited - 1]; nb_visited--; } } // BOUCLE KILL // Détruire un mur aléatoire tant que toutes les cellules voisines n'ont pas été visitées pour créer un long couloir/chemin while (has_adj_no_visited(lab, visited, cell)) { // Tant que cell a une voisine non visitée // On recupère une cellule adjacente non visitée cell = boucle_kill_direction(lab, visited, cell); visited[cell] = true; visited_cells[nb_visited] = cell; nb_visited++; nb_visited_total++; } } free(visited); free(visited_cells); } void random_maze_hklinear(maze* lab) { if (lab == NULL) { ERROR("Pointeur de labyrinthe null"); } // Récupération des dimensions du labyrinthe int hsize = get_hsize(lab); // Taille horizontale int vsize = get_vsize(lab); // Taille verticale // Création d'un tableau repertoriant si une cellule a été visitée bool* visited = malloc(hsize * vsize * sizeof(bool)); for (int i = 0; i < hsize * vsize; i++) { visited[i] = false; } // Création d'un compteur du nombre de cellules visitées int nb_visited = 0; // Initialisation de la cellule de départ int cell = get_start_cell(lab); // Cette cellule est visitée visited[cell] = true; nb_visited++; // Tant que toutes les cellules n'ont pas été visitées while (nb_visited != lab->nb_reachable) { // BOUCLE HUNT // On parcourt toutes les cellules bool found_hunt = false; int hunt = 0; while (!found_hunt && hunt < hsize * vsize) { // Si la cellule a été visitée on regarde si une de ses voisines n'a pas été visitée si c'est le cas, on se déplace dans cette cellule if (visited[hunt]) { if (has_adj_no_visited(lab, visited, hunt)) { cell = hunt; found_hunt = true; } } hunt++; } // BOUCLE KILL // Détruire un mur aléatoire tant que toutes les cellules voisines n'ont pas été visitées pour créer un long couloir/chemin while (has_adj_no_visited(lab, visited, cell)) { // Tant que cell a une voisine non visitée // On recupère une cellule adjacente non visitée cell = boucle_kill_direction(lab, visited, cell); visited[cell] = true; // On marque la cellule comme visitée nb_visited++; // On incrémente le nombre de cellules visitées } } free(visited); } // Structure pour représenter une arête typedef struct { int cell1; // Première cellule de l'arête int cell2; // Seconde cellule de l'arête cardinal dir; // Direction de l'arête } edge; // Fonction pour ajouter une arête à la liste des arêtes à traiter static void add_edges(maze* m, int cell, edge* edges, int* edge_count) { for (int dir = 0; dir < 4; dir++) { int adj_cell = get_adj_maze(m, cell, dir); if (adj_cell != -1 && valid_maze(m, adj_cell) && is_reach_maze(m, adj_cell)) { edges[*edge_count].cell1 = cell; edges[*edge_count].cell2 = adj_cell; edges[*edge_count].dir = dir; (*edge_count)++; } } } void random_maze_prim(maze* m) { srand(time(NULL)); int total_cells = m->hsize * m->vsize; bool* visited = (bool*)calloc(total_cells, sizeof(bool)); edge* edges = (edge*)malloc(total_cells * 4 * sizeof(edge)); // Maximum 4 arêtes par cellule int edge_count = 0; // Initialisation de la cellule de départ int start_cell = get_start_cell(m); visited[start_cell] = true; // Ajouter toutes les arêtes issues de cette cellule à l'ensemble des arêtes à traiter add_edges(m, start_cell, edges, &edge_count); // Tant qu'il reste des arêtes à traiter while (edge_count > 0) { // Choisir une arête aléatoirement et la retirer de l'ensemble des arêtes à traiter int rand_index = rand() % edge_count; edge chosen_edge = edges[rand_index]; edges[rand_index] = edges[--edge_count]; int cell1 = chosen_edge.cell1; int cell2 = chosen_edge.cell2; cardinal dir = chosen_edge.dir; // Si la cellule vers laquelle mène cette arête n'a pas encore été visitée if (!visited[cell2]) { // Casser le mur correspondant à l'arête del_wall_maze(m, cell1, dir); del_wall_maze(m, cell2, (dir + 2) % 4); // Casser le mur opposé fflush(stdout); // Ajouter la cellule à l'ensemble des cellules visitées visited[cell2] = true; // Ajouter toutes les arêtes issues de cette cellule à l'ensemble des arêtes à traiter add_edges(m, cell2, edges, &edge_count); } } free(visited); free(edges); } void random_maze_kruskal(maze* lab) { if (lab == NULL) { ERROR("Pointeur de labyrinthe null"); } // On crée une partition triviale de l'enemble des cellules ufind* uf_cell = create_ufind(lab->hsize * lab->vsize); // On crée un tableau pour stocker les arêtes edge* tab_edges = malloc(lab->hsize * lab->vsize * 4 * sizeof(edge)); // Maximum 4 arêtes par cellule // On fait parcours toutes les cellules pour ajouter les arêtes entre les cellules accessibles int edge_count = 0; for (int i = 0; i < lab->hsize * lab->vsize; i++) { // On ajoute les arêtes de la cellule i si i est accessible if (is_reach_maze(lab, i)) { add_edges(lab, i, tab_edges, &edge_count); } } // Tant qu'il reste des arêtes à traiter while (edge_count > 0) { // Choisir une arête aléatoirement et la retirer de l'ensemble des arêtes à traiter int rand_index = rand() % edge_count; edge chosen_edge = tab_edges[rand_index]; // On remplace l'arête choisie par la dernière arête du tableau tab_edges[rand_index] = tab_edges[edge_count - 1]; edge_count--; // On récupère les cellules et la direction de l'arête int cell1 = chosen_edge.cell1; int cell2 = chosen_edge.cell2; cardinal dir = chosen_edge.dir; // Cell1 -> cell2 // Si les deux cellules ne sont pas dans la même classe if (find_ufind(cell1, uf_cell) != find_ufind(cell2, uf_cell)) { // Casser le mur correspondant à l'arête del_wall_maze(lab, cell1, dir); // Fusionner les deux classes union_ufind(cell1, cell2, uf_cell); } } // On free la partition et le tableau des arêtes delete_ufind(uf_cell); free(tab_edges); } void random_maze_rec(maze* m) { // Initialiser le générateur de nombres aléatoires srand(time(NULL)); // Définir une pile pour gérer la récursion manuellement typedef struct { int x, y, width, height; } Region; // Taille maximale de la pile (au pire des cas, on divise chaque cellule individuellement) int max_stack_size = m->hsize * m->vsize; Region* stack = (Region*)malloc(max_stack_size * sizeof(Region)); int stack_size = 0; // Pousser la région initiale sur la pile stack[stack_size++] = (Region){0, 0, m->hsize, m->vsize}; // Boucle principale pour traiter la pile while (stack_size > 0) { // Pop une région de la pile Region current = stack[--stack_size]; // Cas de base: si la région est réduite à une seule cellule, on ne fait rien if (current.width <= 1 && current.height <= 1) { continue; } // Choisir de diviser horizontalement ou verticalement if (current.height >= current.width) { // Diviser horizontalement int divide_line = current.y + current.height / 2; // Pousser les deux nouvelles régions sur la pile stack[stack_size++] = (Region){current.x, current.y, current.width, divide_line - current.y}; stack[stack_size++] = (Region){current.x, divide_line, current.width, current.y + current.height - divide_line}; // Casser un mur aléatoirement sur la ligne de séparation int random_x = current.x + rand() % current.width; del_wall_maze(m, divide_line * m->hsize + random_x, NORTH); } else { // Diviser verticalement int divide_column = current.x + current.width / 2; // Pousser les deux nouvelles régions sur la pile stack[stack_size++] = (Region){current.x, current.y, divide_column - current.x, current.height}; stack[stack_size++] = (Region){divide_column, current.y, current.x + current.width - divide_column, current.height}; // Casser un mur aléatoirement sur la colonne de séparation int random_y = current.y + rand() % current.height; del_wall_maze(m, random_y * m->hsize + divide_column, WEST); } } // Libérer la mémoire allouée pour la pile free(stack); }