Compare commits
4 Commits
1eddda2605
...
makefile_c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d94b95fa36 | ||
|
|
2db0212b31 | ||
|
|
43e44bf7d2 | ||
|
|
ee374a9221 |
47
Makefile
47
Makefile
@@ -1,5 +1,7 @@
|
||||
# === Environnements ===
|
||||
TEST_ENV = "bin:/usr/share/java/junit.jar:/usr/share/java/hamcrest-core.jar"
|
||||
JUNIT_JAR = /usr/share/java/junit.jar
|
||||
HAMCREST_JAR = /usr/share/java/hamcrest-core.jar
|
||||
TEST_ENV = "bin:$(JUNIT_JAR):$(HAMCREST_JAR)"
|
||||
|
||||
# === Répertoires ===
|
||||
SRC_DIR = fr
|
||||
@@ -9,8 +11,11 @@ BIN_DIR = bin
|
||||
RES_SRC = fr/iut_fbleau/Res
|
||||
RES_BIN = bin/fr/iut_fbleau/Res
|
||||
|
||||
# === Recherche automatique des fichiers .java dans tous les sous-dossiers ===
|
||||
SOURCES := $(shell find $(SRC_DIR) -name "*.java")
|
||||
# === Recherche automatique des fichiers .java ===
|
||||
# SOURCES : uniquement le code de l'application (sans les fichiers de tests)
|
||||
SOURCES := $(shell find $(SRC_DIR) -name "*.java" -not -path "$(SRC_DIR)/iut_fbleau/Tests/*")
|
||||
# TEST_SOURCES : uniquement les fichiers de tests
|
||||
TEST_SOURCES := $(shell find $(SRC_DIR)/iut_fbleau/Tests -name "*.java" 2>/dev/null)
|
||||
|
||||
# === Classe principale ===
|
||||
MAIN_CLASS = fr.iut_fbleau.Avalam.Main
|
||||
@@ -20,7 +25,10 @@ TEST_CLASS = fr.iut_fbleau.Tests.AvalamBoardTest
|
||||
|
||||
# === Commandes Java ===
|
||||
JC = javac
|
||||
JCFLAGS = -d $(BIN_DIR) -cp $(TEST_ENV)
|
||||
# Compilation normale (application uniquement)
|
||||
JCFLAGS = -d $(BIN_DIR)
|
||||
# Compilation des tests (application + JUnit)
|
||||
JCFLAGS_TESTS = -d $(BIN_DIR) -cp $(TEST_ENV)
|
||||
|
||||
JAVA = java
|
||||
JAVAFLAGS = -cp $(BIN_DIR)
|
||||
@@ -38,6 +46,35 @@ compile:
|
||||
@mkdir -p $(BIN_DIR)
|
||||
@$(JC) $(JCFLAGS) $(SOURCES)
|
||||
|
||||
compile_tests: compile
|
||||
@echo "===> Compilation des tests..."
|
||||
@mkdir -p $(BIN_DIR)
|
||||
ifneq ($(TEST_SOURCES),)
|
||||
@$(JC) $(JCFLAGS_TESTS) $(TEST_SOURCES)
|
||||
else
|
||||
@echo "Aucun fichier de test trouvé dans $(SRC_DIR)/iut_fbleau/Tests"
|
||||
endif
|
||||
|
||||
# === Vérification / installation des dépendances de tests ===
|
||||
check_test_deps:
|
||||
@echo "===> Vérification des dépendances de tests (JUnit / Hamcrest)..."
|
||||
@if [ ! -f "$(JUNIT_JAR)" ] || [ ! -f "$(HAMCREST_JAR)" ]; then \
|
||||
echo " JUnit ou Hamcrest manquant, tentative d'installation (sudo requis)..."; \
|
||||
if command -v sudo >/dev/null 2>&1; then \
|
||||
sudo apt-get update && sudo apt-get install -y junit4 libhamcrest-java; \
|
||||
else \
|
||||
apt-get update && apt-get install -y junit4 libhamcrest-java; \
|
||||
fi; \
|
||||
if [ ! -f "$(JUNIT_JAR)" ] || [ ! -f "$(HAMCREST_JAR)" ]; then \
|
||||
echo "✖ Impossible de trouver/installer $(JUNIT_JAR) ou $(HAMCREST_JAR). Vérifiez manuellement vos paquets JUnit/Hamcrest."; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "✔ Dépendances de tests installées."; \
|
||||
fi; \
|
||||
else \
|
||||
echo "✔ JUnit et Hamcrest trouvés."; \
|
||||
fi
|
||||
|
||||
# === Copie des ressources (.txt) dans bin ===
|
||||
resources:
|
||||
@echo "===> Copie des ressources..."
|
||||
@@ -51,7 +88,7 @@ run:
|
||||
@$(JAVA) $(JAVAFLAGS) $(MAIN_CLASS)
|
||||
|
||||
# === Tests ===
|
||||
test:
|
||||
test: check_test_deps compile_tests
|
||||
@echo "===> Lancement des tests..."
|
||||
@$(JAVA) $(JAVAFLAGS_TESTS) org.junit.runner.JUnitCore $(TEST_CLASS)
|
||||
@echo "... Fin des tests."
|
||||
|
||||
32
README.md
32
README.md
@@ -12,37 +12,55 @@ Dans un second temps, nous développerons des bots les plus efficaces possible,
|
||||
|
||||
Le jeu de notre groupe est **Avalam**.
|
||||
|
||||
## Compilation et exécution
|
||||
## Compilation, exécution et tests
|
||||
|
||||
### Compilation
|
||||
### Compilation (sans tests)
|
||||
```bash
|
||||
make build
|
||||
```
|
||||
|
||||
### Tests
|
||||
ou simplement :
|
||||
```bash
|
||||
make test
|
||||
make
|
||||
```
|
||||
Cette commande :
|
||||
- compile uniquement le code de l'application (sans les fichiers du dossier `fr/iut_fbleau/Tests`) ;
|
||||
- copie les ressources dans `bin/`.
|
||||
|
||||
### Exécution
|
||||
### Exécution du jeu
|
||||
```bash
|
||||
make run
|
||||
```
|
||||
Lance la fenêtre de jeu Avalam après compilation.
|
||||
|
||||
### Lancer les tests
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
Cette commande :
|
||||
- vérifie d'abord la présence de **JUnit** et **Hamcrest** dans `/usr/share/java` ;
|
||||
- si nécessaire, tente de les installer automatiquement via `apt-get` (sudo requis sur Debian/Ubuntu) ;
|
||||
- compile ensuite les fichiers de tests (`fr/iut_fbleau/Tests`) ;
|
||||
- lance enfin la suite de tests JUnit (`AvalamBoardTest`).
|
||||
|
||||
Si l'installation automatique échoue (autre OS, pas de droits sudo, pas d'accès réseau, etc.), un message l'indiquera et il faudra installer JUnit/Hamcrest manuellement.
|
||||
|
||||
### Nettoyage
|
||||
```bash
|
||||
make clean
|
||||
```
|
||||
Supprime le répertoire `bin/` (classes compilées et ressources copiées).
|
||||
|
||||
### Recompiler et exécuter
|
||||
### Recompiler puis exécuter
|
||||
```bash
|
||||
make re
|
||||
```
|
||||
Équivaut à `make clean` puis `make build` puis `make run`.
|
||||
|
||||
### Générer la Javadoc
|
||||
```bash
|
||||
make javadoc
|
||||
```
|
||||
Génère la documentation dans le dossier `doc/`.
|
||||
|
||||
## Architecture du projet
|
||||
|
||||
|
||||
@@ -192,64 +192,135 @@ public class ArenaWindow extends JFrame {
|
||||
* @param nbParties nombre de parties à jouer
|
||||
*/
|
||||
private void runArena(String bot1Type, String bot2Type, int depth, int nbParties) {
|
||||
// Créer les bots
|
||||
AbstractGamePlayer bot1 = createBot(bot1Type, Player.PLAYER1, depth);
|
||||
AbstractGamePlayer bot2 = createBot(bot2Type, Player.PLAYER2, depth);
|
||||
|
||||
if (bot1 == null || bot2 == null) {
|
||||
JOptionPane.showMessageDialog(this, "Erreur lors de la création des bots.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Vider le tableau
|
||||
tableModel.setRowCount(0);
|
||||
results.clear();
|
||||
|
||||
// Charger le plateau initial
|
||||
Tower[][] initialGrid = BoardLoader.loadFromFile("fr/iut_fbleau/Res/Plateau.txt");
|
||||
// Créer un dialogue de progression
|
||||
JDialog progressDialog = new JDialog(this, "Parties en cours...", true);
|
||||
JLabel progressLabel = new JLabel("Préparation des parties...", JLabel.CENTER);
|
||||
progressLabel.setBorder(BorderFactory.createEmptyBorder(20, 40, 20, 40));
|
||||
progressDialog.add(progressLabel);
|
||||
progressDialog.setSize(300, 100);
|
||||
progressDialog.setLocationRelativeTo(this);
|
||||
progressDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
|
||||
|
||||
// Lancer les parties
|
||||
for (int i = 1; i <= nbParties; i++) {
|
||||
AvalamBoard board = new AvalamBoard(initialGrid, Player.PLAYER1);
|
||||
ArenaGame game = new ArenaGame(board, bot1, bot2);
|
||||
// Exécuter les parties dans un thread séparé pour ne pas bloquer l'interface
|
||||
Thread arenaThread = new Thread(() -> {
|
||||
SwingUtilities.invokeLater(() -> progressDialog.setVisible(true));
|
||||
|
||||
try {
|
||||
Result result = game.run();
|
||||
String winner = getWinnerName(result, bot1Type, bot2Type);
|
||||
// Statistiques pour déboguer
|
||||
int bot1Wins = 0;
|
||||
int bot2Wins = 0;
|
||||
int draws = 0;
|
||||
int errors = 0;
|
||||
|
||||
// Ajouter au tableau
|
||||
tableModel.addRow(new Object[]{
|
||||
"Partie " + i,
|
||||
bot1Type,
|
||||
bot2Type,
|
||||
winner
|
||||
});
|
||||
} catch (Exception e) {
|
||||
tableModel.addRow(new Object[]{
|
||||
"Partie " + i,
|
||||
bot1Type,
|
||||
bot2Type,
|
||||
"Erreur: " + e.getMessage()
|
||||
for (int i = 1; i <= nbParties; i++) {
|
||||
final int partieNum = i;
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
progressLabel.setText("Partie " + partieNum + " / " + nbParties + " en cours...");
|
||||
});
|
||||
|
||||
// Recréer les bots à chaque partie pour garantir l'indépendance complète
|
||||
// (notamment pour réinitialiser les générateurs aléatoires)
|
||||
AbstractGamePlayer bot1 = createBot(bot1Type, Player.PLAYER1, depth);
|
||||
AbstractGamePlayer bot2 = createBot(bot2Type, Player.PLAYER2, depth);
|
||||
|
||||
if (bot1 == null || bot2 == null) {
|
||||
errors++;
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
tableModel.addRow(new Object[]{
|
||||
"Partie " + partieNum,
|
||||
bot1Type,
|
||||
bot2Type,
|
||||
"Erreur: Impossible de créer les bots"
|
||||
});
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Recharger le plateau à chaque partie pour garantir l'indépendance complète
|
||||
Tower[][] initialGrid = BoardLoader.loadFromFile("fr/iut_fbleau/Res/Plateau.txt");
|
||||
AvalamBoard board = new AvalamBoard(initialGrid, Player.PLAYER1);
|
||||
ArenaGame game = new ArenaGame(board, bot1, bot2);
|
||||
|
||||
try {
|
||||
Result result = game.run();
|
||||
String winner = getWinnerName(result, bot1Type, bot2Type);
|
||||
|
||||
// Mettre à jour les statistiques
|
||||
if (result == Result.WIN) {
|
||||
bot1Wins++;
|
||||
} else if (result == Result.LOSS) {
|
||||
bot2Wins++;
|
||||
} else if (result == Result.DRAW) {
|
||||
draws++;
|
||||
}
|
||||
|
||||
// Ajouter au tableau dans le thread EDT
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
tableModel.addRow(new Object[]{
|
||||
"Partie " + partieNum,
|
||||
bot1Type,
|
||||
bot2Type,
|
||||
winner
|
||||
});
|
||||
});
|
||||
} catch (Exception e) {
|
||||
errors++;
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
tableModel.addRow(new Object[]{
|
||||
"Partie " + partieNum,
|
||||
bot1Type,
|
||||
bot2Type,
|
||||
"Erreur: " + e.getMessage()
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Afficher un message de fin avec possibilité de quitter directement
|
||||
Object[] options = {"OK", "Quitter le jeu"};
|
||||
int choice = JOptionPane.showOptionDialog(
|
||||
this,
|
||||
"Toutes les parties sont terminées !",
|
||||
"Arène terminée",
|
||||
JOptionPane.DEFAULT_OPTION,
|
||||
JOptionPane.INFORMATION_MESSAGE,
|
||||
null,
|
||||
options,
|
||||
options[0]
|
||||
);
|
||||
// Afficher les statistiques finales
|
||||
final int finalBot1Wins = bot1Wins;
|
||||
final int finalBot2Wins = bot2Wins;
|
||||
final int finalDraws = draws;
|
||||
final int finalErrors = errors;
|
||||
|
||||
if (choice == 1) {
|
||||
System.exit(0);
|
||||
}
|
||||
// Fermer le dialogue et afficher le message de fin avec statistiques
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
progressDialog.dispose();
|
||||
|
||||
String statsMessage = String.format(
|
||||
"Toutes les parties sont terminées !\n\n" +
|
||||
"Statistiques :\n" +
|
||||
"- %s (Bot 1) : %d victoires\n" +
|
||||
"- %s (Bot 2) : %d victoires\n" +
|
||||
"- Matchs nuls : %d\n" +
|
||||
"- Erreurs : %d",
|
||||
bot1Type, finalBot1Wins,
|
||||
bot2Type, finalBot2Wins,
|
||||
finalDraws,
|
||||
finalErrors
|
||||
);
|
||||
|
||||
Object[] options = {"OK", "Quitter le jeu"};
|
||||
int choice = JOptionPane.showOptionDialog(
|
||||
this,
|
||||
statsMessage,
|
||||
"Arène terminée",
|
||||
JOptionPane.DEFAULT_OPTION,
|
||||
JOptionPane.INFORMATION_MESSAGE,
|
||||
null,
|
||||
options,
|
||||
options[0]
|
||||
);
|
||||
|
||||
if (choice == 1) {
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
arenaThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,9 +51,16 @@ public class AvalamBoard extends AbstractBoard {
|
||||
super(startingPlayer, new ArrayDeque<>());
|
||||
this.grid = new Tower[SIZE][SIZE];
|
||||
|
||||
// Copie profonde : créer de nouvelles tours pour éviter que toutes les parties partagent les mêmes objets
|
||||
for (int r = 0; r < SIZE; r++)
|
||||
for (int c = 0; c < SIZE; c++)
|
||||
this.grid[r][c] = initialGrid[r][c];
|
||||
for (int c = 0; c < SIZE; c++) {
|
||||
Tower t = initialGrid[r][c];
|
||||
if (t == null) {
|
||||
this.grid[r][c] = null;
|
||||
} else {
|
||||
this.grid[r][c] = new Tower(t.getHeight(), t.getColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -233,7 +233,6 @@ public class AvalamWindow extends JFrame {
|
||||
} else if (mode == GameMode.PVALPHA) {
|
||||
botMove = alphaBot.giveYourMove(board.safeCopy());
|
||||
} else {
|
||||
// A FAIRE PLUS TARD (PVGOD)
|
||||
botMove = divineBot.giveYourMove(board.safeCopy());
|
||||
}
|
||||
|
||||
|
||||
@@ -105,22 +105,38 @@ public class DivineBot extends AbstractGamePlayer {
|
||||
|
||||
boolean isMax = board.getCurrentPlayer() == me;
|
||||
|
||||
for (AbstractPly m : listMoves(board)) {
|
||||
IBoard next = board.safeCopy();
|
||||
next.doPly(m);
|
||||
|
||||
int val = alphaBeta(next, depth - 1, alpha, beta);
|
||||
|
||||
if (isMax) {
|
||||
alpha = Math.max(alpha, val);
|
||||
if (alpha >= beta) break; // Coupure Beta
|
||||
} else {
|
||||
beta = Math.min(beta, val);
|
||||
if (alpha >= beta) break; // Coupure Alpha
|
||||
}
|
||||
List<AbstractPly> moves = listMoves(board);
|
||||
if (moves.isEmpty()) {
|
||||
return evaluate(board);
|
||||
}
|
||||
|
||||
return isMax ? alpha : beta;
|
||||
if (isMax) {
|
||||
int best = Integer.MIN_VALUE;
|
||||
for (AbstractPly m : moves) {
|
||||
IBoard next = board.safeCopy();
|
||||
next.doPly(m);
|
||||
|
||||
int val = alphaBeta(next, depth - 1, alpha, beta);
|
||||
best = Math.max(best, val);
|
||||
alpha = Math.max(alpha, best);
|
||||
|
||||
if (alpha >= beta) break; // Coupure Beta
|
||||
}
|
||||
return best;
|
||||
} else {
|
||||
int best = Integer.MAX_VALUE;
|
||||
for (AbstractPly m : moves) {
|
||||
IBoard next = board.safeCopy();
|
||||
next.doPly(m);
|
||||
|
||||
int val = alphaBeta(next, depth - 1, alpha, beta);
|
||||
best = Math.min(best, val);
|
||||
beta = Math.min(beta, best);
|
||||
|
||||
if (alpha >= beta) break; // Coupure Alpha
|
||||
}
|
||||
return best;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user