Files
BUT3ProjetJeuGroupe/fr/iut_fbleau/Avalam/AvalamWindow.java

314 lines
9.6 KiB
Java
Raw Normal View History

2025-11-20 13:25:09 -05:00
package fr.iut_fbleau.Avalam;
import fr.iut_fbleau.Bot.AlphaBetaBot;
2026-01-30 14:11:13 +01:00
import fr.iut_fbleau.Bot.DivineBot;
2026-01-26 13:41:48 +01:00
import fr.iut_fbleau.Bot.IdiotBot;
import fr.iut_fbleau.GameAPI.AbstractPly;
2025-11-25 16:02:23 -05:00
import fr.iut_fbleau.GameAPI.Player;
import fr.iut_fbleau.GameAPI.Result;
2025-11-20 13:25:09 -05:00
import javax.swing.*;
import java.awt.*;
2025-11-20 13:25:09 -05:00
/**
* La classe <code>AvalamWindow</code>
*
2026-02-05 16:24:56 +01:00
* Fenêtre principale (interface graphique) du jeu Avalam pour tous les modes
* hors Arène :
* - joueur vs joueur (PVP)
* - joueur vs bot idiot (PVBOT)
* - joueur vs bot alpha (PVALPHA)
* - joueur vs bot divin (PVGOD)
*
* Elle contient :
2026-02-05 16:24:56 +01:00
* - le plateau (<code>BoardView</code>)
* - laffichage du score (<code>ScoreView</code>)
* - laffichage du joueur courant (<code>TurnView</code>)
*
2026-02-05 16:24:56 +01:00
* 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).
*
2026-02-05 16:24:56 +01:00
* 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 ».
*/
2025-11-20 13:25:09 -05:00
public class AvalamWindow extends JFrame {
//Attributs
/** Moteur du jeu (état + règles). */
2025-11-25 16:02:23 -05:00
private AvalamBoard board;
2026-01-26 13:41:48 +01:00
/** Vue affichant le score. */
2025-11-25 16:02:23 -05:00
private ScoreView scoreView;
2026-01-26 13:41:48 +01:00
/** Vue affichant le joueur courant. */
2025-11-25 16:02:23 -05:00
private TurnView turnView;
2026-01-26 13:41:48 +01:00
/** Vue affichant le plateau. */
2025-11-25 16:02:23 -05:00
private BoardView boardView;
2026-01-26 13:41:48 +01:00
/** Mode de jeu sélectionné. */
private final GameMode mode;
/** Profondeur de recherche utilisée (utile pour les modes avec bot intelligent et pour rejouer). */
private final int searchDepth;
2026-01-26 13:41:48 +01:00
/** Joueur contrôlé par le bot (si actif). */
private final Player botPlayer = Player.PLAYER2;
/** Bot idiot (utilisé si mode PVBOT). */
private final IdiotBot idiotBot;
/** Bot Alpha-Beta (utilisé si mode PVALPHA). */
private final AlphaBetaBot alphaBot;
2026-02-05 16:24:56 +01:00
/** Bot Divin (utilisé si mode PVGOD). */
2026-01-30 14:11:13 +01:00
private final DivineBot divineBot;
2026-01-26 13:41:48 +01:00
/** Indique si une animation de tour de bot est en cours. */
private boolean botAnimating = false;
//Constructeur
/**
* Construit la fenêtre en mode joueur vs joueur.
*/
2025-11-20 13:25:09 -05:00
public AvalamWindow() {
this(GameMode.PVP, 4);
2026-01-26 13:41:48 +01:00
}
/**
* Construit la fenêtre en fonction du mode choisi.
* Pour PVALPHA/PVGOD, la profondeur par défaut est 4.
*
2026-02-05 16:24:56 +01:00
* @param mode mode de jeu (PVP, PVBOT, PVALPHA ou PVGOD)
*/
2026-01-26 13:41:48 +01:00
public AvalamWindow(GameMode mode) {
this(mode, 4);
}
/**
* Construit la fenêtre en fonction du mode choisi.
2026-02-05 16:24:56 +01:00
* Si le mode est PVALPHA ou PVGOD, la profondeur est utilisée comme cut-off
* pour les bots Alpha-Beta et Divin.
*
2026-02-05 16:24:56 +01:00
* @param mode mode de jeu
* @param alphaDepth profondeur de recherche pour Alpha-Beta / Bot Divin
*/
public AvalamWindow(GameMode mode, int alphaDepth) {
2025-11-25 16:02:23 -05:00
super("Avalam");
2025-11-20 13:25:09 -05:00
2026-01-26 13:41:48 +01:00
this.mode = mode;
this.searchDepth = Math.max(1, alphaDepth);
2026-01-26 13:41:48 +01:00
this.idiotBot = (mode == GameMode.PVBOT) ? new IdiotBot(botPlayer) : null;
int depth = this.searchDepth;
this.alphaBot = (mode == GameMode.PVALPHA) ? new AlphaBetaBot(botPlayer, depth) : null;
2026-01-30 14:11:13 +01:00
this.divineBot = (mode == GameMode.PVGOD) ? new DivineBot(botPlayer, depth) : null;
2025-11-20 13:25:09 -05:00
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
2025-11-25 16:02:23 -05:00
setLayout(new BorderLayout());
2025-11-20 13:25:09 -05:00
2026-01-26 13:41:48 +01:00
// Chargement du plateau initial
2025-11-25 16:02:23 -05:00
Tower[][] initialGrid = BoardLoader.loadFromFile("fr/iut_fbleau/Res/Plateau.txt");
2025-11-27 13:10:03 +01:00
// Initialisation du moteur (PLAYER1 commence)
board = new AvalamBoard(initialGrid);
2026-01-26 13:41:48 +01:00
// Panneau supérieur (score + tour)
2025-11-25 16:02:23 -05:00
JPanel topPanel = new JPanel(new GridLayout(2, 1));
topPanel.setBackground(new java.awt.Color(200, 200, 200));
2025-11-25 16:02:23 -05:00
scoreView = new ScoreView(
computeScore(Color.YELLOW),
computeScore(Color.RED)
);
2025-11-25 16:02:23 -05:00
turnView = new TurnView(turnMessage());
2025-11-25 16:02:23 -05:00
topPanel.add(scoreView);
topPanel.add(turnView);
add(topPanel, BorderLayout.NORTH);
2026-01-26 13:41:48 +01:00
// Plateau
2025-11-25 16:02:23 -05:00
boardView = new BoardView(board, this::onBoardUpdated);
add(boardView, BorderLayout.CENTER);
2025-11-20 13:25:09 -05:00
2025-11-25 16:02:23 -05:00
pack();
setResizable(false);
setLocationRelativeTo(null);
2025-11-20 13:25:09 -05:00
setVisible(true);
2026-01-26 13:41:48 +01:00
// Si un jour le bot devait commencer (pas le cas ici), on le ferait jouer ici.
2026-01-26 13:41:48 +01:00
maybePlayBotTurn();
2025-11-20 13:25:09 -05:00
}
2025-11-25 16:02:23 -05:00
//Méthodes
2025-11-25 16:02:23 -05:00
/**
* Appelée après chaque coup (humain ou bot).
* Met à jour score, tour, et affiche la fin de partie.
*/
2025-11-25 16:02:23 -05:00
public void onBoardUpdated() {
scoreView.updateScores(
computeScore(Color.YELLOW),
computeScore(Color.RED)
);
turnView.setTurn(turnMessage());
if (board.isGameOver()) {
Result res = board.getResult();
int scoreJaune = computeScore(Color.YELLOW);
int scoreRouge = computeScore(Color.RED);
EndGameDialog dialog = new EndGameDialog(
this,
res,
scoreJaune,
scoreRouge,
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);
2026-01-26 13:41:48 +01:00
return;
}
// Si on est contre un bot et que cest son tour, on déclenche son animation.
maybePlayBotTurn();
}
/**
* Fait jouer le bot (idiot / alpha / divin) en deux étapes visibles :
* 1) sélection de la tour (affiche les coups légaux)
* 2) attente 1 seconde
* 3) déplacement vers la destination
*
* Le tout sans bloquer l'interface (Timer Swing).
*/
2026-01-26 13:41:48 +01:00
private void maybePlayBotTurn() {
// Mode joueur vs joueur : aucun bot
if (mode == GameMode.PVP) return;
// Sécurité
2026-01-26 13:41:48 +01:00
if (board.isGameOver()) return;
if (board.getCurrentPlayer() != botPlayer) return;
if (botAnimating) return;
// Vérifier qu'on a bien le bot correspondant
if (mode == GameMode.PVBOT && idiotBot == null) return;
if (mode == GameMode.PVALPHA && alphaBot == null) return;
2026-01-30 14:11:13 +01:00
if (mode == GameMode.PVGOD && divineBot == null) return;
2026-01-26 13:41:48 +01:00
botAnimating = true;
// Désactiver les interactions du joueur pendant le tour du bot.
boardView.setInteractionEnabled(false);
// Choix dun coup sur une copie sûre
AbstractPly botMove;
if (mode == GameMode.PVBOT) {
botMove = idiotBot.giveYourMove(board.safeCopy());
} else if (mode == GameMode.PVALPHA) {
botMove = alphaBot.giveYourMove(board.safeCopy());
} else {
// A FAIRE PLUS TARD (PVGOD)
2026-01-30 14:11:13 +01:00
botMove = divineBot.giveYourMove(board.safeCopy());
}
2026-01-26 13:41:48 +01:00
if (botMove == null) {
botAnimating = false;
boardView.setInteractionEnabled(true);
return;
}
if (!(botMove instanceof AvalamPly)) {
botAnimating = false;
boardView.setInteractionEnabled(true);
return;
2025-11-25 16:02:23 -05:00
}
2026-01-26 13:41:48 +01:00
AvalamPly ap = (AvalamPly) botMove;
// Étape 1 : sélection (comme un clic humain)
InteractionController ctrl = boardView.getController();
ctrl.onPieceClicked(ap.getXFrom(), ap.getYFrom());
boardView.refresh();
// Étape 2 : attendre puis cliquer la destination
javax.swing.Timer t = new javax.swing.Timer(1000, e -> {
// Sécurité : si la partie a changé entre temps
if (board.isGameOver() || board.getCurrentPlayer() != botPlayer) {
botAnimating = false;
boardView.setInteractionEnabled(true);
((javax.swing.Timer) e.getSource()).stop();
return;
}
ctrl.onPieceClicked(ap.getXTo(), ap.getYTo());
boardView.refresh();
botAnimating = false;
boardView.setInteractionEnabled(true);
((javax.swing.Timer) e.getSource()).stop();
});
t.setRepeats(false);
t.start();
2025-11-25 16:02:23 -05:00
}
/**
* Calcule le score d'une couleur : nombre de tours contrôlées.
*
* @param c couleur à compter
* @return nombre de tours appartenant à la couleur c
*/
2025-11-25 16:02:23 -05:00
private int computeScore(Color c) {
int score = 0;
for (int r = 0; r < AvalamBoard.SIZE; r++) {
for (int col = 0; col < AvalamBoard.SIZE; col++) {
Tower t = board.getTowerAt(r, col);
if (t != null && t.getColor() == c) {
score++;
}
}
}
return score;
}
/**
* Retourne le message affiché pour le joueur courant.
*
* @return message du tour
*/
2025-11-25 16:02:23 -05:00
private String turnMessage() {
return "Tour du joueur : " +
2025-11-25 16:02:23 -05:00
(board.getCurrentPlayer() == Player.PLAYER1 ? "Jaune" : "Rouge");
}
2026-01-26 13:41:48 +01:00
//Affichage
2025-11-20 13:25:09 -05:00
}