Compare commits
1 Commits
master
...
e209e5f102
| Author | SHA1 | Date | |
|---|---|---|---|
| e209e5f102 |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 128 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 22 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 40 KiB |
@@ -3,28 +3,6 @@ title: Avalam - Diagramme de classes (complet)
|
|||||||
---
|
---
|
||||||
classDiagram
|
classDiagram
|
||||||
|
|
||||||
class ArenaGame{
|
|
||||||
+ArenaGame(IBoard board, AbstractGamePlayer bot1, AbstractGamePlayer bot2)
|
|
||||||
-createPlayerMap(AbstractGamePlayer bot1, AbstractGamePlayer bot2): EnumMap<Player, AbstractGamePlayer>
|
|
||||||
}
|
|
||||||
|
|
||||||
class ArenaWindow{
|
|
||||||
-resultsTable: JTable
|
|
||||||
-tableModel: DefaultTableModel
|
|
||||||
-results: List<string>
|
|
||||||
|
|
||||||
+ArenaWindows()
|
|
||||||
-createConfigPanel(): JPanel
|
|
||||||
-createResultsTable()
|
|
||||||
-showConfigDialog()
|
|
||||||
-runArena(String bot1Type, String bot2Type, int depth, int nbParties)
|
|
||||||
-createBot(String botType, Player player, int depth): AbstractGamePlayer
|
|
||||||
-getWinnerName(Result result, String bot1Type, String bot2Type): String
|
|
||||||
}
|
|
||||||
|
|
||||||
ArenaWindow *-- AvalamBoard
|
|
||||||
ArenaWindow *-- ArenaGame
|
|
||||||
|
|
||||||
class AvalamBoard{
|
class AvalamBoard{
|
||||||
+SIZE: int
|
+SIZE: int
|
||||||
-MAX_HEIGHT: int
|
-MAX_HEIGHT: int
|
||||||
@@ -64,6 +42,8 @@ classDiagram
|
|||||||
|
|
||||||
+toString(): String
|
+toString(): String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AvalamWindow{
|
class AvalamWindow{
|
||||||
-board : AvalamBoard
|
-board : AvalamBoard
|
||||||
@@ -90,18 +70,13 @@ classDiagram
|
|||||||
AvalamWindow *-- BoardView
|
AvalamWindow *-- BoardView
|
||||||
AvalamWindow *-- ScoreView
|
AvalamWindow *-- ScoreView
|
||||||
AvalamWindow *-- TurnView
|
AvalamWindow *-- TurnView
|
||||||
AvalamWindow *-- EndGameDialog
|
|
||||||
AvalamWindow --> GameMode
|
AvalamWindow --> GameMode
|
||||||
|
|
||||||
class BackgroundLayer{
|
|
||||||
-img: Image
|
|
||||||
+BackgroundLayer(String resourcePath)
|
|
||||||
#paintComponent(Graphics g): void
|
|
||||||
}
|
|
||||||
|
|
||||||
class BoardLoader{
|
class BoardLoader{
|
||||||
+loadFromFile(String resourcePath): Tower[][]
|
+loadFromFile(String resourcePath): Tower[][]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BoardView{
|
class BoardView{
|
||||||
-board: AvalamBoard
|
-board: AvalamBoard
|
||||||
@@ -130,6 +105,14 @@ classDiagram
|
|||||||
BoardView *-- InteractionController
|
BoardView *-- InteractionController
|
||||||
BoardView --> AvalamBoard
|
BoardView --> AvalamBoard
|
||||||
|
|
||||||
|
class BackgroundLayer{
|
||||||
|
-img: Image
|
||||||
|
+BackgroundLayer(String resourcePath)
|
||||||
|
#paintComponent(Graphics g): void
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Color{
|
class Color{
|
||||||
-YELLOW(int r, int g, int b)
|
-YELLOW(int r, int g, int b)
|
||||||
-RED(int r, int g, int b)
|
-RED(int r, int g, int b)
|
||||||
@@ -139,17 +122,11 @@ classDiagram
|
|||||||
+toPlayer(): fr.iut_fbleau.GameAPI.Player
|
+toPlayer(): fr.iut_fbleau.GameAPI.Player
|
||||||
}
|
}
|
||||||
|
|
||||||
class EndGameDialog{
|
|
||||||
+EndGameDialog(JFrame parent, Result result, int scoreJaune, int scoreRouge, GameMode mode, int depth, Runnable onReplay, Runnable onMenu, Runnable onQuit)
|
|
||||||
-modeToString(GameMode mode, int depth): String
|
|
||||||
}
|
|
||||||
|
|
||||||
class GameMode{
|
class GameMode{
|
||||||
PVP
|
PVP
|
||||||
PVBOT
|
PVBOT
|
||||||
PVALPHA
|
PVALPHA
|
||||||
PVGOD
|
PVGOD
|
||||||
ARENA
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class HighlightLayer{
|
class HighlightLayer{
|
||||||
@@ -188,7 +165,6 @@ classDiagram
|
|||||||
|
|
||||||
Main ..> AvalamWindow
|
Main ..> AvalamWindow
|
||||||
Main ..> GameMode
|
Main ..> GameMode
|
||||||
Main ..> ArenaWindow
|
|
||||||
|
|
||||||
class PieceButton{
|
class PieceButton{
|
||||||
-color: java.awt.Color
|
-color: java.awt.Color
|
||||||
|
|||||||
@@ -16,18 +16,8 @@ classDiagram
|
|||||||
-listMoves(IBoard board): List<AbstractPly>
|
-listMoves(IBoard board): List<AbstractPly>
|
||||||
}
|
}
|
||||||
|
|
||||||
class DivineBot {
|
class DivineBot{
|
||||||
-me : Player
|
|
||||||
-maxDepth : int
|
|
||||||
-rng : Random
|
|
||||||
|
|
||||||
+DivineBot(Player p, int maxDepth)
|
|
||||||
+giveYourMove(IBoard board) : AbstractPly
|
|
||||||
-alphaBeta(IBoard board, int depth, int alpha, int beta) : int
|
|
||||||
-evaluate(IBoard board) : int
|
|
||||||
-isIsolated(AvalamBoard b, int r, int c) : boolean
|
|
||||||
-isVulnerable(AvalamBoard b, int r, int c, Color enemyColor) : boolean
|
|
||||||
-listMoves(IBoard board) : List~AbstractPly~
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class IdiotBot{
|
class IdiotBot{
|
||||||
|
|||||||
54
Makefile
54
Makefile
@@ -1,8 +1,3 @@
|
|||||||
# === Environnements ===
|
|
||||||
JUNIT_JAR = /usr/share/java/junit.jar
|
|
||||||
HAMCREST_JAR = /usr/share/java/hamcrest-core.jar
|
|
||||||
TEST_ENV = "bin:$(JUNIT_JAR):$(HAMCREST_JAR)"
|
|
||||||
|
|
||||||
# === Répertoires ===
|
# === Répertoires ===
|
||||||
SRC_DIR = fr
|
SRC_DIR = fr
|
||||||
BIN_DIR = bin
|
BIN_DIR = bin
|
||||||
@@ -11,28 +6,18 @@ BIN_DIR = bin
|
|||||||
RES_SRC = fr/iut_fbleau/Res
|
RES_SRC = fr/iut_fbleau/Res
|
||||||
RES_BIN = bin/fr/iut_fbleau/Res
|
RES_BIN = bin/fr/iut_fbleau/Res
|
||||||
|
|
||||||
# === Recherche automatique des fichiers .java ===
|
# === Recherche automatique des fichiers .java dans tous les sous-dossiers ===
|
||||||
# SOURCES : uniquement le code de l'application (sans les fichiers de tests)
|
SOURCES := $(shell find $(SRC_DIR) -name "*.java")
|
||||||
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 ===
|
# === Classe principale ===
|
||||||
MAIN_CLASS = fr.iut_fbleau.Avalam.Main
|
MAIN_CLASS = fr.iut_fbleau.Avalam.Main
|
||||||
|
|
||||||
# === Classe de test ===
|
|
||||||
TEST_CLASS = fr.iut_fbleau.Tests.AvalamBoardTest
|
|
||||||
|
|
||||||
# === Commandes Java ===
|
# === Commandes Java ===
|
||||||
JC = javac
|
JC = javac
|
||||||
# Compilation normale (application uniquement)
|
|
||||||
JCFLAGS = -d $(BIN_DIR)
|
JCFLAGS = -d $(BIN_DIR)
|
||||||
# Compilation des tests (application + JUnit)
|
|
||||||
JCFLAGS_TESTS = -d $(BIN_DIR) -cp $(TEST_ENV)
|
|
||||||
|
|
||||||
JAVA = java
|
JAVA = java
|
||||||
JAVAFLAGS = -cp $(BIN_DIR)
|
JAVAFLAGS = -cp $(BIN_DIR)
|
||||||
JAVAFLAGS_TESTS = -cp $(TEST_ENV)
|
|
||||||
|
|
||||||
# === Règle par défaut ===
|
# === Règle par défaut ===
|
||||||
all: build
|
all: build
|
||||||
@@ -46,35 +31,6 @@ compile:
|
|||||||
@mkdir -p $(BIN_DIR)
|
@mkdir -p $(BIN_DIR)
|
||||||
@$(JC) $(JCFLAGS) $(SOURCES)
|
@$(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 ===
|
# === Copie des ressources (.txt) dans bin ===
|
||||||
resources:
|
resources:
|
||||||
@echo "===> Copie des ressources..."
|
@echo "===> Copie des ressources..."
|
||||||
@@ -87,12 +43,6 @@ run:
|
|||||||
@echo "===> Lancement du jeu Avalam..."
|
@echo "===> Lancement du jeu Avalam..."
|
||||||
@$(JAVA) $(JAVAFLAGS) $(MAIN_CLASS)
|
@$(JAVA) $(JAVAFLAGS) $(MAIN_CLASS)
|
||||||
|
|
||||||
# === Tests ===
|
|
||||||
test: check_test_deps compile_tests
|
|
||||||
@echo "===> Lancement des tests..."
|
|
||||||
@$(JAVA) $(JAVAFLAGS_TESTS) org.junit.runner.JUnitCore $(TEST_CLASS)
|
|
||||||
@echo "... Fin des tests."
|
|
||||||
|
|
||||||
# === Nettoyage ===
|
# === Nettoyage ===
|
||||||
clean:
|
clean:
|
||||||
@echo "===> Suppression des fichiers compilés..."
|
@echo "===> Suppression des fichiers compilés..."
|
||||||
|
|||||||
46
README.md
46
README.md
@@ -4,63 +4,38 @@
|
|||||||
|
|
||||||
Date de création : 16/10/25
|
Date de création : 16/10/25
|
||||||
|
|
||||||
Date de rendu : 06/02/26
|
Le but est de mettre en oeuvre des jeux plus intéressants que le jeu de Nim, toujours en suivant l'API.
|
||||||
|
|
||||||
Le but est de mettre en oeuvre un jeu plus intéressants que le jeu de Nim, en suivant l'API de Florent Madelaine.
|
Dans un second temps, on fera des bots le plus efficace possible (probablement un alpha beta avec cut-off plus fonction d'évaluation qui peut être faite à la main ou par MonteCarlo).
|
||||||
|
|
||||||
Dans un second temps, nous développerons des bots les plus efficaces possible, probablement en utilisant un algorithme alpha-bêta avec cut-off et une fonction d'évaluation réalisé à la main ou par MonteCarlo.
|
|
||||||
|
|
||||||
Le jeu de notre groupe est **Avalam**.
|
Le jeu de notre groupe est **Avalam**.
|
||||||
|
|
||||||
## Compilation, exécution et tests
|
## Compilation et exécution
|
||||||
|
|
||||||
### Compilation (sans tests)
|
### Compilation
|
||||||
```bash
|
```bash
|
||||||
make build
|
make build
|
||||||
```
|
```
|
||||||
ou simplement :
|
|
||||||
```bash
|
|
||||||
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 du jeu
|
### Exécution
|
||||||
```bash
|
```bash
|
||||||
make run
|
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
|
### Nettoyage
|
||||||
```bash
|
```bash
|
||||||
make clean
|
make clean
|
||||||
```
|
```
|
||||||
Supprime le répertoire `bin/` (classes compilées et ressources copiées).
|
|
||||||
|
|
||||||
### Recompiler puis exécuter
|
### Recompiler et exécuter
|
||||||
```bash
|
```bash
|
||||||
make re
|
make re
|
||||||
```
|
```
|
||||||
Équivaut à `make clean` puis `make build` puis `make run`.
|
|
||||||
|
|
||||||
### Générer la Javadoc
|
### Générer la Javadoc
|
||||||
```bash
|
```bash
|
||||||
make javadoc
|
make javadoc
|
||||||
```
|
```
|
||||||
Génère la documentation dans le dossier `doc/`.
|
|
||||||
|
|
||||||
## Architecture du projet
|
## Architecture du projet
|
||||||
|
|
||||||
@@ -103,13 +78,13 @@ Le plateau est représenté par une grille 9x9 où chaque case contient :
|
|||||||
|
|
||||||
Sur un plateau de jeu, les joueurs disposent de 24 pions chacun. Le but est de créer de petites tours avec son pion de couleur au-dessus. Pour ce faire, chaque joueur déplace son pion sur une tour ou déplace une tour sur un autre pion. La personne ayant le plus de pions sur le dessus des tours gagne.
|
Sur un plateau de jeu, les joueurs disposent de 24 pions chacun. Le but est de créer de petites tours avec son pion de couleur au-dessus. Pour ce faire, chaque joueur déplace son pion sur une tour ou déplace une tour sur un autre pion. La personne ayant le plus de pions sur le dessus des tours gagne.
|
||||||
|
|
||||||
### Règles officielles :
|
### Régles officiel :
|
||||||
**Régle de base** \
|
**Régle de base** \
|
||||||
Chaque joueur choisit sa couleur.
|
Chaque joueur choisit sa couleur.
|
||||||
Le but du jeu est de constituer un maximum de tours de 1 à 5 pions, jamais plus, surmontées par un pion de sa couleur.
|
Le but du jeu est de constituer un maximum de tours de 1 à 5 pions, jamais plus, surmontées par un pion de sa couleur.
|
||||||
Un joueur est le propriétaire d'une tour lorsqu'un pion de sa couleur en occupe le sommet. Un pion isolé constitue également une tour.
|
Un joueur est le propriétaire d'une tour lorsqu'un pion de sa couleur en occupe le sommet. Un pion isolé constitue également une tour.
|
||||||
|
|
||||||
**Déplacements** \
|
**Deplacements** \
|
||||||
Chaque joueur en effectue un seul mouvement, dans n'importe quel sens (horizontal, vertical, diagonal) avec n'importe quel pion (ou pile de pions), quelle qu'en soit la couleur. Ce mouvement consiste à empiler le ou les pions déplacés sur un trou directement voisin déjà occupé par un ou plusieurs pions.
|
Chaque joueur en effectue un seul mouvement, dans n'importe quel sens (horizontal, vertical, diagonal) avec n'importe quel pion (ou pile de pions), quelle qu'en soit la couleur. Ce mouvement consiste à empiler le ou les pions déplacés sur un trou directement voisin déjà occupé par un ou plusieurs pions.
|
||||||
|
|
||||||
**Mouvement interdit (1)** \
|
**Mouvement interdit (1)** \
|
||||||
@@ -123,8 +98,3 @@ Tans qu'un joueur peut effectuer un mouvement il a l'obligation de jouer, la par
|
|||||||
On compte alors combien de pions de chaque couleur occupent le sommet des tours restantes, le vainqueur étant évidemment celui qui en totalise le plus.
|
On compte alors combien de pions de chaque couleur occupent le sommet des tours restantes, le vainqueur étant évidemment celui qui en totalise le plus.
|
||||||
Attention! Qu'une tour comporte 1,2,... ou 5 pions, elle vaut toujours UN point.
|
Attention! Qu'une tour comporte 1,2,... ou 5 pions, elle vaut toujours UN point.
|
||||||
|
|
||||||
## Arène de bots
|
|
||||||
Dans ce mode de jeu, deux bots s'affrontent pour certains nombre de partie choisit, afin de voir quel bot gagne le plus.
|
|
||||||
|
|
||||||
### Lancer parties
|
|
||||||
Lancer parties permet de faire fonctionner l'arène. On choisit le premier bot pour le joueur1, et le deuxième pour le joueur deux. On choisit la profondeurs de recherches pour les bots intelligent, puis enfin le nombre de partie que doivent effectués les bots. Enfin il suffit pour voir l'entièreté des résultats, si y a beaucoup de parties, sinon, on peut voir les parties se déroulé pendant la session.
|
|
||||||
Binary file not shown.
@@ -1,44 +0,0 @@
|
|||||||
package fr.iut_fbleau.Avalam;
|
|
||||||
|
|
||||||
import fr.iut_fbleau.GameAPI.AbstractGame;
|
|
||||||
import fr.iut_fbleau.GameAPI.AbstractGamePlayer;
|
|
||||||
import fr.iut_fbleau.GameAPI.IBoard;
|
|
||||||
import fr.iut_fbleau.GameAPI.Player;
|
|
||||||
|
|
||||||
import java.util.EnumMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Classe pour jouer une partie entre deux bots sans interface graphique.
|
|
||||||
* Utilisée dans le mode Arène.
|
|
||||||
*
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
|
||||||
public class ArenaGame extends AbstractGame {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construit une partie Arène entre deux bots.
|
|
||||||
*
|
|
||||||
* @param board plateau initial
|
|
||||||
* @param bot1 bot pour PLAYER1
|
|
||||||
* @param bot2 bot pour PLAYER2
|
|
||||||
*/
|
|
||||||
public ArenaGame(IBoard board, AbstractGamePlayer bot1, AbstractGamePlayer bot2) {
|
|
||||||
super(board, createPlayerMap(bot1, bot2));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Crée la map des joueurs pour AbstractGame.
|
|
||||||
*
|
|
||||||
* @param bot1 bot pour PLAYER1
|
|
||||||
* @param bot2 bot pour PLAYER2
|
|
||||||
* @return une EnumMap associant chaque Player à son bot
|
|
||||||
*/
|
|
||||||
private static EnumMap<Player, AbstractGamePlayer> createPlayerMap(
|
|
||||||
AbstractGamePlayer bot1, AbstractGamePlayer bot2) {
|
|
||||||
EnumMap<Player, AbstractGamePlayer> map = new EnumMap<>(Player.class);
|
|
||||||
map.put(Player.PLAYER1, bot1);
|
|
||||||
map.put(Player.PLAYER2, bot2);
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,363 +0,0 @@
|
|||||||
package fr.iut_fbleau.Avalam;
|
|
||||||
|
|
||||||
import fr.iut_fbleau.Bot.AlphaBetaBot;
|
|
||||||
import fr.iut_fbleau.Bot.DivineBot;
|
|
||||||
import fr.iut_fbleau.Bot.IdiotBot;
|
|
||||||
import fr.iut_fbleau.GameAPI.AbstractGamePlayer;
|
|
||||||
import fr.iut_fbleau.GameAPI.Player;
|
|
||||||
import fr.iut_fbleau.GameAPI.Result;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.table.DefaultTableModel;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fenêtre pour le mode Arène (bots vs bots).
|
|
||||||
*
|
|
||||||
* Elle permet :
|
|
||||||
* - de sélectionner deux bots parmi Idiot, Alpha-Beta et Divin ;
|
|
||||||
* - de configurer la profondeur de recherche pour les bots intelligents ;
|
|
||||||
* - de choisir le nombre de parties à jouer ;
|
|
||||||
* - d’afficher, dans un tableau, le résultat de chaque partie (gagnant ou erreur) ;
|
|
||||||
* - de revenir au menu principal ou de quitter entièrement le jeu.
|
|
||||||
*/
|
|
||||||
public class ArenaWindow extends JFrame {
|
|
||||||
|
|
||||||
/** Tableau affichant les résultats des parties. */
|
|
||||||
private JTable resultsTable;
|
|
||||||
|
|
||||||
/** Modèle de données pour le tableau des résultats. */
|
|
||||||
private DefaultTableModel tableModel;
|
|
||||||
|
|
||||||
/** Liste des résultats des parties (non utilisée actuellement). */
|
|
||||||
private List<String> results;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construit la fenêtre Arène.
|
|
||||||
*/
|
|
||||||
public ArenaWindow() {
|
|
||||||
super("Arène - Bot vs Bot");
|
|
||||||
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
|
||||||
setLayout(new BorderLayout());
|
|
||||||
|
|
||||||
results = new ArrayList<>();
|
|
||||||
|
|
||||||
// Panneau de configuration
|
|
||||||
JPanel configPanel = createConfigPanel();
|
|
||||||
add(configPanel, BorderLayout.NORTH);
|
|
||||||
|
|
||||||
// Tableau des résultats
|
|
||||||
createResultsTable();
|
|
||||||
JScrollPane scrollPane = new JScrollPane(resultsTable);
|
|
||||||
add(scrollPane, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
// Panneau des boutons
|
|
||||||
JPanel buttonPanel = new JPanel(new FlowLayout());
|
|
||||||
JButton startButton = new JButton("Lancer les parties");
|
|
||||||
startButton.addActionListener(e -> showConfigDialog());
|
|
||||||
buttonPanel.add(startButton);
|
|
||||||
|
|
||||||
JButton backButton = new JButton("Retour au menu");
|
|
||||||
backButton.addActionListener(e -> {
|
|
||||||
dispose(); // Ferme la fenêtre Arène
|
|
||||||
Main.showModeSelection(); // Affiche le menu principal
|
|
||||||
});
|
|
||||||
buttonPanel.add(backButton);
|
|
||||||
|
|
||||||
// Nouveau bouton pour quitter entièrement le jeu
|
|
||||||
JButton quitButton = new JButton("Quitter");
|
|
||||||
quitButton.addActionListener(e -> System.exit(0));
|
|
||||||
buttonPanel.add(quitButton);
|
|
||||||
|
|
||||||
add(buttonPanel, BorderLayout.SOUTH);
|
|
||||||
|
|
||||||
pack();
|
|
||||||
setSize(600, 400);
|
|
||||||
setLocationRelativeTo(null);
|
|
||||||
setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Crée le panneau de configuration (pour l'instant vide, sera rempli par le dialogue).
|
|
||||||
*
|
|
||||||
* @return un JPanel contenant les informations de configuration
|
|
||||||
*/
|
|
||||||
private JPanel createConfigPanel() {
|
|
||||||
JPanel panel = new JPanel();
|
|
||||||
panel.setBorder(BorderFactory.createTitledBorder("Configuration"));
|
|
||||||
panel.add(new JLabel("Utilisez le bouton 'Lancer les parties' pour configurer et démarrer."));
|
|
||||||
return panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Crée le tableau des résultats avec les colonnes : Partie, Bot 1, Bot 2, Gagnant.
|
|
||||||
*/
|
|
||||||
private void createResultsTable() {
|
|
||||||
String[] columnNames = {"Partie", "Bot 1", "Bot 2", "Gagnant"};
|
|
||||||
tableModel = new DefaultTableModel(columnNames, 0) {
|
|
||||||
@Override
|
|
||||||
public boolean isCellEditable(int row, int column) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
resultsTable = new JTable(tableModel);
|
|
||||||
resultsTable.setFillsViewportHeight(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Affiche le dialogue de configuration et lance les parties.
|
|
||||||
*/
|
|
||||||
private void showConfigDialog() {
|
|
||||||
// Choix du bot 1
|
|
||||||
String[] botTypes = {"Bot Idiot", "Bot Alpha-Beta", "Bot Divin"};
|
|
||||||
String bot1Choice = (String) JOptionPane.showInputDialog(
|
|
||||||
this,
|
|
||||||
"Choisissez le Bot 1 (Joueur 1) :",
|
|
||||||
"Configuration Arène - Bot 1",
|
|
||||||
JOptionPane.QUESTION_MESSAGE,
|
|
||||||
null,
|
|
||||||
botTypes,
|
|
||||||
botTypes[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (bot1Choice == null) return;
|
|
||||||
|
|
||||||
// Choix du bot 2
|
|
||||||
String bot2Choice = (String) JOptionPane.showInputDialog(
|
|
||||||
this,
|
|
||||||
"Choisissez le Bot 2 (Joueur 2) :",
|
|
||||||
"Configuration Arène - Bot 2",
|
|
||||||
JOptionPane.QUESTION_MESSAGE,
|
|
||||||
null,
|
|
||||||
botTypes,
|
|
||||||
botTypes[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (bot2Choice == null) return;
|
|
||||||
|
|
||||||
// Profondeur pour Alpha-Beta et Divin
|
|
||||||
int depth = 4;
|
|
||||||
if (bot1Choice.contains("Alpha") || bot1Choice.contains("Divin") ||
|
|
||||||
bot2Choice.contains("Alpha") || bot2Choice.contains("Divin")) {
|
|
||||||
String depthStr = JOptionPane.showInputDialog(
|
|
||||||
this,
|
|
||||||
"Profondeur de recherche pour les bots Alpha-Beta/Divin ?\n(Conseil: 4)",
|
|
||||||
"Profondeur",
|
|
||||||
JOptionPane.QUESTION_MESSAGE
|
|
||||||
);
|
|
||||||
if (depthStr != null) {
|
|
||||||
try {
|
|
||||||
depth = Integer.parseInt(depthStr.trim());
|
|
||||||
if (depth < 1) depth = 1;
|
|
||||||
} catch (Exception e) {
|
|
||||||
depth = 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nombre de parties
|
|
||||||
String nbPartiesStr = JOptionPane.showInputDialog(
|
|
||||||
this,
|
|
||||||
"Combien de parties voulez-vous jouer ?",
|
|
||||||
"Nombre de parties",
|
|
||||||
JOptionPane.QUESTION_MESSAGE
|
|
||||||
);
|
|
||||||
|
|
||||||
if (nbPartiesStr == null) return;
|
|
||||||
|
|
||||||
int nbParties;
|
|
||||||
try {
|
|
||||||
nbParties = Integer.parseInt(nbPartiesStr.trim());
|
|
||||||
if (nbParties < 1) {
|
|
||||||
JOptionPane.showMessageDialog(this, "Le nombre de parties doit être au moins 1.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
JOptionPane.showMessageDialog(this, "Nombre invalide.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lancer les parties
|
|
||||||
runArena(bot1Choice, bot2Choice, depth, nbParties);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lance les parties entre les deux bots.
|
|
||||||
*
|
|
||||||
* @param bot1Type type du bot 1 (Joueur 1)
|
|
||||||
* @param bot2Type type du bot 2 (Joueur 2)
|
|
||||||
* @param depth profondeur de recherche pour les bots Alpha-Beta/Divin
|
|
||||||
* @param nbParties nombre de parties à jouer
|
|
||||||
*/
|
|
||||||
private void runArena(String bot1Type, String bot2Type, int depth, int nbParties) {
|
|
||||||
// Vider le tableau
|
|
||||||
tableModel.setRowCount(0);
|
|
||||||
results.clear();
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Exécuter les parties dans un thread séparé pour ne pas bloquer l'interface
|
|
||||||
Thread arenaThread = new Thread(() -> {
|
|
||||||
SwingUtilities.invokeLater(() -> progressDialog.setVisible(true));
|
|
||||||
|
|
||||||
// Statistiques pour déboguer
|
|
||||||
int bot1Wins = 0;
|
|
||||||
int bot2Wins = 0;
|
|
||||||
int draws = 0;
|
|
||||||
int errors = 0;
|
|
||||||
|
|
||||||
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 les statistiques finales
|
|
||||||
final int finalBot1Wins = bot1Wins;
|
|
||||||
final int finalBot2Wins = bot2Wins;
|
|
||||||
final int finalDraws = draws;
|
|
||||||
final int finalErrors = errors;
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Crée un bot selon son type.
|
|
||||||
*
|
|
||||||
* @param botType type de bot ("Bot Idiot", "Bot Alpha-Beta", "Bot Divin")
|
|
||||||
* @param player joueur contrôlé par ce bot (PLAYER1 ou PLAYER2)
|
|
||||||
* @param depth profondeur de recherche pour Alpha-Beta/Divin
|
|
||||||
* @return une instance de AbstractGamePlayer correspondant au type, ou null si le type est invalide
|
|
||||||
*/
|
|
||||||
private AbstractGamePlayer createBot(String botType, Player player, int depth) {
|
|
||||||
if (botType.equals("Bot Idiot")) {
|
|
||||||
return new IdiotBot(player);
|
|
||||||
} else if (botType.equals("Bot Alpha-Beta")) {
|
|
||||||
return new AlphaBetaBot(player, depth);
|
|
||||||
} else if (botType.equals("Bot Divin")) {
|
|
||||||
return new DivineBot(player, depth);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Détermine le nom du gagnant selon le résultat.
|
|
||||||
*
|
|
||||||
* @param result résultat de la partie (WIN, LOSS, ou DRAW du point de vue de PLAYER1)
|
|
||||||
* @param bot1Type nom du bot 1
|
|
||||||
* @param bot2Type nom du bot 2
|
|
||||||
* @return une chaîne indiquant le gagnant ou "Match nul"
|
|
||||||
*/
|
|
||||||
private String getWinnerName(Result result, String bot1Type, String bot2Type) {
|
|
||||||
if (result == Result.WIN) {
|
|
||||||
return bot1Type + " (Joueur 1)";
|
|
||||||
} else if (result == Result.LOSS) {
|
|
||||||
return bot2Type + " (Joueur 2)";
|
|
||||||
} else {
|
|
||||||
return "Match nul";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -51,16 +51,9 @@ public class AvalamBoard extends AbstractBoard {
|
|||||||
super(startingPlayer, new ArrayDeque<>());
|
super(startingPlayer, new ArrayDeque<>());
|
||||||
this.grid = new Tower[SIZE][SIZE];
|
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 r = 0; r < SIZE; r++)
|
||||||
for (int c = 0; c < SIZE; c++) {
|
for (int c = 0; c < SIZE; c++)
|
||||||
Tower t = initialGrid[r][c];
|
this.grid[r][c] = initialGrid[r][c];
|
||||||
if (t == null) {
|
|
||||||
this.grid[r][c] = null;
|
|
||||||
} else {
|
|
||||||
this.grid[r][c] = new Tower(t.getHeight(), t.getColor());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -212,7 +205,6 @@ public class AvalamBoard extends AbstractBoard {
|
|||||||
Tower dst = grid[xT][yT];
|
Tower dst = grid[xT][yT];
|
||||||
if (src == null || dst == null) return false;
|
if (src == null || dst == null) return false;
|
||||||
|
|
||||||
if (p.getPlayer() != getCurrentPlayer()) return false;
|
|
||||||
if (src.getColor() != colorForPlayer(getCurrentPlayer())) return false;
|
if (src.getColor() != colorForPlayer(getCurrentPlayer())) return false;
|
||||||
if (!areAdjacent(xF, yF, xT, yT)) return false;
|
if (!areAdjacent(xF, yF, xT, yT)) return false;
|
||||||
if (src.getColor() == dst.getColor()) return false;
|
if (src.getColor() == dst.getColor()) return false;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package fr.iut_fbleau.Avalam;
|
package fr.iut_fbleau.Avalam;
|
||||||
|
|
||||||
import fr.iut_fbleau.Bot.AlphaBetaBot;
|
import fr.iut_fbleau.Bot.AlphaBetaBot;
|
||||||
import fr.iut_fbleau.Bot.DivineBot;
|
// A FAIRE PLUS TARD (PVGOD)
|
||||||
|
// import fr.iut_fbleau.Bot.DivineBot;
|
||||||
import fr.iut_fbleau.Bot.IdiotBot;
|
import fr.iut_fbleau.Bot.IdiotBot;
|
||||||
import fr.iut_fbleau.GameAPI.AbstractPly;
|
import fr.iut_fbleau.GameAPI.AbstractPly;
|
||||||
import fr.iut_fbleau.GameAPI.Player;
|
import fr.iut_fbleau.GameAPI.Player;
|
||||||
@@ -13,25 +14,22 @@ import java.awt.*;
|
|||||||
/**
|
/**
|
||||||
* La classe <code>AvalamWindow</code>
|
* La classe <code>AvalamWindow</code>
|
||||||
*
|
*
|
||||||
* Fenêtre principale (interface graphique) du jeu Avalam pour tous les modes
|
* Fenêtre principale (interface graphique) du jeu Avalam.
|
||||||
* hors Arène :
|
* Elle contient :
|
||||||
* - joueur vs joueur (PVP)
|
* - le plateau (BoardView)
|
||||||
* - joueur vs bot idiot (PVBOT)
|
* - l’affichage du score (ScoreView)
|
||||||
* - joueur vs bot alpha (PVALPHA)
|
* - l’affichage du joueur courant (TurnView)
|
||||||
|
*
|
||||||
|
* Elle pilote un objet <code>AvalamBoard</code> (moteur du jeu).
|
||||||
|
* Elle peut fonctionner en mode :
|
||||||
|
* - joueur vs joueur
|
||||||
|
* - joueur vs bot idiot (aléatoire)
|
||||||
|
* - joueur vs bot alpha (cut-off)
|
||||||
* - joueur vs bot divin (PVGOD)
|
* - joueur vs bot divin (PVGOD)
|
||||||
*
|
*
|
||||||
* Elle contient :
|
* @version 1.0
|
||||||
* - le plateau (<code>BoardView</code>)
|
* Date :
|
||||||
* - l’affichage du score (<code>ScoreView</code>)
|
* Licence :
|
||||||
* - l’affichage du joueur courant (<code>TurnView</code>)
|
|
||||||
*
|
|
||||||
* Elle pilote un objet <code>AvalamBoard</code> (moteur du jeu) et,
|
|
||||||
* en fonction du {@link GameMode}, instancie le bot approprié
|
|
||||||
* (idiot, alpha-bêta ou divin).
|
|
||||||
*
|
|
||||||
* En fin de partie, elle ouvre une fenêtre de fin (<code>EndGameDialog</code>)
|
|
||||||
* affichant le gagnant, les scores et proposant les actions :
|
|
||||||
* « Rejouer », « Menu principal » ou « Quitter le jeu ».
|
|
||||||
*/
|
*/
|
||||||
public class AvalamWindow extends JFrame {
|
public class AvalamWindow extends JFrame {
|
||||||
|
|
||||||
@@ -52,9 +50,6 @@ public class AvalamWindow extends JFrame {
|
|||||||
/** Mode de jeu sélectionné. */
|
/** Mode de jeu sélectionné. */
|
||||||
private final GameMode mode;
|
private final GameMode mode;
|
||||||
|
|
||||||
/** Profondeur de recherche utilisée (utile pour les modes avec bot intelligent et pour rejouer). */
|
|
||||||
private final int searchDepth;
|
|
||||||
|
|
||||||
/** Joueur contrôlé par le bot (si actif). */
|
/** Joueur contrôlé par le bot (si actif). */
|
||||||
private final Player botPlayer = Player.PLAYER2;
|
private final Player botPlayer = Player.PLAYER2;
|
||||||
|
|
||||||
@@ -64,8 +59,16 @@ public class AvalamWindow extends JFrame {
|
|||||||
/** Bot Alpha-Beta (utilisé si mode PVALPHA). */
|
/** Bot Alpha-Beta (utilisé si mode PVALPHA). */
|
||||||
private final AlphaBetaBot alphaBot;
|
private final AlphaBetaBot alphaBot;
|
||||||
|
|
||||||
/** Bot Divin (utilisé si mode PVGOD). */
|
// A FAIRE PLUS TARD (PVGOD)
|
||||||
private final DivineBot divineBot;
|
// /** Bot Divin (utilisé si mode PVGOD). */
|
||||||
|
// private final DivineBot divineBot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A FAIRE PLUS TARD (PVGOD)
|
||||||
|
* On garde l'attribut à null pour ne pas casser la compilation,
|
||||||
|
* mais toute la logique PVGOD est désactivée/commentée.
|
||||||
|
*/
|
||||||
|
private final Object divineBot = null;
|
||||||
|
|
||||||
/** Indique si une animation de tour de bot est en cours. */
|
/** Indique si une animation de tour de bot est en cours. */
|
||||||
private boolean botAnimating = false;
|
private boolean botAnimating = false;
|
||||||
@@ -83,7 +86,7 @@ public class AvalamWindow extends JFrame {
|
|||||||
* Construit la fenêtre en fonction du mode choisi.
|
* Construit la fenêtre en fonction du mode choisi.
|
||||||
* Pour PVALPHA/PVGOD, la profondeur par défaut est 4.
|
* Pour PVALPHA/PVGOD, la profondeur par défaut est 4.
|
||||||
*
|
*
|
||||||
* @param mode mode de jeu (PVP, PVBOT, PVALPHA ou PVGOD)
|
* @param mode mode de jeu
|
||||||
*/
|
*/
|
||||||
public AvalamWindow(GameMode mode) {
|
public AvalamWindow(GameMode mode) {
|
||||||
this(mode, 4);
|
this(mode, 4);
|
||||||
@@ -91,24 +94,23 @@ public class AvalamWindow extends JFrame {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Construit la fenêtre en fonction du mode choisi.
|
* Construit la fenêtre en fonction du mode choisi.
|
||||||
* Si le mode est PVALPHA ou PVGOD, la profondeur est utilisée comme cut-off
|
* Si le mode est PVALPHA ou PVGOD, la profondeur est utilisée comme cut-off.
|
||||||
* pour les bots Alpha-Beta et Divin.
|
|
||||||
*
|
*
|
||||||
* @param mode mode de jeu
|
* @param mode mode de jeu
|
||||||
* @param alphaDepth profondeur de recherche pour Alpha-Beta / Bot Divin
|
* @param alphaDepth profondeur de recherche pour Alpha-Beta / Bot Divin
|
||||||
*/
|
*/
|
||||||
public AvalamWindow(GameMode mode, int alphaDepth) {
|
public AvalamWindow(GameMode mode, int alphaDepth) {
|
||||||
super("Avalam");
|
super("Avalam");
|
||||||
|
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
this.searchDepth = Math.max(1, alphaDepth);
|
|
||||||
|
|
||||||
this.idiotBot = (mode == GameMode.PVBOT) ? new IdiotBot(botPlayer) : null;
|
this.idiotBot = (mode == GameMode.PVBOT) ? new IdiotBot(botPlayer) : null;
|
||||||
|
|
||||||
int depth = this.searchDepth;
|
int depth = Math.max(1, alphaDepth);
|
||||||
this.alphaBot = (mode == GameMode.PVALPHA) ? new AlphaBetaBot(botPlayer, depth) : null;
|
this.alphaBot = (mode == GameMode.PVALPHA) ? new AlphaBetaBot(botPlayer, depth) : null;
|
||||||
|
|
||||||
this.divineBot = (mode == GameMode.PVGOD) ? new DivineBot(botPlayer, depth) : null;
|
// A FAIRE PLUS TARD (PVGOD)
|
||||||
|
// this.divineBot = (mode == GameMode.PVGOD) ? new DivineBot(botPlayer, depth) : null;
|
||||||
|
|
||||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
@@ -166,30 +168,28 @@ public class AvalamWindow extends JFrame {
|
|||||||
if (board.isGameOver()) {
|
if (board.isGameOver()) {
|
||||||
Result res = board.getResult();
|
Result res = board.getResult();
|
||||||
|
|
||||||
int scoreJaune = computeScore(Color.YELLOW);
|
String msg;
|
||||||
int scoreRouge = computeScore(Color.RED);
|
switch (res) {
|
||||||
|
case WIN:
|
||||||
|
msg = "Le joueur jaune a gagné !";
|
||||||
|
break;
|
||||||
|
case LOSS:
|
||||||
|
msg = "Le joueur rouge a gagné !";
|
||||||
|
break;
|
||||||
|
case DRAW:
|
||||||
|
msg = "Égalité !";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
msg = "Fin de partie.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
EndGameDialog dialog = new EndGameDialog(
|
JOptionPane.showMessageDialog(
|
||||||
this,
|
this,
|
||||||
res,
|
msg,
|
||||||
scoreJaune,
|
"Partie terminée",
|
||||||
scoreRouge,
|
JOptionPane.INFORMATION_MESSAGE
|
||||||
mode,
|
|
||||||
searchDepth,
|
|
||||||
// Rejouer : on ferme la fenêtre actuelle et on relance une nouvelle partie avec le même mode/profondeur
|
|
||||||
() -> SwingUtilities.invokeLater(() -> {
|
|
||||||
dispose();
|
|
||||||
new AvalamWindow(mode, searchDepth);
|
|
||||||
}),
|
|
||||||
// Menu principal : on ferme la fenêtre actuelle et on réaffiche le menu de mode de jeu
|
|
||||||
() -> SwingUtilities.invokeLater(() -> {
|
|
||||||
dispose();
|
|
||||||
Main.showModeSelection();
|
|
||||||
}),
|
|
||||||
// Quitter complètement l'application
|
|
||||||
() -> System.exit(0)
|
|
||||||
);
|
);
|
||||||
dialog.setVisible(true);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +219,12 @@ public class AvalamWindow extends JFrame {
|
|||||||
if (mode == GameMode.PVBOT && idiotBot == null) return;
|
if (mode == GameMode.PVBOT && idiotBot == null) return;
|
||||||
if (mode == GameMode.PVALPHA && alphaBot == null) return;
|
if (mode == GameMode.PVALPHA && alphaBot == null) return;
|
||||||
|
|
||||||
if (mode == GameMode.PVGOD && divineBot == null) return;
|
// A FAIRE PLUS TARD (PVGOD)
|
||||||
|
// if (mode == GameMode.PVGOD && divineBot == null) return;
|
||||||
|
|
||||||
|
// A FAIRE PLUS TARD (PVGOD)
|
||||||
|
// Pour l'instant, si PVGOD est sélectionné, on ne joue pas de coup bot.
|
||||||
|
if (mode == GameMode.PVGOD) return;
|
||||||
|
|
||||||
botAnimating = true;
|
botAnimating = true;
|
||||||
|
|
||||||
@@ -233,7 +238,9 @@ public class AvalamWindow extends JFrame {
|
|||||||
} else if (mode == GameMode.PVALPHA) {
|
} else if (mode == GameMode.PVALPHA) {
|
||||||
botMove = alphaBot.giveYourMove(board.safeCopy());
|
botMove = alphaBot.giveYourMove(board.safeCopy());
|
||||||
} else {
|
} else {
|
||||||
botMove = divineBot.giveYourMove(board.safeCopy());
|
// A FAIRE PLUS TARD (PVGOD)
|
||||||
|
// botMove = divineBot.giveYourMove(board.safeCopy());
|
||||||
|
botMove = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (botMove == null) {
|
if (botMove == null) {
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
package fr.iut_fbleau.Avalam;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* La classe <code>BackgroundLayer</code>
|
|
||||||
*
|
|
||||||
* Sous composant de BoardView affichant l’image de fond.
|
|
||||||
*/
|
|
||||||
public class BackgroundLayer extends JComponent {
|
|
||||||
private Image img;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construit une couche de fond.
|
|
||||||
*
|
|
||||||
* @param resourcePath chemin de l'image de fond
|
|
||||||
*/
|
|
||||||
public BackgroundLayer(String resourcePath) {
|
|
||||||
img = Toolkit.getDefaultToolkit().getImage(
|
|
||||||
getClass().getClassLoader().getResource(resourcePath)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dessine l'image de fond.
|
|
||||||
*
|
|
||||||
* @param g contexte graphique
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void paintComponent(Graphics g) {
|
|
||||||
super.paintComponent(g);
|
|
||||||
if (img != null) {
|
|
||||||
g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,159 +1,192 @@
|
|||||||
package fr.iut_fbleau.Avalam;
|
package fr.iut_fbleau.Avalam;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* La classe <code>BoardView</code>
|
* La classe <code>BoardView</code>
|
||||||
*
|
*
|
||||||
* Représente la vue principale du plateau Avalam.
|
* Représente la vue principale du plateau Avalam.
|
||||||
* Elle gère :
|
* Elle gère :
|
||||||
* - l’affichage des tours (PieceLayer)
|
* - l’affichage des tours (PieceLayer)
|
||||||
* - l’affichage des coups possibles (HighlightLayer)
|
* - l’affichage des coups possibles (HighlightLayer)
|
||||||
* - l’affichage du fond graphique (BackgroundLayer)
|
* - l’affichage du fond graphique
|
||||||
* - les clics via InteractionController
|
* - les clics via InteractionController
|
||||||
*
|
*
|
||||||
* Cette classe ne contient aucune logique de règles du jeu.
|
* Cette classe ne contient aucune logique de règles du jeu.
|
||||||
*
|
*
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
* Date :
|
* Date :
|
||||||
* Licence :
|
* Licence :
|
||||||
*/
|
*/
|
||||||
public class BoardView extends JLayeredPane {
|
public class BoardView extends JLayeredPane {
|
||||||
|
|
||||||
//Attributs
|
//Attributs
|
||||||
|
|
||||||
/** Référence au moteur Avalam. */
|
/** Référence au moteur Avalam. */
|
||||||
private AvalamBoard board;
|
private AvalamBoard board;
|
||||||
|
|
||||||
/** Couche d’affichage du fond. */
|
/** Couche d’affichage du fond. */
|
||||||
private BackgroundLayer backgroundLayer;
|
private BackgroundLayer backgroundLayer;
|
||||||
|
|
||||||
/** Couche d’affichage des coups possibles. */
|
/** Couche d’affichage des coups possibles. */
|
||||||
private HighlightLayer highlightLayer;
|
private HighlightLayer highlightLayer;
|
||||||
|
|
||||||
/** Couche d’affichage des pièces. */
|
/** Couche d’affichage des pièces. */
|
||||||
private PieceLayer pieceLayer;
|
private PieceLayer pieceLayer;
|
||||||
|
|
||||||
/** Contrôleur des interactions. */
|
/** Contrôleur des interactions. */
|
||||||
private InteractionController controller;
|
private InteractionController controller;
|
||||||
|
|
||||||
/** Taille d’un pion en pixels. */
|
/** Taille d’un pion en pixels. */
|
||||||
private final int size = 50;
|
private final int size = 50;
|
||||||
|
|
||||||
/** Espacement entre les cases. */
|
/** Espacement entre les cases. */
|
||||||
private final int spacing = 70;
|
private final int spacing = 70;
|
||||||
|
|
||||||
/** Décalage horizontal du plateau. */
|
/** Décalage horizontal du plateau. */
|
||||||
private final int xBase = 60;
|
private final int xBase = 60;
|
||||||
|
|
||||||
/** Décalage vertical du plateau. */
|
/** Décalage vertical du plateau. */
|
||||||
private final int yBase = 60;
|
private final int yBase = 60;
|
||||||
|
|
||||||
/** Callback vers AvalamWindow pour mise à jour (score, tour, fin). */
|
/** Callback vers AvalamWindow pour mise à jour (score, tour, fin). */
|
||||||
private Runnable boardUpdateCallback;
|
private Runnable boardUpdateCallback;
|
||||||
|
|
||||||
//Constructeur
|
//Constructeur
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construit la vue du plateau.
|
* Construit la vue du plateau.
|
||||||
*
|
*
|
||||||
* @param board moteur du jeu
|
* @param board moteur du jeu
|
||||||
* @param boardUpdateCallback callback à appeler après un coup
|
* @param boardUpdateCallback callback à appeler après un coup
|
||||||
*/
|
*/
|
||||||
public BoardView(AvalamBoard board, Runnable boardUpdateCallback) {
|
public BoardView(AvalamBoard board, Runnable boardUpdateCallback) {
|
||||||
this.board = board;
|
this.board = board;
|
||||||
this.boardUpdateCallback = boardUpdateCallback;
|
this.boardUpdateCallback = boardUpdateCallback;
|
||||||
|
|
||||||
setLayout(null);
|
setLayout(null);
|
||||||
|
|
||||||
// Contrôleur
|
// Contrôleur
|
||||||
this.controller = new InteractionController(board, this);
|
this.controller = new InteractionController(board, this);
|
||||||
|
|
||||||
// Couche fond
|
// Couche fond
|
||||||
backgroundLayer = new BackgroundLayer("fr/iut_fbleau/Res/BackgroundAvalam.png");
|
backgroundLayer = new BackgroundLayer("fr/iut_fbleau/Res/BackgroundAvalam.png");
|
||||||
backgroundLayer.setBounds(0, 0, 725, 725);
|
backgroundLayer.setBounds(0, 0, 725, 725);
|
||||||
add(backgroundLayer, JLayeredPane.FRAME_CONTENT_LAYER);
|
add(backgroundLayer, JLayeredPane.FRAME_CONTENT_LAYER);
|
||||||
|
|
||||||
// Couche highlight
|
// Couche highlight
|
||||||
highlightLayer = new HighlightLayer(xBase, yBase, spacing, size);
|
highlightLayer = new HighlightLayer(xBase, yBase, spacing, size);
|
||||||
add(highlightLayer, JLayeredPane.DEFAULT_LAYER);
|
add(highlightLayer, JLayeredPane.DEFAULT_LAYER);
|
||||||
|
|
||||||
// Couche pièces
|
// Couche pièces
|
||||||
pieceLayer = new PieceLayer();
|
pieceLayer = new PieceLayer();
|
||||||
add(pieceLayer, JLayeredPane.PALETTE_LAYER);
|
add(pieceLayer, JLayeredPane.PALETTE_LAYER);
|
||||||
|
|
||||||
setPreferredSize(new Dimension(725, 725));
|
setPreferredSize(new Dimension(725, 725));
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Méthodes
|
//Méthodes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retourne le contrôleur d'interactions (utile pour simuler les clics du bot).
|
* Retourne le contrôleur d'interactions (utile pour simuler les clics du bot).
|
||||||
*
|
*
|
||||||
* @return contrôleur
|
* @return contrôleur
|
||||||
*/
|
*/
|
||||||
public InteractionController getController() {
|
public InteractionController getController() {
|
||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Active/désactive les interactions utilisateur sur le plateau.
|
* Active/désactive les interactions utilisateur sur le plateau.
|
||||||
* Utile pendant l'animation du bot pour éviter des clics concurrents.
|
* Utile pendant l'animation du bot pour éviter des clics concurrents.
|
||||||
*
|
*
|
||||||
* @param enabled true pour activer, false pour désactiver
|
* @param enabled true pour activer, false pour désactiver
|
||||||
*/
|
*/
|
||||||
public void setInteractionEnabled(boolean enabled) {
|
public void setInteractionEnabled(boolean enabled) {
|
||||||
// Désactive la couche des pièces (boutons) principalement
|
// Désactive la couche des pièces (boutons) principalement
|
||||||
pieceLayer.setEnabled(enabled);
|
pieceLayer.setEnabled(enabled);
|
||||||
for (Component c : pieceLayer.getComponents()) {
|
for (Component c : pieceLayer.getComponents()) {
|
||||||
c.setEnabled(enabled);
|
c.setEnabled(enabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appelé par le contrôleur après un coup.
|
* Appelé par le contrôleur après un coup.
|
||||||
*/
|
*/
|
||||||
public void onBoardUpdated() {
|
public void onBoardUpdated() {
|
||||||
if (boardUpdateCallback != null) {
|
if (boardUpdateCallback != null) {
|
||||||
boardUpdateCallback.run();
|
boardUpdateCallback.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rafraîchit les couches visuelles.
|
* Rafraîchit les couches visuelles.
|
||||||
*/
|
*/
|
||||||
public void refresh() {
|
public void refresh() {
|
||||||
|
|
||||||
pieceLayer.displayGrid(
|
pieceLayer.displayGrid(
|
||||||
boardGrid(),
|
boardGrid(),
|
||||||
xBase, yBase, spacing, size,
|
xBase, yBase, spacing, size,
|
||||||
(r, c) -> controller.onPieceClicked(r, c)
|
(r, c) -> controller.onPieceClicked(r, c)
|
||||||
);
|
);
|
||||||
|
|
||||||
highlightLayer.setLegalMoves(controller.getLegalMoves());
|
highlightLayer.setLegalMoves(controller.getLegalMoves());
|
||||||
|
|
||||||
backgroundLayer.repaint();
|
backgroundLayer.repaint();
|
||||||
highlightLayer.repaint();
|
highlightLayer.repaint();
|
||||||
pieceLayer.repaint();
|
pieceLayer.repaint();
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Récupère la grille depuis le moteur.
|
* Récupère la grille depuis le moteur.
|
||||||
*
|
*
|
||||||
* @return grille 9x9 de tours
|
* @return grille 9x9 de tours
|
||||||
*/
|
*/
|
||||||
private Tower[][] boardGrid() {
|
private Tower[][] boardGrid() {
|
||||||
Tower[][] grid = new Tower[AvalamBoard.SIZE][AvalamBoard.SIZE];
|
Tower[][] grid = new Tower[AvalamBoard.SIZE][AvalamBoard.SIZE];
|
||||||
|
|
||||||
for (int r = 0; r < AvalamBoard.SIZE; r++) {
|
for (int r = 0; r < AvalamBoard.SIZE; r++) {
|
||||||
for (int c = 0; c < AvalamBoard.SIZE; c++) {
|
for (int c = 0; c < AvalamBoard.SIZE; c++) {
|
||||||
grid[r][c] = board.getTowerAt(r, c);
|
grid[r][c] = board.getTowerAt(r, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return grid;
|
return grid;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
//Affichage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composant affichant l’image de fond.
|
||||||
|
*/
|
||||||
|
private static class BackgroundLayer extends JComponent {
|
||||||
|
private Image img;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construit une couche de fond.
|
||||||
|
*
|
||||||
|
* @param resourcePath chemin de l'image de fond
|
||||||
|
*/
|
||||||
|
public BackgroundLayer(String resourcePath) {
|
||||||
|
img = Toolkit.getDefaultToolkit().getImage(
|
||||||
|
getClass().getClassLoader().getResource(resourcePath)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dessine l'image de fond.
|
||||||
|
*
|
||||||
|
* @param g contexte graphique
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void paintComponent(Graphics g) {
|
||||||
|
super.paintComponent(g);
|
||||||
|
if (img != null) {
|
||||||
|
g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
package fr.iut_fbleau.Avalam;
|
|
||||||
|
|
||||||
import fr.iut_fbleau.GameAPI.Result;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fenêtre de fin de partie.
|
|
||||||
*
|
|
||||||
* Elle est ouverte par {@link AvalamWindow} lorsque le moteur signale
|
|
||||||
* que la partie est terminée. Elle affiche :
|
|
||||||
* - le résultat (gagnant ou égalité) à partir du {@link Result} ;
|
|
||||||
* - le score détaillé (tours contrôlées par Jaune et Rouge) ;
|
|
||||||
* - le mode de jeu courant (PVP, PVBOT, PVALPHA, PVGOD, avec profondeur pour les bots intelligents).
|
|
||||||
*
|
|
||||||
* Elle propose également trois actions sous forme de boutons :
|
|
||||||
* - « Rejouer » : relancer une partie avec la même configuration ;
|
|
||||||
* - « Menu principal » : retourner au menu de sélection de mode ;
|
|
||||||
* - « Quitter » : fermer complètement l’application.
|
|
||||||
*/
|
|
||||||
public class EndGameDialog extends JDialog {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construit la fenêtre de fin de partie.
|
|
||||||
*
|
|
||||||
* @param parent fenêtre principale (généralement une {@link AvalamWindow})
|
|
||||||
* @param result résultat de la partie (WIN / LOSS / DRAW du point de vue de PLAYER1 / Jaune)
|
|
||||||
* @param scoreJaune score du joueur jaune (nombre de tours contrôlées)
|
|
||||||
* @param scoreRouge score du joueur rouge (nombre de tours contrôlées)
|
|
||||||
* @param mode mode de jeu courant (pour l’information et le « Rejouer »)
|
|
||||||
* @param depth profondeur utilisée (pour les modes avec bot intelligent)
|
|
||||||
* @param onReplay action à exécuter lorsque l’utilisateur clique sur « Rejouer »
|
|
||||||
* @param onMenu action à exécuter lorsque l’utilisateur clique sur « Menu principal »
|
|
||||||
* @param onQuit action à exécuter lorsque l’utilisateur clique sur « Quitter »
|
|
||||||
*/
|
|
||||||
public EndGameDialog(
|
|
||||||
JFrame parent,
|
|
||||||
Result result,
|
|
||||||
int scoreJaune,
|
|
||||||
int scoreRouge,
|
|
||||||
GameMode mode,
|
|
||||||
int depth,
|
|
||||||
Runnable onReplay,
|
|
||||||
Runnable onMenu,
|
|
||||||
Runnable onQuit
|
|
||||||
) {
|
|
||||||
super(parent, "Fin de partie", true);
|
|
||||||
|
|
||||||
setLayout(new BorderLayout(10, 10));
|
|
||||||
|
|
||||||
// Panel principal d'information
|
|
||||||
JPanel infoPanel = new JPanel();
|
|
||||||
infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.Y_AXIS));
|
|
||||||
infoPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
|
||||||
|
|
||||||
JLabel titre = new JLabel("Partie terminée");
|
|
||||||
titre.setFont(titre.getFont().deriveFont(Font.BOLD, 18f));
|
|
||||||
titre.setAlignmentX(Component.CENTER_ALIGNMENT);
|
|
||||||
|
|
||||||
String gagnant;
|
|
||||||
switch (result) {
|
|
||||||
case WIN:
|
|
||||||
gagnant = "Le joueur JAUNE a gagné !";
|
|
||||||
break;
|
|
||||||
case LOSS:
|
|
||||||
gagnant = "Le joueur ROUGE a gagné !";
|
|
||||||
break;
|
|
||||||
case DRAW:
|
|
||||||
gagnant = "Égalité parfaite !";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
gagnant = "Fin de partie.";
|
|
||||||
}
|
|
||||||
|
|
||||||
JLabel gagnantLabel = new JLabel(gagnant);
|
|
||||||
gagnantLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
|
||||||
|
|
||||||
JLabel scoreLabel = new JLabel(
|
|
||||||
"Score - Jaune : " + scoreJaune + " | Rouge : " + scoreRouge
|
|
||||||
);
|
|
||||||
scoreLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
|
||||||
|
|
||||||
JLabel modeLabel = new JLabel("Mode : " + modeToString(mode, depth));
|
|
||||||
modeLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
|
||||||
|
|
||||||
infoPanel.add(titre);
|
|
||||||
infoPanel.add(Box.createVerticalStrut(10));
|
|
||||||
infoPanel.add(gagnantLabel);
|
|
||||||
infoPanel.add(Box.createVerticalStrut(5));
|
|
||||||
infoPanel.add(scoreLabel);
|
|
||||||
infoPanel.add(Box.createVerticalStrut(5));
|
|
||||||
infoPanel.add(modeLabel);
|
|
||||||
|
|
||||||
add(infoPanel, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
// Panel des boutons d'action
|
|
||||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 15, 10));
|
|
||||||
|
|
||||||
JButton replayButton = new JButton("Rejouer");
|
|
||||||
replayButton.addActionListener(e -> {
|
|
||||||
if (onReplay != null) {
|
|
||||||
onReplay.run();
|
|
||||||
}
|
|
||||||
dispose();
|
|
||||||
});
|
|
||||||
|
|
||||||
JButton menuButton = new JButton("Menu principal");
|
|
||||||
menuButton.addActionListener(e -> {
|
|
||||||
if (onMenu != null) {
|
|
||||||
onMenu.run();
|
|
||||||
}
|
|
||||||
dispose();
|
|
||||||
});
|
|
||||||
|
|
||||||
JButton quitButton = new JButton("Quitter");
|
|
||||||
quitButton.addActionListener(e -> {
|
|
||||||
if (onQuit != null) {
|
|
||||||
onQuit.run();
|
|
||||||
}
|
|
||||||
dispose();
|
|
||||||
});
|
|
||||||
|
|
||||||
buttonPanel.add(replayButton);
|
|
||||||
buttonPanel.add(menuButton);
|
|
||||||
buttonPanel.add(quitButton);
|
|
||||||
|
|
||||||
add(buttonPanel, BorderLayout.SOUTH);
|
|
||||||
|
|
||||||
pack();
|
|
||||||
setResizable(false);
|
|
||||||
setLocationRelativeTo(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retourne une description lisible du mode de jeu courant.
|
|
||||||
*
|
|
||||||
* @param mode mode de jeu
|
|
||||||
* @param depth profondeur (pour les bots intelligents)
|
|
||||||
* @return chaîne décrivant le mode
|
|
||||||
*/
|
|
||||||
private String modeToString(GameMode mode, int depth) {
|
|
||||||
switch (mode) {
|
|
||||||
case PVP:
|
|
||||||
return "Joueur vs Joueur";
|
|
||||||
case PVBOT:
|
|
||||||
return "Joueur vs Bot idiot";
|
|
||||||
case PVALPHA:
|
|
||||||
return "Joueur vs Bot Alpha (profondeur " + depth + ")";
|
|
||||||
case PVGOD:
|
|
||||||
return "Joueur vs Bot Divin (profondeur " + depth + ")";
|
|
||||||
case ARENA:
|
|
||||||
return "Arène (bots)"; // normalement non utilisé ici
|
|
||||||
default:
|
|
||||||
return mode.name();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -7,6 +7,5 @@ public enum GameMode {
|
|||||||
PVP, // joueur vs joueur
|
PVP, // joueur vs joueur
|
||||||
PVBOT, // joueur vs bot idiot
|
PVBOT, // joueur vs bot idiot
|
||||||
PVALPHA, // joueur vs bot alpha
|
PVALPHA, // joueur vs bot alpha
|
||||||
PVGOD, // joueur vs bot stratégique
|
PVGOD // joueur vs bot stratégique
|
||||||
ARENA // bot vs bot (mode arène)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,75 +8,62 @@ import javax.swing.*;
|
|||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
showModeSelection();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
String[] options = {
|
||||||
* Affiche le menu de sélection du mode de jeu.
|
"joueur vs joueur",
|
||||||
* Peut être appelé depuis d'autres fenêtres pour revenir au menu.
|
"joueur vs botidiot",
|
||||||
*/
|
"joueur vs bot alpha",
|
||||||
public static void showModeSelection() {
|
"joueur vs bot divin (NON IMPLEMENTE)"
|
||||||
String[] options = {
|
};
|
||||||
"joueur vs joueur",
|
|
||||||
"joueur vs botidiot",
|
|
||||||
"joueur vs bot alpha",
|
|
||||||
"joueur vs bot divin",
|
|
||||||
"Arène"
|
|
||||||
};
|
|
||||||
|
|
||||||
int choice = JOptionPane.showOptionDialog(
|
int choice = JOptionPane.showOptionDialog(
|
||||||
null,
|
|
||||||
"Choisissez un mode de jeu :",
|
|
||||||
"Avalam - Mode de jeu",
|
|
||||||
JOptionPane.DEFAULT_OPTION,
|
|
||||||
JOptionPane.QUESTION_MESSAGE,
|
|
||||||
null,
|
|
||||||
options,
|
|
||||||
options[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (choice == -1) System.exit(0);
|
|
||||||
|
|
||||||
// Mode Arène
|
|
||||||
if (choice == 4) {
|
|
||||||
new ArenaWindow();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GameMode mode;
|
|
||||||
if (choice == 1) mode = GameMode.PVBOT;
|
|
||||||
else if (choice == 2) mode = GameMode.PVALPHA;
|
|
||||||
else if (choice == 3) mode = GameMode.PVGOD;
|
|
||||||
else mode = GameMode.PVP;
|
|
||||||
|
|
||||||
// Pour ALPHA et GOD : demander une profondeur
|
|
||||||
if (mode == GameMode.PVALPHA || mode == GameMode.PVGOD) {
|
|
||||||
|
|
||||||
String s = JOptionPane.showInputDialog(
|
|
||||||
null,
|
null,
|
||||||
"Profondeur de recherche ?\n(Conseil 4)",
|
"Choisissez un mode de jeu :",
|
||||||
(mode == GameMode.PVGOD ? "Bot Divin (PVGOD)" : "Bot Alpha-Beta"),
|
"Avalam - Mode de jeu",
|
||||||
JOptionPane.QUESTION_MESSAGE
|
JOptionPane.DEFAULT_OPTION,
|
||||||
|
JOptionPane.QUESTION_MESSAGE,
|
||||||
|
null,
|
||||||
|
options,
|
||||||
|
options[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
int depth = 4; // défaut
|
if (choice == -1) System.exit(0);
|
||||||
if (s != null) {
|
|
||||||
try { depth = Integer.parseInt(s.trim()); }
|
GameMode mode;
|
||||||
catch (Exception ignored) { depth = 4; }
|
if (choice == 1) mode = GameMode.PVBOT;
|
||||||
} else {
|
else if (choice == 2) mode = GameMode.PVALPHA;
|
||||||
// Annulation : on revient en PVP
|
else if (choice == 3) mode = GameMode.PVGOD;
|
||||||
new AvalamWindow(GameMode.PVP);
|
else mode = GameMode.PVP;
|
||||||
|
|
||||||
|
// Pour ALPHA et GOD : demander une profondeur
|
||||||
|
if (mode == GameMode.PVALPHA || mode == GameMode.PVGOD) {
|
||||||
|
|
||||||
|
String s = JOptionPane.showInputDialog(
|
||||||
|
null,
|
||||||
|
"Profondeur de recherche ?\n(Conseil 4)",
|
||||||
|
(mode == GameMode.PVGOD ? "Bot Divin (PVGOD)" : "Bot Alpha-Beta"),
|
||||||
|
JOptionPane.QUESTION_MESSAGE
|
||||||
|
);
|
||||||
|
|
||||||
|
int depth = 4; // défaut
|
||||||
|
if (s != null) {
|
||||||
|
try { depth = Integer.parseInt(s.trim()); }
|
||||||
|
catch (Exception ignored) { depth = 4; }
|
||||||
|
} else {
|
||||||
|
// Annulation : on revient en PVP
|
||||||
|
new AvalamWindow(GameMode.PVP);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depth < 1) depth = 1;
|
||||||
|
|
||||||
|
new AvalamWindow(mode, depth);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depth < 1) depth = 1;
|
new AvalamWindow(mode);
|
||||||
|
});
|
||||||
new AvalamWindow(mode, depth);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
new AvalamWindow(mode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,180 +1,192 @@
|
|||||||
package fr.iut_fbleau.Bot;
|
package fr.iut_fbleau.Bot;
|
||||||
|
|
||||||
import fr.iut_fbleau.Avalam.*;
|
import fr.iut_fbleau.Avalam.AvalamBoard;
|
||||||
import fr.iut_fbleau.GameAPI.*;
|
import fr.iut_fbleau.Avalam.AvalamPly;
|
||||||
|
import fr.iut_fbleau.Avalam.Color;
|
||||||
|
import fr.iut_fbleau.Avalam.Tower;
|
||||||
|
import fr.iut_fbleau.GameAPI.AbstractGamePlayer;
|
||||||
|
import fr.iut_fbleau.GameAPI.AbstractPly;
|
||||||
|
import fr.iut_fbleau.GameAPI.IBoard;
|
||||||
|
import fr.iut_fbleau.GameAPI.Player;
|
||||||
|
import fr.iut_fbleau.GameAPI.Result;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bot expert pour le jeu Avalam utilisant l'algorithme Alpha-Beta.
|
* Bot "Divin" (fort) pour Avalam.
|
||||||
* * Ce bot évalue le plateau en fonction du contrôle des tours,
|
*
|
||||||
* de leur isolation (points sécurisés) et de leur vulnérabilité.
|
*
|
||||||
*/
|
* Objectif : trop fort. */
|
||||||
public class DivineBot extends AbstractGamePlayer {
|
public class DivineBot{
|
||||||
|
|
||||||
/** Joueur contrôlé par le bot. */
|
|
||||||
private final Player me;
|
private final Player me;
|
||||||
|
|
||||||
/** Profondeur de recherche dans l'arbre des coups. */
|
|
||||||
private final int maxDepth;
|
private final int maxDepth;
|
||||||
|
|
||||||
/** Générateur aléatoire pour choisir entre des coups d'égale valeur. */
|
|
||||||
private final Random rng = new Random();
|
private final Random rng = new Random();
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructeur du DivineBot.
|
|
||||||
* @param p le joueur (P1 ou P2).
|
|
||||||
* @param maxDepth la profondeur d'anticipation.
|
|
||||||
*/
|
|
||||||
public DivineBot(Player p, int maxDepth) {
|
public DivineBot(Player p, int maxDepth) {
|
||||||
super(p);
|
super(p);
|
||||||
this.me = p;
|
this.me = p;
|
||||||
this.maxDepth = maxDepth;
|
this.maxDepth = Math.max(1, maxDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ===================== COUP À JOUER =====================
|
||||||
* Calcule et retourne le meilleur coup possible.
|
|
||||||
* @param board l'état actuel du jeu.
|
|
||||||
* @return le coup sélectionné.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public AbstractPly giveYourMove(IBoard board) {
|
public AbstractPly giveYourMove(IBoard board) {
|
||||||
|
|
||||||
if (board == null || board.isGameOver()) return null;
|
if (board == null || board.isGameOver()) return null;
|
||||||
|
|
||||||
List<AbstractPly> moves = listMoves(board);
|
List<AbstractPly> moves = listMoves(board);
|
||||||
int bestValue = Integer.MIN_VALUE;
|
if (moves.isEmpty()) return null;
|
||||||
|
|
||||||
|
boolean isMax = board.getCurrentPlayer() == me;
|
||||||
|
|
||||||
|
int bestValue = isMax ? Integer.MIN_VALUE : Integer.MAX_VALUE;
|
||||||
List<AbstractPly> bestMoves = new ArrayList<>();
|
List<AbstractPly> bestMoves = new ArrayList<>();
|
||||||
|
|
||||||
|
int alpha = Integer.MIN_VALUE;
|
||||||
|
int beta = Integer.MAX_VALUE;
|
||||||
|
|
||||||
for (AbstractPly m : moves) {
|
for (AbstractPly m : moves) {
|
||||||
IBoard next = board.safeCopy();
|
IBoard next = board.safeCopy();
|
||||||
next.doPly(m);
|
next.doPly(m);
|
||||||
int value = alphaBeta(next, maxDepth - 1, Integer.MIN_VALUE, Integer.MAX_VALUE);
|
|
||||||
|
|
||||||
if (value > bestValue) {
|
int value = alphaBeta(next, maxDepth - 1, alpha, beta);
|
||||||
bestValue = value;
|
|
||||||
bestMoves.clear();
|
if (isMax) {
|
||||||
bestMoves.add(m);
|
if (value > bestValue) {
|
||||||
} else if (value == bestValue) {
|
bestValue = value;
|
||||||
bestMoves.add(m);
|
bestMoves.clear();
|
||||||
|
bestMoves.add(m);
|
||||||
|
} else if (value == bestValue) {
|
||||||
|
bestMoves.add(m);
|
||||||
|
}
|
||||||
|
alpha = Math.max(alpha, bestValue);
|
||||||
|
} else {
|
||||||
|
if (value < bestValue) {
|
||||||
|
bestValue = value;
|
||||||
|
bestMoves.clear();
|
||||||
|
bestMoves.add(m);
|
||||||
|
} else if (value == bestValue) {
|
||||||
|
bestMoves.add(m);
|
||||||
|
}
|
||||||
|
beta = Math.min(beta, bestValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bestMoves.get(rng.nextInt(bestMoves.size()));
|
return bestMoves.get(rng.nextInt(bestMoves.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ===================== ALPHA-BETA =====================
|
||||||
* Algorithme Alpha-Beta pour optimiser la recherche du meilleur score.
|
|
||||||
*/
|
|
||||||
private int alphaBeta(IBoard board, int depth, int alpha, int beta) {
|
private int alphaBeta(IBoard board, int depth, int alpha, int beta) {
|
||||||
|
|
||||||
if (board.isGameOver()) {
|
if (board.isGameOver()) {
|
||||||
Result r = board.getResult();
|
return terminalValue(board);
|
||||||
if (r == Result.DRAW) return 0;
|
|
||||||
return (r == Result.WIN == (me == Player.PLAYER1)) ? 1000000 : -1000000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depth == 0) return evaluate(board);
|
|
||||||
|
|
||||||
boolean isMax = (board.getCurrentPlayer() == me);
|
if (depth == 0) {
|
||||||
int best = isMax ? Integer.MIN_VALUE : Integer.MAX_VALUE;
|
return evaluate(board);
|
||||||
|
}
|
||||||
|
|
||||||
for (AbstractPly m : listMoves(board)) {
|
List<AbstractPly> moves = listMoves(board);
|
||||||
IBoard next = board.safeCopy();
|
if (moves.isEmpty()) {
|
||||||
next.doPly(m);
|
return evaluate(board);
|
||||||
int val = alphaBeta(next, depth - 1, alpha, beta);
|
}
|
||||||
|
|
||||||
if (isMax) {
|
boolean isMax = board.getCurrentPlayer() == me;
|
||||||
|
|
||||||
|
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);
|
best = Math.max(best, val);
|
||||||
alpha = Math.max(alpha, best);
|
alpha = Math.max(alpha, best);
|
||||||
} else {
|
|
||||||
|
if (alpha >= beta) break;
|
||||||
|
}
|
||||||
|
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);
|
best = Math.min(best, val);
|
||||||
beta = Math.min(beta, best);
|
beta = Math.min(beta, best);
|
||||||
|
|
||||||
|
if (alpha >= beta) break;
|
||||||
}
|
}
|
||||||
if (alpha >= beta) break;
|
return best;
|
||||||
}
|
}
|
||||||
return best;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================== TERMINAL =====================
|
||||||
|
|
||||||
|
private int terminalValue(IBoard board) {
|
||||||
|
Result r = board.getResult();
|
||||||
|
if (r == null) return 0;
|
||||||
|
|
||||||
|
boolean botIsP1 = (me == Player.PLAYER1);
|
||||||
|
|
||||||
|
if (r == Result.DRAW) return 0;
|
||||||
|
|
||||||
|
if (botIsP1) {
|
||||||
|
return (r == Result.WIN) ? 100000 : -100000;
|
||||||
|
} else {
|
||||||
|
return (r == Result.LOSS) ? 100000 : -100000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== ÉVALUATEUR =====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Analyse la situation actuelle et attribue un score au plateau.
|
* Évaluateur heuristique Avalam :
|
||||||
* @param board le plateau à analyser.
|
* - tours finales > quasi finales > stables > faibles
|
||||||
* @return le score (positif si avantageux pour le bot).
|
* - approximation du score final
|
||||||
*/
|
*/
|
||||||
private int evaluate(IBoard board) {
|
private int evaluate(IBoard board) {
|
||||||
|
|
||||||
if (!(board instanceof AvalamBoard)) return 0;
|
if (!(board instanceof AvalamBoard)) return 0;
|
||||||
AvalamBoard b = (AvalamBoard) board;
|
AvalamBoard b = (AvalamBoard) board;
|
||||||
|
|
||||||
Color myColor = (me == Player.PLAYER1) ? Color.YELLOW : Color.RED;
|
Color myColor = (me == Player.PLAYER1) ? Color.YELLOW : Color.RED;
|
||||||
Color oppColor = (myColor == Color.YELLOW) ? Color.RED : Color.YELLOW;
|
Color oppColor = (me == Player.PLAYER1) ? Color.RED : Color.YELLOW;
|
||||||
|
|
||||||
int score = 0;
|
int score = 0;
|
||||||
|
|
||||||
for (int r = 0; r < AvalamBoard.SIZE; r++) {
|
for (int r = 0; r < AvalamBoard.SIZE; r++) {
|
||||||
for (int c = 0; c < AvalamBoard.SIZE; c++) {
|
for (int c = 0; c < AvalamBoard.SIZE; c++) {
|
||||||
|
|
||||||
Tower t = b.getTowerAt(r, c);
|
Tower t = b.getTowerAt(r, c);
|
||||||
if (t == null || t.getHeight() == 0) continue;
|
if (t == null) continue;
|
||||||
|
|
||||||
int h = t.getHeight();
|
int h = t.getHeight();
|
||||||
int weight = 0;
|
int value;
|
||||||
|
|
||||||
// Points sécurisés (isolation ou hauteur max)
|
if (h == 5) value = 1000; // tour gagnée
|
||||||
if (h == 5 || isIsolated(b, r, c)) {
|
else if (h == 4) value = 300; // quasi gagnée
|
||||||
weight = 1000;
|
else if (h == 3) value = 120; // stable
|
||||||
} else {
|
else if (h == 2) value = 40;
|
||||||
// Gestion de la vulnérabilité face à l'ennemi
|
else value = 10;
|
||||||
if (isVulnerable(b, r, c, oppColor)) {
|
|
||||||
weight = -200;
|
if (t.getColor() == myColor) score += value;
|
||||||
} else {
|
else if (t.getColor() == oppColor) score -= value;
|
||||||
switch (h) {
|
|
||||||
case 4: weight = 400; break;
|
|
||||||
case 3: weight = 150; break;
|
|
||||||
case 2: weight = 60; break;
|
|
||||||
default: weight = 10; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.getColor() == myColor) score += weight;
|
|
||||||
else score -= weight;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ===================== OUTILS =====================
|
||||||
* Vérifie si une tour n'a plus de voisins (point bloqué).
|
|
||||||
*/
|
|
||||||
private boolean isIsolated(AvalamBoard b, int r, int c) {
|
|
||||||
for (int dr = -1; dr <= 1; dr++) {
|
|
||||||
for (int dc = -1; dc <= 1; dc++) {
|
|
||||||
if (dr == 0 && dc == 0) continue;
|
|
||||||
Tower n = b.getTowerAt(r + dr, c + dc);
|
|
||||||
if (n != null && n.getHeight() > 0) return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si l'adversaire peut capturer la tour au prochain coup.
|
|
||||||
*/
|
|
||||||
private boolean isVulnerable(AvalamBoard b, int r, int c, Color enemyColor) {
|
|
||||||
int myHeight = b.getTowerAt(r, c).getHeight();
|
|
||||||
for (int dr = -1; dr <= 1; dr++) {
|
|
||||||
for (int dc = -1; dc <= 1; dc++) {
|
|
||||||
if (dr == 0 && dc == 0) continue;
|
|
||||||
Tower n = b.getTowerAt(r + dr, c + dc);
|
|
||||||
if (n != null && n.getColor() == enemyColor && (n.getHeight() + myHeight <= 5)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Liste tous les coups possibles sur le plateau actuel.
|
|
||||||
*/
|
|
||||||
private List<AbstractPly> listMoves(IBoard board) {
|
private List<AbstractPly> listMoves(IBoard board) {
|
||||||
List<AbstractPly> moves = new ArrayList<>();
|
List<AbstractPly> moves = new ArrayList<>();
|
||||||
Iterator<AbstractPly> it = board.iterator();
|
Iterator<AbstractPly> it = board.iterator();
|
||||||
while (it.hasNext()) moves.add(it.next());
|
while (it.hasNext()) moves.add(it.next());
|
||||||
return moves;
|
return moves;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,123 +0,0 @@
|
|||||||
package fr.iut_fbleau.Tests;
|
|
||||||
|
|
||||||
import fr.iut_fbleau.GameAPI.AbstractPly;
|
|
||||||
import fr.iut_fbleau.GameAPI.Player;
|
|
||||||
|
|
||||||
import fr.iut_fbleau.Avalam.AvalamBoard;
|
|
||||||
import fr.iut_fbleau.Avalam.AvalamPly;
|
|
||||||
import fr.iut_fbleau.Avalam.Tower;
|
|
||||||
import fr.iut_fbleau.Avalam.Color;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
//import org.mockito.Mockito; //Mockito absent
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* La classe <code>AvalamBoardTest</code> test si la méthode isLegal() fonctionne comme prévu.
|
|
||||||
*/
|
|
||||||
public class AvalamBoardTest {
|
|
||||||
|
|
||||||
private Tower[][] grid;
|
|
||||||
private AvalamBoard board;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
grid = new Tower[AvalamBoard.SIZE][AvalamBoard.SIZE];
|
|
||||||
|
|
||||||
//Création des tours de tests
|
|
||||||
/* Motif
|
|
||||||
1,0,2 | 2,0,3
|
|
||||||
2,1,0 | 2,3,0
|
|
||||||
0,2,1 | 0,1,1
|
|
||||||
*/
|
|
||||||
grid[4][2] = new Tower(2, Color.YELLOW);
|
|
||||||
grid[6][2] = new Tower(3, Color.RED);
|
|
||||||
|
|
||||||
grid[4][3] = new Tower(2, Color.RED);
|
|
||||||
grid[5][3] = new Tower(3, Color.YELLOW);
|
|
||||||
|
|
||||||
grid[5][4] = new Tower(1, Color.RED);
|
|
||||||
grid[6][4] = new Tower(1, Color.YELLOW);
|
|
||||||
|
|
||||||
//Joueur courant initialisé à 1, soit jaune
|
|
||||||
board = new AvalamBoard(grid); //AvalamBoard copie la grille
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
@Test //Mockito absent
|
|
||||||
public void nonAvalamPly_returnsFalse() {
|
|
||||||
//Vérifie si l'instance est bien AvalamPly
|
|
||||||
AbstractPly fake = Mockito.mock(AbstractPly.class); //Crée une instance non-AvalamPly
|
|
||||||
assertFalse(board.isLegal(fake));
|
|
||||||
}*/
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void outOfBounds_returnsFalse() {
|
|
||||||
//Source "out of box"
|
|
||||||
AvalamPly p = new AvalamPly(Player.PLAYER1, -1, 2, 4, 2);
|
|
||||||
assertFalse(board.isLegal(p));
|
|
||||||
|
|
||||||
//Destination "out of box"
|
|
||||||
AvalamPly p2 = new AvalamPly(Player.PLAYER1, 6, 4, 9, 4);
|
|
||||||
assertFalse(board.isLegal(p2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sameCell_returnsFalse() {
|
|
||||||
AvalamPly p = new AvalamPly(Player.PLAYER1, 5, 4, 5, 4);
|
|
||||||
assertFalse(board.isLegal(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void emptySourceOrDest_returnsFalse() {
|
|
||||||
//Source null
|
|
||||||
AvalamPly p1 = new AvalamPly(Player.PLAYER1, 5, 5, 5, 4);
|
|
||||||
assertFalse(board.isLegal(p1));
|
|
||||||
|
|
||||||
//Destination null
|
|
||||||
AvalamPly p2 = new AvalamPly(Player.PLAYER1, 6, 4, 6, 3);
|
|
||||||
assertFalse(board.isLegal(p2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sourceNotOwned_returnsFalse() {
|
|
||||||
//Le joueur courant n'est pas rouge
|
|
||||||
AvalamPly p = new AvalamPly(Player.PLAYER1, 5, 4, 6, 4);
|
|
||||||
assertFalse(board.isLegal(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void notAdjacent_returnsFalse() {
|
|
||||||
AvalamPly p = new AvalamPly(Player.PLAYER1, 4, 2, 6, 2);
|
|
||||||
assertFalse(board.isLegal(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sameColor_returnsFalse() {
|
|
||||||
//La couleur des tours est identique
|
|
||||||
AvalamPly p = new AvalamPly(Player.PLAYER1, 4, 2, 5, 3);
|
|
||||||
assertFalse(board.isLegal(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void tooTallAfterMerge_returnsFalse() {
|
|
||||||
//Hauteur maximale dépassé : 3+3 = 6 > MAX_HEIGHT (5)
|
|
||||||
AvalamPly p = new AvalamPly(Player.PLAYER1, 5, 3, 6, 2);
|
|
||||||
assertFalse(board.isLegal(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void validMove_returnsTrue() {
|
|
||||||
AvalamPly p = new AvalamPly(Player.PLAYER1, 5, 3, 4, 3); //Hauteur limite à 5
|
|
||||||
assertTrue(board.isLegal(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test //À vérifier
|
|
||||||
public void currentPlayerMismatchInPlyDoesNotAffectOwnershipCheck() {
|
|
||||||
//Si le coup est construit avec le mauvais joueur
|
|
||||||
AvalamPly p = new AvalamPly(Player.PLAYER2, 4, 2, 4, 3);
|
|
||||||
assertFalse(board.isLegal(p));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user