519 lines
18 KiB
C
519 lines
18 KiB
C
#include "solve.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#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;
|
|
}
|