2025-11-20 13:25:09 -05:00
|
|
|
|
package fr.iut_fbleau.Avalam;
|
|
|
|
|
|
|
2026-01-29 13:34:40 -05:00
|
|
|
|
import fr.iut_fbleau.Bot.AlphaBetaBot;
|
|
|
|
|
|
// A FAIRE PLUS TARD (PVGOD)
|
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-22 11:56:51 -05:00
|
|
|
|
|
2025-11-20 13:25:09 -05:00
|
|
|
|
import javax.swing.*;
|
2025-11-22 11:56:51 -05:00
|
|
|
|
import java.awt.*;
|
2025-11-20 13:25:09 -05:00
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-29 13:34:40 -05:00
|
|
|
|
* La classe <code>AvalamWindow</code>
|
|
|
|
|
|
*
|
|
|
|
|
|
* Fenêtre principale (interface graphique) du jeu Avalam.
|
|
|
|
|
|
* Elle contient :
|
|
|
|
|
|
* - le plateau (BoardView)
|
|
|
|
|
|
* - l’affichage du score (ScoreView)
|
|
|
|
|
|
* - 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)
|
|
|
|
|
|
*
|
|
|
|
|
|
* @version 1.0
|
|
|
|
|
|
* Date :
|
|
|
|
|
|
* Licence :
|
|
|
|
|
|
*/
|
2025-11-20 13:25:09 -05:00
|
|
|
|
public class AvalamWindow extends JFrame {
|
|
|
|
|
|
|
2026-01-26 06:39:49 -05:00
|
|
|
|
//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 06:39:49 -05:00
|
|
|
|
|
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 06:39:49 -05:00
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
/** Joueur contrôlé par le bot (si actif). */
|
|
|
|
|
|
private final Player botPlayer = Player.PLAYER2;
|
|
|
|
|
|
|
|
|
|
|
|
/** Bot idiot (utilisé si mode PVBOT). */
|
|
|
|
|
|
private final IdiotBot idiotBot;
|
|
|
|
|
|
|
2026-01-29 13:34:40 -05:00
|
|
|
|
/** Bot Alpha-Beta (utilisé si mode PVALPHA). */
|
|
|
|
|
|
private final AlphaBetaBot alphaBot;
|
|
|
|
|
|
|
2026-01-30 14:11:13 +01:00
|
|
|
|
private final DivineBot divineBot;
|
|
|
|
|
|
|
2026-01-29 13:34:40 -05:00
|
|
|
|
// A FAIRE PLUS TARD (PVGOD)
|
|
|
|
|
|
// /** 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.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2026-01-26 13:41:48 +01:00
|
|
|
|
/** Indique si une animation de tour de bot est en cours. */
|
|
|
|
|
|
private boolean botAnimating = false;
|
|
|
|
|
|
|
2026-01-26 06:39:49 -05:00
|
|
|
|
//Constructeur
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-29 13:34:40 -05:00
|
|
|
|
* Construit la fenêtre en mode joueur vs joueur.
|
|
|
|
|
|
*/
|
2025-11-20 13:25:09 -05:00
|
|
|
|
public AvalamWindow() {
|
2026-01-29 13:34:40 -05:00
|
|
|
|
this(GameMode.PVP, 4);
|
2026-01-26 13:41:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-29 13:34:40 -05:00
|
|
|
|
* Construit la fenêtre en fonction du mode choisi.
|
|
|
|
|
|
* Pour PVALPHA/PVGOD, la profondeur par défaut est 4.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param mode mode de jeu
|
|
|
|
|
|
*/
|
2026-01-26 13:41:48 +01:00
|
|
|
|
public AvalamWindow(GameMode mode) {
|
2026-01-29 13:34:40 -05:00
|
|
|
|
this(mode, 4);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Construit la fenêtre en fonction du mode choisi.
|
|
|
|
|
|
* Si le mode est PVALPHA ou PVGOD, la profondeur est utilisée comme cut-off.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @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;
|
2026-01-29 13:34:40 -05:00
|
|
|
|
|
2026-01-26 13:41:48 +01:00
|
|
|
|
this.idiotBot = (mode == GameMode.PVBOT) ? new IdiotBot(botPlayer) : null;
|
|
|
|
|
|
|
2026-01-29 13:34:40 -05:00
|
|
|
|
int depth = Math.max(1, alphaDepth);
|
|
|
|
|
|
this.alphaBot = (mode == GameMode.PVALPHA) ? new AlphaBetaBot(botPlayer, depth) : null;
|
|
|
|
|
|
|
|
|
|
|
|
// A FAIRE PLUS TARD (PVGOD)
|
2026-01-30 14:11:13 +01:00
|
|
|
|
this.divineBot = (mode == GameMode.PVGOD) ? new DivineBot(botPlayer, depth) : null;
|
2026-01-29 13:34:40 -05:00
|
|
|
|
|
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
|
|
|
|
|
2026-01-26 06:39:49 -05:00
|
|
|
|
// Initialisation du moteur (PLAYER1 commence)
|
|
|
|
|
|
board = new AvalamBoard(initialGrid);
|
2025-11-22 11:56:51 -05:00
|
|
|
|
|
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-22 11:56:51 -05:00
|
|
|
|
|
2025-11-25 16:02:23 -05:00
|
|
|
|
scoreView = new ScoreView(
|
|
|
|
|
|
computeScore(Color.YELLOW),
|
|
|
|
|
|
computeScore(Color.RED)
|
|
|
|
|
|
);
|
2025-11-22 11:56:51 -05:00
|
|
|
|
|
2025-11-25 16:02:23 -05:00
|
|
|
|
turnView = new TurnView(turnMessage());
|
2025-11-22 11:56:51 -05:00
|
|
|
|
|
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);
|
2025-11-22 11:56:51 -05:00
|
|
|
|
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
|
|
|
|
|
2026-01-29 13:34:40 -05: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
|
|
|
|
|
2026-01-26 06:39:49 -05:00
|
|
|
|
//Méthodes
|
2025-11-25 16:02:23 -05:00
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-29 13:34:40 -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();
|
|
|
|
|
|
|
|
|
|
|
|
String msg;
|
|
|
|
|
|
switch (res) {
|
2026-01-26 06:39:49 -05:00
|
|
|
|
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;
|
2025-11-25 16:02:23 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-26 06:39:49 -05:00
|
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
|
|
this,
|
|
|
|
|
|
msg,
|
|
|
|
|
|
"Partie terminée",
|
|
|
|
|
|
JOptionPane.INFORMATION_MESSAGE
|
|
|
|
|
|
);
|
2026-01-26 13:41:48 +01:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Si on est contre un bot et que c’est son tour, on déclenche son animation.
|
|
|
|
|
|
maybePlayBotTurn();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-29 13:34:40 -05:00
|
|
|
|
* 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() {
|
|
|
|
|
|
|
2026-01-29 13:34:40 -05:00
|
|
|
|
// 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;
|
|
|
|
|
|
|
2026-01-29 13:34:40 -05:00
|
|
|
|
// Vérifier qu'on a bien le bot correspondant
|
|
|
|
|
|
if (mode == GameMode.PVBOT && idiotBot == null) return;
|
|
|
|
|
|
if (mode == GameMode.PVALPHA && alphaBot == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
// A FAIRE PLUS TARD (PVGOD)
|
2026-01-30 14:11:13 +01:00
|
|
|
|
if (mode == GameMode.PVGOD && divineBot == null) return;
|
2026-01-29 13:34:40 -05:00
|
|
|
|
|
|
|
|
|
|
// A FAIRE PLUS TARD (PVGOD)
|
|
|
|
|
|
// Pour l'instant, si PVGOD est sélectionné, on ne joue pas de coup bot.
|
|
|
|
|
|
|
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 d’un coup sur une copie sûre
|
2026-01-29 13:34:40 -05:00
|
|
|
|
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-29 13:34:40 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-29 13:34:40 -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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-29 13:34:40 -05:00
|
|
|
|
* Retourne le message affiché pour le joueur courant.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return message du tour
|
|
|
|
|
|
*/
|
2025-11-25 16:02:23 -05:00
|
|
|
|
private String turnMessage() {
|
2026-01-26 06:39:49 -05:00
|
|
|
|
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
|
|
|
|
}
|