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
compile_flags.txt
# Archives
*.tar.gz

View file

@ -1,17 +1,16 @@
#ifndef MYSOK_H
#define MYSOK_H
#include <iostream>
#include <queue>
#include <stack>
#include <string>
#define NBL 20
#define NBC 20
#define MAX_CRATES 20
#define MAX_DEPTH 60
#define MOVE_U 0
#define MOVE_D 1
#define MOVE_L 2
#define MOVE_R 3
#define MOVE_W 4
enum movement { MOVE_U = 0, MOVE_D, MOVE_L, MOVE_R, MOVE_W };
enum board_str {
OUT = ' ',
@ -27,7 +26,11 @@ enum board_str {
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 {
int board[NBL][NBC];
@ -36,10 +39,20 @@ struct sok_board_t {
int man1_y;
int man2_x;
int man2_y;
int num_crates_free;
sok_board_t();
void print_board();
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

View file

@ -1,16 +1,16 @@
#include "../includes/mysok.h"
sok_board_t::sok_board_t() {
for (int i = 0; i < NBL; i++) {
for (int j = 0; j < NBC; j++) {
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++) {
for (int i = 0; i < board_nbl; ++i) {
for (int j = 0; j < NBC; ++j) {
if (board[i][j] == END_OF_LINE) {
break;
}
@ -33,11 +33,12 @@ void sok_board_t::load(char *_file) {
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++) {
for (int i = 0; i < nread; ++i) {
switch (line[i]) {
case (FREE): {
board[board_nbl][i] = FREE;
@ -54,6 +55,7 @@ void sok_board_t::load(char *_file) {
}
case (CRATE_ON_FREE): {
board[board_nbl][i] = CRATE_ON_FREE;
++num_crates_free;
break;
}
case (CRATE_ON_TARGET): {
@ -88,7 +90,7 @@ void sok_board_t::load(char *_file) {
}
if (read_ok) {
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);
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.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;
}

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
% \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}\\
\\Intelligence artificielle pour les jeux $\cdot$ Université Paris 8}
@ -28,20 +33,47 @@
\tableofcontents
\clearpage
\section{Algorithme}
TODO
\section{Algorithmes}
\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}
TODO
Nous n'avons pas utilisés de pré-calculs.
\section{Problèmes}
\begin{figure}[h]
\centering
\begin{tabular}{c|c|c|c}
Nombre max caisses déplacées & Solution & Temps calcul & Temps précalculs \\
\hline
TODO & TODO & TODO & TODO \\
\end{tabular}
\end{figure}
\subsection{1 joueur}
\begin{enumerate}
\item \texttt{Screen-0} :
\begin{figure}[h]
\centering
\begin{tabular}{Y}
Nombre max caisses déplacées & Solution & Temps calcul & Temps précalculs \\
\hline
6 (toutes) & Oui, en 39 coups & 5-10s & Aucun \\
\end{tabular}
\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}

View file

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