Files
algo-labyrinthes-giiiiive/Aloyse et Anna/maze_gen.c
Anna Mielcarek a4703085b6 Aloyse et Anna
j'ai push pas mal de fichier envoyer un message si vous en voulez un aute
2024-12-12 16:22:28 +01:00

596 lines
23 KiB
C

#include "maze_gen.h"
#include <stdio.h>
#include <time.h>
#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);
}