2 Commits

10 changed files with 116 additions and 167 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 128 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -3,6 +3,28 @@ 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
@@ -43,8 +65,6 @@ classDiagram
+toString(): String +toString(): String
} }
class AvalamWindow{ class AvalamWindow{
-board : AvalamBoard -board : AvalamBoard
-scoreView : ScoreView -scoreView : ScoreView
@@ -70,14 +90,19 @@ 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
-backgroundLayer: BackgroundLayer -backgroundLayer: BackgroundLayer
@@ -105,14 +130,6 @@ 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)
@@ -122,11 +139,17 @@ 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{
@@ -165,6 +188,7 @@ classDiagram
Main ..> AvalamWindow Main ..> AvalamWindow
Main ..> GameMode Main ..> GameMode
Main ..> ArenaWindow
class PieceButton{ class PieceButton{
-color: java.awt.Color -color: java.awt.Color

View File

@@ -17,7 +17,16 @@ classDiagram
} }
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
-terminalValue(IBoard board): int
-evaluate(IBoard board): int
-listMoves(IBoard board): List<AbstractPly>
} }
class IdiotBot{ class IdiotBot{

View File

@@ -192,55 +192,24 @@ public class ArenaWindow extends JFrame {
* @param nbParties nombre de parties à jouer * @param nbParties nombre de parties à jouer
*/ */
private void runArena(String bot1Type, String bot2Type, int depth, int nbParties) { private void runArena(String bot1Type, String bot2Type, int depth, int nbParties) {
// Vider le tableau // Créer les bots
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 bot1 = createBot(bot1Type, Player.PLAYER1, depth);
AbstractGamePlayer bot2 = createBot(bot2Type, Player.PLAYER2, depth); AbstractGamePlayer bot2 = createBot(bot2Type, Player.PLAYER2, depth);
if (bot1 == null || bot2 == null) { if (bot1 == null || bot2 == null) {
errors++; JOptionPane.showMessageDialog(this, "Erreur lors de la création des bots.");
SwingUtilities.invokeLater(() -> { return;
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 // Vider le tableau
tableModel.setRowCount(0);
results.clear();
// Charger le plateau initial
Tower[][] initialGrid = BoardLoader.loadFromFile("fr/iut_fbleau/Res/Plateau.txt"); 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); AvalamBoard board = new AvalamBoard(initialGrid, Player.PLAYER1);
ArenaGame game = new ArenaGame(board, bot1, bot2); ArenaGame game = new ArenaGame(board, bot1, bot2);
@@ -248,64 +217,28 @@ public class ArenaWindow extends JFrame {
Result result = game.run(); Result result = game.run();
String winner = getWinnerName(result, bot1Type, bot2Type); String winner = getWinnerName(result, bot1Type, bot2Type);
// Mettre à jour les statistiques // Ajouter au tableau
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[]{ tableModel.addRow(new Object[]{
"Partie " + partieNum, "Partie " + i,
bot1Type, bot1Type,
bot2Type, bot2Type,
winner winner
}); });
});
} catch (Exception e) { } catch (Exception e) {
errors++;
SwingUtilities.invokeLater(() -> {
tableModel.addRow(new Object[]{ tableModel.addRow(new Object[]{
"Partie " + partieNum, "Partie " + i,
bot1Type, bot1Type,
bot2Type, bot2Type,
"Erreur: " + e.getMessage() "Erreur: " + e.getMessage()
}); });
});
} }
} }
// Afficher les statistiques finales // Afficher un message de fin avec possibilité de quitter directement
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"}; Object[] options = {"OK", "Quitter le jeu"};
int choice = JOptionPane.showOptionDialog( int choice = JOptionPane.showOptionDialog(
this, this,
statsMessage, "Toutes les parties sont terminées !",
"Arène terminée", "Arène terminée",
JOptionPane.DEFAULT_OPTION, JOptionPane.DEFAULT_OPTION,
JOptionPane.INFORMATION_MESSAGE, JOptionPane.INFORMATION_MESSAGE,
@@ -317,10 +250,6 @@ public class ArenaWindow extends JFrame {
if (choice == 1) { if (choice == 1) {
System.exit(0); System.exit(0);
} }
});
});
arenaThread.start();
} }
/** /**

View File

@@ -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());
}
}
} }
/** /**

View File

@@ -233,6 +233,7 @@ 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 {
// A FAIRE PLUS TARD (PVGOD)
botMove = divineBot.giveYourMove(board.safeCopy()); botMove = divineBot.giveYourMove(board.safeCopy());
} }

View File

@@ -105,38 +105,22 @@ public class DivineBot extends AbstractGamePlayer {
boolean isMax = board.getCurrentPlayer() == me; boolean isMax = board.getCurrentPlayer() == me;
List<AbstractPly> moves = listMoves(board); for (AbstractPly m : listMoves(board)) {
if (moves.isEmpty()) { IBoard next = board.safeCopy();
return evaluate(board); next.doPly(m);
}
int val = alphaBeta(next, depth - 1, alpha, beta);
if (isMax) { if (isMax) {
int best = Integer.MIN_VALUE; alpha = Math.max(alpha, val);
for (AbstractPly m : moves) {
IBoard next = board.safeCopy();
next.doPly(m);
int val = alphaBeta(next, depth - 1, alpha, beta);
best = Math.max(best, val);
alpha = Math.max(alpha, best);
if (alpha >= beta) break; // Coupure Beta if (alpha >= beta) break; // Coupure Beta
}
return best;
} else { } else {
int best = Integer.MAX_VALUE; beta = Math.min(beta, val);
for (AbstractPly m : moves) {
IBoard next = board.safeCopy();
next.doPly(m);
int val = alphaBeta(next, depth - 1, alpha, beta);
best = Math.min(best, val);
beta = Math.min(beta, best);
if (alpha >= beta) break; // Coupure Alpha if (alpha >= beta) break; // Coupure Alpha
} }
return best;
} }
return isMax ? alpha : beta;
} }
/** /**