364 lines
11 KiB
C++
364 lines
11 KiB
C++
#include "../includes/mysok.h"
|
|
|
|
sok_board_t::sok_board_t() {
|
|
for (int i = 0; i < NBL; ++i) {
|
|
for (int j = 0; j < NBC; ++j) {
|
|
board[i][j] = FREE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void sok_board_t::print_board() {
|
|
for (int i = 0; i < board_nbl; ++i) {
|
|
for (int j = 0; j < NBC; ++j) {
|
|
if (board[i][j] == END_OF_LINE) {
|
|
break;
|
|
}
|
|
|
|
printf("%c", board[i][j]);
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
void sok_board_t::load(char *_file) {
|
|
FILE *fp = fopen(_file, "r");
|
|
char *line = NULL;
|
|
size_t len = 0;
|
|
ssize_t nread;
|
|
|
|
if (fp == NULL) {
|
|
perror("fopen");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
num_crates_free = 0;
|
|
board_nbl = 0;
|
|
while ((nread = getline(&line, &len, fp)) != -1) {
|
|
if ((static_cast<int>(nread)) > 0) {
|
|
bool read_ok = false;
|
|
for (int i = 0; i < nread; ++i) {
|
|
switch (line[i]) {
|
|
case (FREE): {
|
|
board[board_nbl][i] = FREE;
|
|
break;
|
|
}
|
|
case (TARGET): {
|
|
board[board_nbl][i] = TARGET;
|
|
break;
|
|
}
|
|
case (WALL): {
|
|
read_ok = true;
|
|
board[board_nbl][i] = WALL;
|
|
break;
|
|
}
|
|
case (CRATE_ON_FREE): {
|
|
board[board_nbl][i] = CRATE_ON_FREE;
|
|
++num_crates_free;
|
|
break;
|
|
}
|
|
case (CRATE_ON_TARGET): {
|
|
board[board_nbl][i] = CRATE_ON_TARGET;
|
|
break;
|
|
}
|
|
case (MAN1_ON_FREE): {
|
|
man1_x = board_nbl;
|
|
man1_y = i;
|
|
board[board_nbl][i] = MAN1_ON_FREE;
|
|
break;
|
|
}
|
|
case (MAN1_ON_TARGET): {
|
|
man1_x = board_nbl;
|
|
man1_y = i;
|
|
board[board_nbl][i] = MAN1_ON_TARGET;
|
|
break;
|
|
}
|
|
case (MAN2_ON_FREE): {
|
|
man2_x = board_nbl;
|
|
man2_y = i;
|
|
board[board_nbl][i] = MAN2_ON_FREE;
|
|
break;
|
|
}
|
|
case (MAN2_ON_TARGET): {
|
|
man2_x = board_nbl;
|
|
man2_y = i;
|
|
board[board_nbl][i] = MAN2_ON_TARGET;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (read_ok) {
|
|
board[board_nbl][nread - 1] = END_OF_LINE;
|
|
++board_nbl;
|
|
}
|
|
}
|
|
}
|
|
|
|
free(line);
|
|
fclose(fp);
|
|
}
|
|
|
|
bool is_in_history(Node current, std::vector<Node> history) {
|
|
// printf("Taille history : %d\n", history.size());
|
|
|
|
for (auto &noeud : history) {
|
|
|
|
int c = 0;
|
|
for (int l = 0; l < NBL; ++l) {
|
|
for (int m = 0; m < NBC; ++m) {
|
|
if (current.state.board[l][m] != noeud.state.board[l][m])
|
|
c++;
|
|
}
|
|
}
|
|
|
|
if (c == 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int bfs(Node &result) {
|
|
// Création de la queue de BFS
|
|
std::queue<Node> q;
|
|
|
|
// Ajout de l'état initial à la queue
|
|
Node first;
|
|
first.state = result.state;
|
|
first.path_len = 0;
|
|
first.path = "";
|
|
q.push(first);
|
|
|
|
// Boucle principale de la recherche BFS
|
|
while (!q.empty()) {
|
|
// Retrait de l'état en tête de la queue
|
|
Node cur = q.front();
|
|
q.pop();
|
|
|
|
// Si l'état actuel est celui souhaité (toutes les caisses sur des cibles)
|
|
if (cur.state.num_crates_free <= 0) {
|
|
result = cur;
|
|
return 0;
|
|
}
|
|
|
|
// Parcours des déplacements possibles à partir de l'état actuel
|
|
for (int i = 0; i < dsize - 1; ++i) {
|
|
// Position de la case du joueur
|
|
int man1_x = cur.state.man1_x;
|
|
int man1_y = cur.state.man1_y;
|
|
|
|
// Position du joueur après déplacement
|
|
int new_man1_x = cur.state.man1_x + dx[i];
|
|
int new_man1_y = cur.state.man1_y + dy[i];
|
|
|
|
// Si la case devant le joueur est un mur
|
|
if (cur.state.board[new_man1_x][new_man1_y] == WALL) {
|
|
continue;
|
|
}
|
|
|
|
// Si la case devant le joueur est libre
|
|
if (cur.state.board[new_man1_x][new_man1_y] == FREE ||
|
|
cur.state.board[new_man1_x][new_man1_y] == TARGET) {
|
|
// On déplace le joueur dans le prochain état
|
|
sok_board_t next = cur.state;
|
|
|
|
// Déplacement joueur, ancienne position
|
|
next.board[man1_x][man1_y] =
|
|
next.board[man1_x][man1_y] == MAN1_ON_TARGET ? TARGET : FREE;
|
|
// Déplacement joueur, nouvelle position
|
|
next.board[new_man1_x][new_man1_y] =
|
|
next.board[new_man1_x][new_man1_y] == TARGET ? MAN1_ON_TARGET
|
|
: MAN1_ON_FREE;
|
|
next.man1_x = new_man1_x;
|
|
next.man1_y = new_man1_y;
|
|
|
|
// On ajoute cet état suivant à la queue
|
|
Node next_bfs;
|
|
next_bfs.state = next;
|
|
next_bfs.path_len = cur.path_len + 1;
|
|
next_bfs.path = cur.path + move_str[i] + " ";
|
|
q.push(next_bfs);
|
|
}
|
|
|
|
// Si la case devant le joueur est une caisse
|
|
if (cur.state.board[new_man1_x][new_man1_y] == CRATE_ON_FREE ||
|
|
cur.state.board[new_man1_x][new_man1_y] == CRATE_ON_TARGET) {
|
|
// Position de la caisse
|
|
int cx = new_man1_x;
|
|
int cy = new_man1_y;
|
|
// Position de la caisse après déplacement
|
|
int new_cx = cx + dx[i];
|
|
int new_cy = cy + dy[i];
|
|
|
|
// Si la case derrière la caisse n'est pas accessible
|
|
if (cur.state.board[new_cx][new_cy] != TARGET &&
|
|
cur.state.board[new_cx][new_cy] != FREE) {
|
|
continue;
|
|
}
|
|
|
|
// On déplace la caisse et le joueur
|
|
sok_board_t next = cur.state;
|
|
|
|
// Déplacement joueur ancienne position
|
|
next.board[man1_x][man1_y] =
|
|
next.board[man1_x][man1_y] == MAN1_ON_TARGET ? TARGET : FREE;
|
|
|
|
// Déplacement caisse, anciennne position, ie. nouvelle position joueur
|
|
next.board[cx][cy] = next.board[cx][cy] == CRATE_ON_TARGET
|
|
? MAN1_ON_TARGET
|
|
: MAN1_ON_FREE;
|
|
next.man1_x = new_man1_x;
|
|
next.man1_y = new_man1_y;
|
|
// S'éloigne de l'objectif si on déplace une caisse d'une cible
|
|
if (next.board[cx][cy] == MAN1_ON_TARGET) {
|
|
++next.num_crates_free;
|
|
}
|
|
|
|
// Déplacement caisse, nouvelle position
|
|
next.board[new_cx][new_cy] = next.board[new_cx][new_cy] == TARGET
|
|
? CRATE_ON_TARGET
|
|
: CRATE_ON_FREE;
|
|
// S'approche de l'objectif si on place une caisse sur la cible
|
|
if (next.board[new_cx][new_cy] == CRATE_ON_TARGET) {
|
|
--next.num_crates_free;
|
|
}
|
|
|
|
// On ajoute l'état suivant à la queue
|
|
Node next_bfs;
|
|
next_bfs.state = next;
|
|
next_bfs.path_len = cur.path_len + 1;
|
|
next_bfs.path = cur.path + move_str[i] + " ";
|
|
q.push(next_bfs);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Si la queue est vide et qu'on n'a pas trouvé de solution, c'est qu'il n'y
|
|
// en a pas
|
|
return -1;
|
|
}
|
|
|
|
int dfs(Node &result, int depth, std::vector<Node> history) {
|
|
|
|
// Si on a atteint la profondeur maximale, on arrête la recherche
|
|
if (depth == MAX_DEPTH) {
|
|
/* std::cout << "max\n"; */
|
|
return -1;
|
|
}
|
|
|
|
// Si l'état actuel est celui souhaité (toutes les caisses sur des cibles)
|
|
if (result.state.num_crates_free <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
// Parcours des déplacements possibles à partir de l'état actuel
|
|
for (int i = 0; i < dsize - 1; ++i) {
|
|
// Position de la case du joueur
|
|
int man1_x = result.state.man1_x;
|
|
int man1_y = result.state.man1_y;
|
|
|
|
// Position du joueur après déplacement
|
|
int new_man1_x = result.state.man1_x + dx[i];
|
|
int new_man1_y = result.state.man1_y + dy[i];
|
|
|
|
// Si la case devant le joueur est un mur, on ignore ce mouvement
|
|
if (result.state.board[new_man1_x][new_man1_y] == WALL) {
|
|
continue;
|
|
}
|
|
|
|
// Si la case devant le joueur est libre ou une cible, on déplace le joueur
|
|
if (result.state.board[new_man1_x][new_man1_y] == FREE ||
|
|
result.state.board[new_man1_x][new_man1_y] == TARGET) {
|
|
// On déplace le joueur dans le prochain état
|
|
sok_board_t next = result.state;
|
|
|
|
// Déplacement joueur, ancienne position
|
|
next.board[man1_x][man1_y] =
|
|
next.board[man1_x][man1_y] == MAN1_ON_TARGET ? TARGET : FREE;
|
|
// Déplacement joueur, nouvelle position
|
|
next.board[new_man1_x][new_man1_y] =
|
|
next.board[new_man1_x][new_man1_y] == TARGET ? MAN1_ON_TARGET
|
|
: MAN1_ON_FREE;
|
|
next.man1_x = new_man1_x;
|
|
next.man1_y = new_man1_y;
|
|
|
|
// On ajoute cet état suivant à la pile
|
|
Node next_dfs;
|
|
next_dfs.state = next;
|
|
next_dfs.path_len = result.path_len + 1;
|
|
next_dfs.path = result.path + move_str[i] + " ";
|
|
|
|
if (!is_in_history(next_dfs, history)) {
|
|
history.push_back(next_dfs);
|
|
int res = dfs(next_dfs, depth + 1, history);
|
|
if (res == 0) {
|
|
result = next_dfs;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Si la case devant le joueur est une caisse, on déplace la caisse et le
|
|
// joueur
|
|
if (result.state.board[new_man1_x][new_man1_y] == CRATE_ON_FREE ||
|
|
result.state.board[new_man1_x][new_man1_y] == CRATE_ON_TARGET) {
|
|
// Position de la caisse
|
|
int cx = new_man1_x;
|
|
int cy = new_man1_y;
|
|
// Position de la caisse après déplacement
|
|
int new_cx = cx + dx[i];
|
|
int new_cy = cy + dy[i];
|
|
|
|
// Si la case derrière la caisse n'est pas accessible, on ignore ce
|
|
// mouvement
|
|
if (result.state.board[new_cx][new_cy] != TARGET &&
|
|
result.state.board[new_cx][new_cy] != FREE) {
|
|
continue;
|
|
}
|
|
|
|
// On déplace la caisse et le joueur dans le prochain état
|
|
sok_board_t next = result.state;
|
|
|
|
// Déplacement caisse, ancienne position
|
|
if (next.board[cx][cy] == CRATE_ON_TARGET) {
|
|
++next.num_crates_free;
|
|
}
|
|
next.board[cx][cy] =
|
|
next.board[cx][cy] == CRATE_ON_TARGET ? TARGET : FREE;
|
|
// Déplacement caisse, nouvelle position
|
|
next.board[new_cx][new_cy] = next.board[new_cx][new_cy] == TARGET
|
|
? CRATE_ON_TARGET
|
|
: CRATE_ON_FREE;
|
|
if (next.board[new_cx][new_cy] == CRATE_ON_TARGET) {
|
|
--next.num_crates_free;
|
|
}
|
|
// Déplacement joueur, ancienne position
|
|
next.board[man1_x][man1_y] =
|
|
next.board[man1_x][man1_y] == MAN1_ON_TARGET ? TARGET : FREE;
|
|
// Déplacement joueur, nouvelle position
|
|
next.board[new_man1_x][new_man1_y] =
|
|
next.board[new_man1_x][new_man1_y] == TARGET ? MAN1_ON_TARGET
|
|
: MAN1_ON_FREE;
|
|
next.man1_x = new_man1_x;
|
|
next.man1_y = new_man1_y;
|
|
|
|
// On ajoute cet état suivant à la pile
|
|
Node next_dfs;
|
|
next_dfs.state = next;
|
|
next_dfs.path_len = result.path_len + 1;
|
|
next_dfs.path = result.path + move_str[i] + " ";
|
|
|
|
if (!is_in_history(next_dfs, history)) {
|
|
history.push_back(next_dfs);
|
|
int res = dfs(next_dfs, depth + 1, history);
|
|
if (res == 0) {
|
|
result = next_dfs;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Aucune solution n'a été trouvée à partir de cet état
|
|
return -1;
|
|
}
|