Merge branch 'main' of code.up8.edu:Anri/iaj
This commit is contained in:
commit
bf148a629f
26 changed files with 395 additions and 27 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -22,3 +22,6 @@ Cours/
|
|||
|
||||
# IDE related
|
||||
compile_flags.txt
|
||||
|
||||
# Archives
|
||||
*.tar.gz
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
32
TP1/Makefile
Normal 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
|
|
@ -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}
|
||||
\subsection{1 joueur}
|
||||
\begin{enumerate}
|
||||
\item \texttt{Screen-0} :
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\begin{tabular}{c|c|c|c}
|
||||
\begin{tabular}{Y}
|
||||
Nombre max caisses déplacées & Solution & Temps calcul & Temps précalculs \\
|
||||
\hline
|
||||
TODO & TODO & TODO & TODO \\
|
||||
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}
|
||||
|
|
|
@ -5,4 +5,3 @@
|
|||
#......#
|
||||
# #
|
||||
########
|
||||
|
Reference in a new issue