Merge branch 'main' of code.up8.edu:Anri/iaj

This commit is contained in:
César 2023-02-28 18:27:55 +01:00
commit bf148a629f
26 changed files with 395 additions and 27 deletions

3
.gitignore vendored
View file

@ -22,3 +22,6 @@ Cours/
# IDE related # IDE related
compile_flags.txt compile_flags.txt
# Archives
*.tar.gz

View file

@ -1,17 +1,16 @@
#ifndef MYSOK_H #ifndef MYSOK_H
#define MYSOK_H #define MYSOK_H
#include <iostream>
#include <queue>
#include <stack>
#include <string> #include <string>
#define NBL 20 #define NBL 20
#define NBC 20 #define NBC 20
#define MAX_CRATES 20 #define MAX_DEPTH 60
#define MOVE_U 0 enum movement { MOVE_U = 0, MOVE_D, MOVE_L, MOVE_R, MOVE_W };
#define MOVE_D 1
#define MOVE_L 2
#define MOVE_R 3
#define MOVE_W 4
enum board_str { enum board_str {
OUT = ' ', OUT = ' ',
@ -27,7 +26,11 @@ enum board_str {
END_OF_LINE = 'a' END_OF_LINE = 'a'
}; };
// const std::string move_str[] = {"Up", "Down", "Left", "Right", "Wait"}; const std::string move_str[] = {"Up", "Down", "Left", "Right", "Wait"};
const int dx[] = {-1, 1, 0, 0, 0};
const int dy[] = {0, 0, -1, 1, 0};
const int dsize = 5;
struct sok_board_t { struct sok_board_t {
int board[NBL][NBC]; int board[NBL][NBC];
@ -36,10 +39,20 @@ struct sok_board_t {
int man1_y; int man1_y;
int man2_x; int man2_x;
int man2_y; int man2_y;
int num_crates_free;
sok_board_t(); sok_board_t();
void print_board(); void print_board();
void load(char *_file); void load(char *_file);
}; };
typedef struct {
sok_board_t state;
int path_len;
std::string path;
} Node;
int bfs(Node &result);
int dfs(Node &result, int depth, std::vector<Node> history);
#endif #endif

View file

@ -1,16 +1,16 @@
#include "../includes/mysok.h" #include "../includes/mysok.h"
sok_board_t::sok_board_t() { sok_board_t::sok_board_t() {
for (int i = 0; i < NBL; i++) { for (int i = 0; i < NBL; ++i) {
for (int j = 0; j < NBC; j++) { for (int j = 0; j < NBC; ++j) {
board[i][j] = FREE; board[i][j] = FREE;
} }
} }
} }
void sok_board_t::print_board() { void sok_board_t::print_board() {
for (int i = 0; i < board_nbl; i++) { for (int i = 0; i < board_nbl; ++i) {
for (int j = 0; j < NBC; j++) { for (int j = 0; j < NBC; ++j) {
if (board[i][j] == END_OF_LINE) { if (board[i][j] == END_OF_LINE) {
break; break;
} }
@ -33,11 +33,12 @@ void sok_board_t::load(char *_file) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
num_crates_free = 0;
board_nbl = 0; board_nbl = 0;
while ((nread = getline(&line, &len, fp)) != -1) { while ((nread = getline(&line, &len, fp)) != -1) {
if ((static_cast<int>(nread)) > 0) { if ((static_cast<int>(nread)) > 0) {
bool read_ok = false; bool read_ok = false;
for (int i = 0; i < nread; i++) { for (int i = 0; i < nread; ++i) {
switch (line[i]) { switch (line[i]) {
case (FREE): { case (FREE): {
board[board_nbl][i] = FREE; board[board_nbl][i] = FREE;
@ -54,6 +55,7 @@ void sok_board_t::load(char *_file) {
} }
case (CRATE_ON_FREE): { case (CRATE_ON_FREE): {
board[board_nbl][i] = CRATE_ON_FREE; board[board_nbl][i] = CRATE_ON_FREE;
++num_crates_free;
break; break;
} }
case (CRATE_ON_TARGET): { case (CRATE_ON_TARGET): {
@ -88,7 +90,7 @@ void sok_board_t::load(char *_file) {
} }
if (read_ok) { if (read_ok) {
board[board_nbl][nread - 1] = END_OF_LINE; board[board_nbl][nread - 1] = END_OF_LINE;
board_nbl++; ++board_nbl;
} }
} }
} }
@ -96,3 +98,267 @@ void sok_board_t::load(char *_file) {
free(line); free(line);
fclose(fp); 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;
}

View file

@ -11,5 +11,28 @@ int main(int _ac, char **_av) {
S.load(_av[1]); S.load(_av[1]);
S.print_board(); S.print_board();
Node result;
result.state = S;
result.path = "";
result.path_len = 0;
/* if (bfs(result) == -1) {
std::cout << "Aucune solution trouvée\n";
} else {
std::cout << "Coups : " << result.path_len << "\n";
std::cout << "Solution : " << result.path << "\n";
} */
std::vector<Node> history;
if (dfs(result, 0, history) == -1) {
std::cout << "Aucune solution trouvée\n";
} else {
std::cout << "Coups : " << result.path_len << "\n";
std::cout << "Solution : " << result.path << "\n";
}
// result.state.print_board();
return 0; return 0;
} }

32
TP1/Makefile Normal file
View file

@ -0,0 +1,32 @@
NAME = TP1 - Groupe 4
CPP_NAME = C++
PROLOG_NAME = Prolog
TAR = tar --exclude="*AidesCPP" --exclude="*TODO.md" -czf
CP = cp -r
RM = rm -r
RAPPORT = Rapport/rapport.pdf
CPP = C-Cpp/
TESTS = Screens-*
PROLOG = Prolog/
tgz-all:
echo $(CPP) $(PROLOG) | xargs -n 1 $(CP) $(TESTS)
-$(MAKE) -C $(CPP) clean 2> /dev/null
$(TAR) "$(NAME).tar.gz" $(RAPPORT) $(CPP) $(PROLOG)
$(RM) $(CPP)$(TESTS) $(PROLOG)$(TESTS)
tgz-cpp:
$(CP) $(TESTS) $(CPP)
-$(MAKE) -C $(CPP) clean 2> /dev/null
$(TAR) "$(NAME) - $(CPP_NAME).tar.gz" $(RAPPORT) $(CPP)
$(RM) $(CPP)$(TESTS)
tgz-prolog:
$(CP) $(TESTS) $(PROLOG)
$(TAR) "$(NAME) - $(PROLOG_NAME).tar.gz" $(RAPPORT) $(PROLOG)
$(RM) $(PROLOG)$(TESTS)
clean:
$(RM) *.tar.gz

View file

@ -17,7 +17,12 @@
% \usepackage{minted} % intégration code % \usepackage{minted} % intégration code
% \usemintedstyle{emacs} % \usemintedstyle{emacs}
\title{\textbf{TP1 - Sokoban}} % Minimum pour les colonnes des tableaux
\usepackage{array}
\newcolumntype{y}[1]{>{\centering\let\newline\\\arraybackslash\hspace{0pt}}p{#1}}
\newcolumntype{Y}{y{126pt}|y{70pt}|y{66pt}|y{71pt}}
\title{\textbf{TP1 (CPP) - Sokoban}}
\author{Groupe 4\thanks{César PICHON, Florian POSEZ, Omar ANOUAR, Anri KENNEL}\\ \author{Groupe 4\thanks{César PICHON, Florian POSEZ, Omar ANOUAR, Anri KENNEL}\\
\\Intelligence artificielle pour les jeux $\cdot$ Université Paris 8} \\Intelligence artificielle pour les jeux $\cdot$ Université Paris 8}
@ -28,20 +33,47 @@
\tableofcontents \tableofcontents
\clearpage \clearpage
\section{Algorithme} \section{Algorithmes}
TODO \subsection{Algorithme de parcours en largeur}
Notre implémentation de l'algorithme de parcours en largeur est, malheureusement,
trop lente pour résoudre un Sokoban, on a décidé de ne pas l'optimiser et
d'utiliser un autre algorithme.
\subsection{Algorithme de parcours en profondeur}
Notre implémentation de l'algorithme de parcours en profondeur,
elle arrive à résoudre le \texttt{screen-0} en temps raisonnable.
\subsubsection{Optimisations}
Afin d'éviter les cas répétitifs, on utilises un tableau qui stockes les états
déjà visités.
\section{Précalculs} \section{Précalculs}
TODO Nous n'avons pas utilisés de pré-calculs.
\section{Problèmes} \section{Problèmes}
\subsection{1 joueur}
\begin{enumerate}
\item \texttt{Screen-0} :
\begin{figure}[h] \begin{figure}[h]
\centering \centering
\begin{tabular}{c|c|c|c} \begin{tabular}{Y}
Nombre max caisses déplacées & Solution & Temps calcul & Temps précalculs \\ Nombre max caisses déplacées & Solution & Temps calcul & Temps précalculs \\
\hline \hline
TODO & TODO & TODO & TODO \\ 6 (toutes) & Oui, en 39 coups & 5-10s & Aucun \\
\end{tabular} \end{tabular}
\end{figure} \end{figure}
\item \texttt{Screen-2} :
\begin{figure}[h]
\centering
\begin{tabular}{Y}
Nombre max caisses déplacées & Solution & Temps calcul & Temps précalculs \\
\hline
2-3 & Non & Encore en cours & Aucun \\
\end{tabular}
\end{figure}
\end{enumerate}
\subsection{2 joueurs}
L'algorithme n'as pas tourné sur les parties à 2 joueurs.
\end{document} \end{document}

View file

@ -5,4 +5,3 @@
#......# #......#
# # # #
######## ########