Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c0cd120b1e | |||
| fa578b86d2 | |||
| 14e5df4332 | |||
| 6eb63cacaa | |||
| fdba5f0f2f | |||
| 6269404e7d | |||
| 4e8528fd58 | |||
| fa493f2fc1 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Ignorer le répertoire bin
|
||||||
|
/bin
|
||||||
218
Diagrammes/Diagramme_Avalam.mmd
Normal file
218
Diagrammes/Diagramme_Avalam.mmd
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
---
|
||||||
|
title: Avalam - Diagramme de classes (complet)
|
||||||
|
---
|
||||||
|
classDiagram
|
||||||
|
|
||||||
|
class AvalamBoard{
|
||||||
|
+SIZE: int
|
||||||
|
-MAX_HEIGHT: int
|
||||||
|
-grid: Tower[][]
|
||||||
|
-gameOver: boolean
|
||||||
|
-result: Result
|
||||||
|
|
||||||
|
+AvalamBoard(Tower[][] initialGrid, Player startingPlayer)
|
||||||
|
+AvalamBoard(Tower[][] initialGrid)
|
||||||
|
-AvalamBoard(Tower[][] grid, Player current, boolean gameOver, Result result)
|
||||||
|
+getTowerAt(int row, int col): Tower
|
||||||
|
-inBounds(int r, int c): boolean
|
||||||
|
-areAdjacent(int r1, int c1, int r2, int c2): boolean
|
||||||
|
-colorForPlayer(Player p): Color
|
||||||
|
+isGameOver(): boolean
|
||||||
|
+getResult(): Result
|
||||||
|
+isLegal(AbstractPly c): boolean
|
||||||
|
+doPly(AbstractPly c): void
|
||||||
|
+iterator(): Iterator<AbstractPly>
|
||||||
|
+safeCopy(): IBoard
|
||||||
|
}
|
||||||
|
|
||||||
|
AvalamBoard "1" *-- "many" Tower
|
||||||
|
AvalamBoard ..> AvalamPly
|
||||||
|
|
||||||
|
class AvalamPly{
|
||||||
|
-xFrom : int
|
||||||
|
-yFrom : int
|
||||||
|
-xTo : int
|
||||||
|
-yTo : int
|
||||||
|
|
||||||
|
+AvalamPly(Player player, int xFrom, int yFrom, int xTo, int yTo)
|
||||||
|
+getXFrom(): int
|
||||||
|
+getXFrom(): int
|
||||||
|
+getXFrom(): int
|
||||||
|
+getXFrom(): int
|
||||||
|
|
||||||
|
+toString(): String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AvalamWindow{
|
||||||
|
-board : AvalamBoard
|
||||||
|
-scoreView : ScoreView
|
||||||
|
-turnView : TurnView
|
||||||
|
-boardView : BoardView
|
||||||
|
-mode: GameMode
|
||||||
|
-botPlayer: Player
|
||||||
|
-idiotBot: IdiotBot
|
||||||
|
-alphaBot: AlphaBetaBot
|
||||||
|
-divineBot: Object
|
||||||
|
-botAnimating: boolean
|
||||||
|
|
||||||
|
+AvalamWindow()
|
||||||
|
+AvalamWindow(GameMode mode)
|
||||||
|
+AvalamWindow(GameMode mode, int alphaDepth)
|
||||||
|
+onBoardUpdated(): void
|
||||||
|
-maybePlayBotTurn(): void
|
||||||
|
-computeScore(Color c): int
|
||||||
|
-turnMessage(): String
|
||||||
|
}
|
||||||
|
|
||||||
|
AvalamWindow *-- AvalamBoard
|
||||||
|
AvalamWindow *-- BoardView
|
||||||
|
AvalamWindow *-- ScoreView
|
||||||
|
AvalamWindow *-- TurnView
|
||||||
|
AvalamWindow --> GameMode
|
||||||
|
|
||||||
|
class BoardLoader{
|
||||||
|
+loadFromFile(String resourcePath): Tower[][]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BoardView{
|
||||||
|
-board: AvalamBoard
|
||||||
|
-backgroundLayer: BackgroundLayer
|
||||||
|
-highlightLayer: HighlightLayer
|
||||||
|
-pieceLayer: PieceLayer
|
||||||
|
-controller: InteractionController
|
||||||
|
-size: int
|
||||||
|
-spacing: int
|
||||||
|
-xBase: int
|
||||||
|
-yBase: int
|
||||||
|
-boardUpdateCallback: Runnable
|
||||||
|
|
||||||
|
+BoardView(AvalamBoard board, Runnable boardUpdateCallback)
|
||||||
|
+getController(): InteractionController
|
||||||
|
+setInteractionEnabled(boolean enabled): void
|
||||||
|
+onBoardUpdated(): void
|
||||||
|
+refresh(): void
|
||||||
|
-boardGrid(): Tower[][]
|
||||||
|
-boardGrid(): Tower[][]
|
||||||
|
}
|
||||||
|
|
||||||
|
BoardView *-- BackgroundLayer
|
||||||
|
BoardView *-- HighlightLayer
|
||||||
|
BoardView *-- PieceLayer
|
||||||
|
BoardView *-- InteractionController
|
||||||
|
BoardView --> AvalamBoard
|
||||||
|
|
||||||
|
class BackgroundLayer{
|
||||||
|
-img: Image
|
||||||
|
+BackgroundLayer(String resourcePath)
|
||||||
|
#paintComponent(Graphics g): void
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Color{
|
||||||
|
-YELLOW(int r, int g, int b)
|
||||||
|
-RED(int r, int g, int b)
|
||||||
|
-swingColor: java.awt.Color
|
||||||
|
+Color(int r, int g, int b)
|
||||||
|
+getSwingColor(): java.awt.Color
|
||||||
|
+toPlayer(): fr.iut_fbleau.GameAPI.Player
|
||||||
|
}
|
||||||
|
|
||||||
|
class GameMode{
|
||||||
|
PVP
|
||||||
|
PVBOT
|
||||||
|
PVALPHA
|
||||||
|
PVGOD
|
||||||
|
}
|
||||||
|
|
||||||
|
class HighlightLayer{
|
||||||
|
-xBase: int
|
||||||
|
-yBase: int
|
||||||
|
-spacing: int
|
||||||
|
-size: int
|
||||||
|
-legalMoves: List<Point>
|
||||||
|
|
||||||
|
+HighlightLayer(int xBase, int yBase, int spacing, int size)
|
||||||
|
+setLegalMoves(List<Point> moves): void
|
||||||
|
#paintComponent(Graphics g): void
|
||||||
|
}
|
||||||
|
|
||||||
|
class InteractionController{
|
||||||
|
-board: AvalamBoard
|
||||||
|
-selectedRow: int
|
||||||
|
-selectedCol: int
|
||||||
|
-legalMoves: List<Point>
|
||||||
|
-view: BoardView
|
||||||
|
|
||||||
|
+InteractionController(AvalamBoard board, BoardView view)
|
||||||
|
+onPieceClicked(int r, int c): void
|
||||||
|
+selectTower(int r, int c): void
|
||||||
|
-clearSelection(): void
|
||||||
|
-computeLegalMoves(): void
|
||||||
|
-tryMove(int r, int c): void
|
||||||
|
}
|
||||||
|
|
||||||
|
InteractionController --> AvalamBoard
|
||||||
|
InteractionController --> BoardView
|
||||||
|
|
||||||
|
class Main{
|
||||||
|
+main(String[] args): void
|
||||||
|
}
|
||||||
|
|
||||||
|
Main ..> AvalamWindow
|
||||||
|
Main ..> GameMode
|
||||||
|
|
||||||
|
class PieceButton{
|
||||||
|
-color: java.awt.Color
|
||||||
|
-height: int
|
||||||
|
-hover: boolean
|
||||||
|
+row: int
|
||||||
|
+col: int
|
||||||
|
|
||||||
|
+PieceButton(java.awt.Color c, int height, int row, int col)
|
||||||
|
#paintComponent(Graphics g): void
|
||||||
|
+contains(int x, int y): boolean
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class PieceLayer{
|
||||||
|
+PieceLayer()
|
||||||
|
+displayGrid(Tower[][] grid, int xBase, int yBase, int spacing, int size, java.util.function.BiConsumer<Integer, Integer> clickCallback): void
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScoreView{
|
||||||
|
-scoreY: JLabel
|
||||||
|
-scoreR: JLabel
|
||||||
|
|
||||||
|
+ScoreView(int y, int r)
|
||||||
|
+updateScores(int y, int r): void
|
||||||
|
}
|
||||||
|
|
||||||
|
class Tower{
|
||||||
|
-height: byte
|
||||||
|
-color: Color
|
||||||
|
|
||||||
|
+Tower(int height, Color color)
|
||||||
|
+createTower(Color color): Tower
|
||||||
|
+getHeight(): int
|
||||||
|
+getColor(): Color
|
||||||
|
+mergeTower(Tower src): void
|
||||||
|
+toString(): String
|
||||||
|
}
|
||||||
|
|
||||||
|
Tower --> Color
|
||||||
|
|
||||||
|
class TurnView{
|
||||||
|
-text: JLabel
|
||||||
|
|
||||||
|
+TurnView(String initial)
|
||||||
|
+setTurn(String s): void
|
||||||
|
}
|
||||||
|
|
||||||
|
BoardLoader ..> Tower
|
||||||
|
PieceLayer ..> Tower
|
||||||
|
PieceButton ..> Tower
|
||||||
27
Diagrammes/Diagramme_Bot.mmd
Normal file
27
Diagrammes/Diagramme_Bot.mmd
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
title: Bot - Diagramme de classes (complet)
|
||||||
|
---
|
||||||
|
classDiagram
|
||||||
|
|
||||||
|
class AlphaBetaBot{
|
||||||
|
-me: Player
|
||||||
|
-maxDepth: int
|
||||||
|
-rng: Random
|
||||||
|
|
||||||
|
+AlphaBetaBot(Player p, int maxDepth)
|
||||||
|
+giveYourMove(IBoard board): AbstractPly
|
||||||
|
-alphaBeta(IBoard board, int depth, int alpha, int beta): int
|
||||||
|
-terminalValue(IBoard board): int
|
||||||
|
-evaluate(IBoard board): int
|
||||||
|
-listMoves(IBoard board): List<AbstractPly>
|
||||||
|
}
|
||||||
|
|
||||||
|
class DivineBot{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class IdiotBot{
|
||||||
|
-rng: Random
|
||||||
|
+IdiotBot(Player p)
|
||||||
|
+giveYourMove(IBoard board): AbstractPly
|
||||||
|
}
|
||||||
73
Diagrammes/Diagramme_GameAPI
Normal file
73
Diagrammes/Diagramme_GameAPI
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
---
|
||||||
|
title: GameAPI - Diagramme de classes (complet)
|
||||||
|
---
|
||||||
|
classDiagram
|
||||||
|
|
||||||
|
class AbstractBoard{
|
||||||
|
<<abstract>>
|
||||||
|
-currentPlayer: Player
|
||||||
|
-history: Deque<AbstractPly>
|
||||||
|
|
||||||
|
+AbstractBoard(Player p, Deque<AbstractPly> h)
|
||||||
|
#setNextPlayer(): void
|
||||||
|
#addPlyToHistory(AbstractPly c): void
|
||||||
|
#removePlyFromHistory(): AbstractPly
|
||||||
|
#getLastPlyFromHistory(): AbstractPly
|
||||||
|
+getCurrentPlayer()
|
||||||
|
+isGameOver(): boolean
|
||||||
|
+getResult(): Result
|
||||||
|
+isLegal(AbstractPly c): boolean
|
||||||
|
+iterator(): Iterator<AbstractPly>
|
||||||
|
+doPly(AbstractPly c): void
|
||||||
|
+undoPly(): void
|
||||||
|
+safeCopy(): IBoard
|
||||||
|
}
|
||||||
|
|
||||||
|
class AbstractGame{
|
||||||
|
<<abstract>>
|
||||||
|
-currentBoard: IBoard
|
||||||
|
-mapPlayers: EnumMap<Player, AbstractGamePlayer>
|
||||||
|
|
||||||
|
+AbstractGame(IBoard b, EnumMap<Player,AbstractGamePlayer> m)
|
||||||
|
+run(): Result
|
||||||
|
}
|
||||||
|
|
||||||
|
class AbstractGamePlayer{
|
||||||
|
<<abstract>>
|
||||||
|
-iAm: Player
|
||||||
|
|
||||||
|
+AbstractGamePlayer(Player p)
|
||||||
|
+giveYourMove(IBoard p): AbstractPly
|
||||||
|
}
|
||||||
|
|
||||||
|
class AbstractPly{
|
||||||
|
<<abstract>>
|
||||||
|
-joueur: Player
|
||||||
|
|
||||||
|
+AbstractPly(Player j)
|
||||||
|
+getPlayer(): Player
|
||||||
|
}
|
||||||
|
|
||||||
|
class IBoard{
|
||||||
|
+getCurrentPlayer(): Player
|
||||||
|
+isGameOver(): boolean
|
||||||
|
+getResult(): Result
|
||||||
|
+isLegal(AbstractPly c): boolean
|
||||||
|
+iterator(): Iterator<AbstractPly>
|
||||||
|
+doPly(AbstractPly c): void
|
||||||
|
+undoPly(): void
|
||||||
|
+safeCopy(): IBoard
|
||||||
|
}
|
||||||
|
|
||||||
|
class Player{
|
||||||
|
<<enumerate>>
|
||||||
|
PLAYER1
|
||||||
|
PLAYER2
|
||||||
|
}
|
||||||
|
|
||||||
|
class Result{
|
||||||
|
<<enumerate>>
|
||||||
|
WIN
|
||||||
|
DRAW
|
||||||
|
LOSS
|
||||||
|
}
|
||||||
15
Makefile
15
Makefile
@@ -1,3 +1,6 @@
|
|||||||
|
# === Environnements ===
|
||||||
|
TEST_ENV = "bin:/usr/share/java/junit.jar:/usr/share/java/hamcrest-core.jar"
|
||||||
|
|
||||||
# === Répertoires ===
|
# === Répertoires ===
|
||||||
SRC_DIR = fr
|
SRC_DIR = fr
|
||||||
BIN_DIR = bin
|
BIN_DIR = bin
|
||||||
@@ -12,12 +15,16 @@ SOURCES := $(shell find $(SRC_DIR) -name "*.java")
|
|||||||
# === 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
|
||||||
JCFLAGS = -d $(BIN_DIR)
|
JCFLAGS = -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
|
||||||
@@ -43,6 +50,12 @@ run:
|
|||||||
@echo "===> Lancement du jeu Avalam..."
|
@echo "===> Lancement du jeu Avalam..."
|
||||||
@$(JAVA) $(JAVAFLAGS) $(MAIN_CLASS)
|
@$(JAVA) $(JAVAFLAGS) $(MAIN_CLASS)
|
||||||
|
|
||||||
|
# === Tests ===
|
||||||
|
test:
|
||||||
|
@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..."
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -4,9 +4,11 @@
|
|||||||
|
|
||||||
Date de création : 16/10/25
|
Date de création : 16/10/25
|
||||||
|
|
||||||
Le but est de mettre en oeuvre des jeux plus intéressants que le jeu de Nim, toujours en suivant l'API.
|
Date de rendu : 06/02/26
|
||||||
|
|
||||||
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).
|
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, 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**.
|
||||||
|
|
||||||
@@ -17,6 +19,11 @@ Le jeu de notre groupe est **Avalam**.
|
|||||||
make build
|
make build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
```bash
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
### Exécution
|
### Exécution
|
||||||
```bash
|
```bash
|
||||||
make run
|
make run
|
||||||
@@ -78,13 +85,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 officiel :
|
### Règles officielles :
|
||||||
**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.
|
||||||
|
|
||||||
**Deplacements** \
|
**Déplacements** \
|
||||||
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)** \
|
||||||
|
|||||||
@@ -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,280 +0,0 @@
|
|||||||
package fr.iut_fbleau.Avalam;
|
|
||||||
|
|
||||||
import fr.iut_fbleau.Bot.AlphaBetaBot;
|
|
||||||
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.
|
|
||||||
* Permet de sélectionner deux bots, le nombre de parties, et affiche les résultats.
|
|
||||||
*
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
|
|
||||||
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) {
|
|
||||||
// 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");
|
|
||||||
|
|
||||||
// Lancer les parties
|
|
||||||
for (int i = 1; i <= nbParties; i++) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Afficher un message de fin
|
|
||||||
JOptionPane.showMessageDialog(
|
|
||||||
this,
|
|
||||||
"Toutes les parties sont terminées !",
|
|
||||||
"Arène terminée",
|
|
||||||
JOptionPane.INFORMATION_MESSAGE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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")) {
|
|
||||||
// Pour l'instant, le Bot Divin n'est pas implémenté, on utilise IdiotBot
|
|
||||||
JOptionPane.showMessageDialog(
|
|
||||||
this,
|
|
||||||
"Le Bot Divin n'est pas encore implémenté. Utilisation du Bot Idiot à la place.",
|
|
||||||
"Avertissement",
|
|
||||||
JOptionPane.WARNING_MESSAGE
|
|
||||||
);
|
|
||||||
return new IdiotBot(player);
|
|
||||||
}
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
37
fr/iut_fbleau/Avalam/BackgroundLayer.java
Normal file
37
fr/iut_fbleau/Avalam/BackgroundLayer.java
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,10 +61,10 @@ public class BoardLoader {
|
|||||||
|
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 1:
|
case 1:
|
||||||
grid[row][col] = new Tower(Color.YELLOW);
|
grid[row][col] = Tower.createTower(Color.YELLOW);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
grid[row][col] = new Tower(Color.RED);
|
grid[row][col] = Tower.createTower(Color.RED);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
grid[row][col] = null;
|
grid[row][col] = null;
|
||||||
|
|||||||
@@ -1,192 +1,159 @@
|
|||||||
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
|
* - l’affichage du fond graphique (BackgroundLayer)
|
||||||
* - 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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 (NON IMPLEMENTE)",
|
|
||||||
"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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,33 +9,21 @@ package fr.iut_fbleau.Avalam;
|
|||||||
* - une hauteur (nombre de pions empilés)
|
* - une hauteur (nombre de pions empilés)
|
||||||
*
|
*
|
||||||
* Cette version est volontairement compatible avec le reste du projet :
|
* Cette version est volontairement compatible avec le reste du projet :
|
||||||
* - constructeur Tower(Color) utilisé par BoardLoader
|
|
||||||
* - constructeur Tower(int, Color) utilisé dans d'autres parties possibles
|
* - constructeur Tower(int, Color) utilisé dans d'autres parties possibles
|
||||||
|
* - usine createTower(Color) utilisé par BoardLoader
|
||||||
* - méthode mergeTower(Tower) utilisée par AvalamBoard
|
* - méthode mergeTower(Tower) utilisée par AvalamBoard
|
||||||
* - méthode merge(Tower) conservée (si elle est utilisée ailleurs)
|
|
||||||
*/
|
*/
|
||||||
public class Tower {
|
public class Tower {
|
||||||
|
|
||||||
//Attributs
|
//Attributs
|
||||||
|
|
||||||
/** Hauteur de la tour (nombre de pions empilés). */
|
/** Hauteur de la tour (nombre de pions empilés). */
|
||||||
private int height;
|
private byte height;
|
||||||
|
|
||||||
/** Couleur du sommet de la tour (propriétaire actuel). */
|
/** Couleur du sommet de la tour (propriétaire actuel). */
|
||||||
private Color color;
|
private Color color;
|
||||||
|
|
||||||
//Constructeur
|
//Constructeur
|
||||||
|
|
||||||
/**
|
|
||||||
* Construit une tour de hauteur 1 avec la couleur donnée.
|
|
||||||
* (Constructeur attendu par BoardLoader dans ton projet.)
|
|
||||||
*
|
|
||||||
* @param color couleur du sommet
|
|
||||||
*/
|
|
||||||
public Tower(Color color) {
|
|
||||||
this(1, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construit une tour avec une hauteur et une couleur données.
|
* Construit une tour avec une hauteur et une couleur données.
|
||||||
*
|
*
|
||||||
@@ -43,10 +31,20 @@ public class Tower {
|
|||||||
* @param color couleur du sommet
|
* @param color couleur du sommet
|
||||||
*/
|
*/
|
||||||
public Tower(int height, Color color) {
|
public Tower(int height, Color color) {
|
||||||
this.height = height;
|
this.height = (byte) height;
|
||||||
this.color = color;
|
this.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construit une tour de hauteur 1 avec la couleur donnée.
|
||||||
|
* (Constructeur attendu par BoardLoader dans le projet.)
|
||||||
|
*
|
||||||
|
* @param color couleur du sommet
|
||||||
|
*/
|
||||||
|
public static Tower createTower(Color color) {
|
||||||
|
return new Tower(1, color);
|
||||||
|
}
|
||||||
|
|
||||||
//Méthodes
|
//Méthodes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,20 +71,11 @@ public class Tower {
|
|||||||
*
|
*
|
||||||
* @param src tour source empilée sur la destination
|
* @param src tour source empilée sur la destination
|
||||||
*/
|
*/
|
||||||
public void merge(Tower src) {
|
public void mergeTower(Tower src) {
|
||||||
this.height += src.height;
|
this.height += src.height;
|
||||||
this.color = src.color;
|
this.color = src.color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Alias de merge() pour compatibilité avec d'autres classes.
|
|
||||||
*
|
|
||||||
* @param src tour source empilée sur la destination
|
|
||||||
*/
|
|
||||||
public void mergeTower(Tower src) {
|
|
||||||
merge(src);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Affichage
|
//Affichage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
123
fr/iut_fbleau/Tests/AvalamBoardTest.java
Normal file
123
fr/iut_fbleau/Tests/AvalamBoardTest.java
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
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