18 Commits

Author SHA1 Message Date
25f8dcdc76 Fond avalam board sans .class 2025-11-27 14:06:21 -05:00
823b0fcae0 Fond avalam board 2025-11-27 14:06:05 -05:00
9f78dad6e5 Téléverser les fichiers vers "fr/iut_fbleau/Res" 2025-11-27 20:01:28 +01:00
830729ca21 RM temp AvalamWindow 2025-11-27 13:10:03 +01:00
d95d52785e Merge pull request 'Maj BoardLoader + Makefile' (#14) from MecaniqueJeu into master
Reviewed-on: #14
2025-11-27 13:07:55 +01:00
ddf9b00c0a Maj BoardLoader + makefile - .class 2025-11-27 13:06:32 +01:00
c1e5de9ed2 Maj BoardLoader + makefile 2025-11-27 13:06:14 +01:00
1921b523c6 Jeu jouable avec fin de parti 2025-11-25 16:02:23 -05:00
d23aeb266f Merge pull request 'Regle_coup' (#12) from Regle_coup into master
Reviewed-on: #12
2025-11-24 13:01:18 +01:00
felix-vi
7db1583766 ajout de la logique des règles de coups 2025-11-23 17:29:28 +01:00
felix-vi
8ccd0229b0 Merge remote-tracking branch 'origin/master' into Regle_coup 2025-11-23 17:06:18 +01:00
27bfff9aa1 Plateau graphique v2 *score,tourJoueur,tailleTour,selection* + maj makefile,readme 2025-11-22 11:56:51 -05:00
7bb6b79d53 Plateau graphique v1 2025-11-20 13:25:09 -05:00
8cefae7da6 Merge pull request 'Classe Tower'
Logique des pions et "tour"
2025-10-16 12:00:30 +02:00
32217b8e39 debut des regle 2025-10-16 11:57:37 +02:00
a5bfa18b3a Merge remote-tracking branch 'refs/remotes/origin/master' 2025-10-16 10:58:05 +02:00
962d21d2a6 Fix orthographe readme 2025-10-16 10:57:32 +02:00
6c268ee2da Merge Modeles
Merge des modeles
2025-10-16 10:36:02 +02:00
19 changed files with 1296 additions and 74 deletions

61
Makefile Normal file
View File

@@ -0,0 +1,61 @@
# === Répertoires ===
SRC_DIR = fr
BIN_DIR = bin
# === Répertoires des ressources ===
RES_SRC = fr/iut_fbleau/Res
RES_BIN = bin/fr/iut_fbleau/Res
# === Recherche automatique des fichiers .java dans tous les sous-dossiers ===
SOURCES := $(shell find $(SRC_DIR) -name "*.java")
# === Classe principale ===
MAIN_CLASS = fr.iut_fbleau.Avalam.Main
# === Commandes Java ===
JC = javac
JCFLAGS = -d $(BIN_DIR)
JAVA = java
JAVAFLAGS = -cp $(BIN_DIR)
# === Règle par défaut ===
all: build
# === Compilation ===
build: compile resources
@echo "✔ Compilation terminée."
compile:
@echo "===> Compilation du projet Avalam..."
@mkdir -p $(BIN_DIR)
@$(JC) $(JCFLAGS) $(SOURCES)
# === Copie des ressources (.txt) dans bin ===
resources:
@echo "===> Copie des ressources..."
@mkdir -p $(RES_BIN)
@cp $(RES_SRC)/* $(RES_BIN)/
@echo "✔ Ressources copiées."
# === Exécution ===
run:
@echo "===> Lancement du jeu Avalam..."
@$(JAVA) $(JAVAFLAGS) $(MAIN_CLASS)
# === Nettoyage ===
clean:
@echo "===> Suppression des fichiers compilés..."
@rm -rf $(BIN_DIR)
@echo "✔ Nettoyage complet."
# === Recompile + run ===
re: clean build run
# === Génération de la Javadoc ===
DOC_DIR = doc
javadoc:
@echo "===> Génération de la Javadoc..."
@mkdir -p $(DOC_DIR)
@javadoc -d $(DOC_DIR) -sourcepath $(SRC_DIR) -subpackages fr.iut_fbleau.Avalam
@echo "✔ Javadoc générée dans $(DOC_DIR)/"

View File

@@ -1,6 +1,6 @@
# BUT3 Projet Jeu Groupe # BUT3 Projet Jeu Groupe
*Auteurs : AMARY Aurelien, DICK Adrien, FELIX-VIMALARATNAM Patrick, RABN Hugo* *Auteurs : AMARY Aurelien, DICK Adrien, FELIX-VIMALARATNAM Patrick, RABAN Hugo*
Date de création : 16/10/25 Date de création : 16/10/25
@@ -10,20 +10,85 @@ Dans un second temps, on fera des bots le plus efficace possible (probablement u
Le jeu de notre groupe est **Avalam**. Le jeu de notre groupe est **Avalam**.
## Présentation Avalam ## Compilation et exécution
Sur un plateau de jeu, les joueurs dispose de 24 pions chacun. Le but est de créer des petites tours avec son pions 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 avec le plus de pions sur le dessus de chaque tour gagne.
### Compilation
```bash
make build
```
### Exécution
```bash
make run
```
### Nettoyage
```bash
make clean
```
### Recompiler et exécuter
```bash
make re
```
### Générer la Javadoc
```bash
make javadoc
```
## Architecture du projet
### Structure des classes principales
- **`AvalamBoard`** : Implémentation du plateau de jeu conforme à l'API `AbstractBoard`
- Gère l'état du plateau (grille 9x9)
- Valide les règles d'Avalam
- Applique et annule les coups
- Détecte la fin de partie et calcule le résultat
- **`AvalamPly`** : Représente un coup dans le jeu
- Stocke les coordonnées de départ et d'arrivée
- Stocke la hauteur de la tour source (pour l'annulation)
- **`Tower`** : Représente une tour de pions
- Stocke la couleur du sommet et la hauteur
- **`Color`** : Énumération des couleurs des joueurs
- COLOR1 (Jaune) et COLOR2 (Rouge)
### Logique du jeu
Le plateau est représenté par une grille 9x9 où chaque case contient :
- `null` : case vide (trou)
- `ArrayList<Integer>` : tour de pions (chaque Integer = 1 pour PLAYER1, 2 pour PLAYER2)
### Règles implémentées
- ✅ Validation des limites du plateau
- ✅ Vérification que la case source n'est pas vide
- ✅ Vérification que la case destination n'est pas vide (pas de trou)
- ✅ Vérification que la destination est voisine (horizontal, vertical ou diagonal)
- ✅ Vérification que la hauteur totale après déplacement ≤ 5
- ✅ Déplacement de toute la pile (pas partiel)
- ✅ Détection de fin de partie (plus aucun coup possible)
- ✅ Calcul du résultat (nombre de tours possédées par chaque joueur)
## Présentation [Avalam](https://escaleajeux.fr/?principal=/jeu/avaxl?)
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 officiel :
**Regle 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** \ **Deplacements** \
Chaque joueur en effectue un seul, 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)** \
On déplace obligatoirement toute la pile se trouvant sur un troue (elle peut évidemment n'être constituée que d'un seul pion). Autrement dit, une pile de pions ne peut qu'augmenter, jamais diminuer. On déplace obligatoirement toute la pile se trouvant sur un trou (elle peut évidemment n'être constituée que d'un seul pion). Autrement dit, une pile de pions ne peut qu'augmenter, jamais diminuer.
**Mouvement interdit (2)** \ **Mouvement interdit (2)** \
On ne peut jamais poser de pions sur un trou inoccupé: il le reste donc définitivement. Un pion (ou une tour) isolé de tous les cotés ne pourra donc plus changer de propriétaire. On ne peut jamais poser de pions sur un trou inoccupé: il le reste donc définitivement. Un pion (ou une tour) isolé de tous les cotés ne pourra donc plus changer de propriétaire.
@@ -31,4 +96,5 @@ On ne peut jamais poser de pions sur un trou inoccupé: il le reste donc défini
**Fin de partie** \ **Fin de partie** \
Tans qu'un joueur peut effectuer un mouvement il a l'obligation de jouer, la partie ne s'achevant que lorque plus aucun déplacement n'est possible. Tans qu'un joueur peut effectuer un mouvement il a l'obligation de jouer, la partie ne s'achevant que lorque plus aucun déplacement n'est possible.
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.

View File

@@ -0,0 +1,172 @@
package fr.iut_fbleau.Avalam;
import fr.iut_fbleau.GameAPI.AbstractBoard;
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.ArrayDeque;
import java.util.Iterator;
public class AvalamBoard extends AbstractBoard {
public static final int SIZE = 9;
private static final int MAX_HEIGHT = 5;
private final Tower[][] grid;
private boolean gameOver = false;
private Result result = null;
public AvalamBoard(Tower[][] initialGrid, Player startingPlayer) {
super(startingPlayer, new ArrayDeque<>());
this.grid = new Tower[SIZE][SIZE];
for (int r = 0; r < SIZE; r++)
for (int c = 0; c < SIZE; c++)
this.grid[r][c] = initialGrid[r][c];
}
public AvalamBoard(Tower[][] initialGrid) {
this(initialGrid, Player.PLAYER1);
}
private AvalamBoard(Tower[][] grid, Player current, boolean gameOver, Result result) {
super(current, new ArrayDeque<>());
this.grid = grid;
this.gameOver = gameOver;
this.result = result;
}
public Tower getTowerAt(int row, int col) {
return inBounds(row, col) ? grid[row][col] : null;
}
private boolean inBounds(int r, int c) {
return r >= 0 && r < SIZE && c >= 0 && c < SIZE;
}
private boolean areAdjacent(int r1, int c1, int r2, int c2) {
int dr = Math.abs(r1 - r2);
int dc = Math.abs(c1 - c2);
return (dr <= 1 && dc <= 1 && !(dr == 0 && dc == 0));
}
private Color colorForPlayer(Player p) {
return (p == Player.PLAYER1 ? Color.YELLOW : Color.RED);
}
@Override
public boolean isGameOver() {
if (gameOver) return true;
Iterator<AbstractPly> it = iterator();
if (it.hasNext()) return false;
gameOver = true;
return true;
}
@Override
public Result getResult() {
if (!isGameOver()) return null;
if (result != null) return result;
int yellow = 0;
int red = 0;
for (int r = 0; r < SIZE; r++)
for (int c = 0; c < SIZE; c++) {
Tower t = grid[r][c];
if (t == null) continue;
if (t.getColor() == Color.YELLOW) yellow++;
else if (t.getColor() == Color.RED) red++;
}
if (yellow > red) result = Result.WIN;
else if (yellow < red) result = Result.LOSS;
else result = Result.DRAW;
return result;
}
@Override
public boolean isLegal(AbstractPly c) {
if (!(c instanceof AvalamPly)) return false;
AvalamPly p = (AvalamPly) c;
int xF = p.getXFrom(), yF = p.getYFrom();
int xT = p.getXTo(), yT = p.getYTo();
if (!inBounds(xF, yF) || !inBounds(xT, yT)) return false;
if (xF == xT && yF == yT) return false;
Tower src = grid[xF][yF];
Tower dst = grid[xT][yT];
if (src == null || dst == null) return false;
if (src.getColor() != colorForPlayer(getCurrentPlayer())) return false;
if (!areAdjacent(xF, yF, xT, yT)) return false;
if (src.getColor() == dst.getColor()) return false;
if (src.getHeight() + dst.getHeight() > MAX_HEIGHT) return false;
return true;
}
@Override
public void doPly(AbstractPly c) {
if (!isLegal(c)) throw new IllegalArgumentException("Coup illégal : " + c);
AvalamPly p = (AvalamPly) c;
int xF = p.getXFrom(), yF = p.getYFrom();
int xT = p.getXTo(), yT = p.getYTo();
Tower src = grid[xF][yF];
Tower dst = grid[xT][yT];
dst.mergeTower(src);
grid[xF][yF] = null;
super.doPly(c);
gameOver = false;
result = null;
}
@Override
public Iterator<AbstractPly> iterator() {
java.util.List<AbstractPly> moves = new java.util.ArrayList<>();
Player cur = getCurrentPlayer();
for (int r = 0; r < SIZE; r++) {
for (int c = 0; c < SIZE; c++) {
for (int dr = -1; dr <= 1; dr++) {
for (int dc = -1; dc <= 1; dc++) {
if (dr == 0 && dc == 0) continue;
int nr = r + dr, nc = c + dc;
AvalamPly p = new AvalamPly(cur, r, c, nr, nc);
if (isLegal(p)) moves.add(p);
}
}
}
}
return moves.iterator();
}
@Override
public IBoard safeCopy() {
Tower[][] newGrid = new Tower[SIZE][SIZE];
for (int r = 0; r < SIZE; r++)
for (int c = 0; c < SIZE; c++)
newGrid[r][c] = grid[r][c];
return new AvalamBoard(newGrid, getCurrentPlayer(), gameOver, result);
}
}

View File

@@ -0,0 +1,80 @@
package fr.iut_fbleau.Avalam;
import fr.iut_fbleau.GameAPI.AbstractPly;
import fr.iut_fbleau.GameAPI.Player;
/**
* Représente un coup (ply) dans le jeu Avalam.
*
* Un coup est défini par :
* <ul>
* <li>un joueur (PLAYER1 ou PLAYER2)</li>
* <li>une position source (xFrom, yFrom)</li>
* <li>une position destination (xTo, yTo)</li>
* </ul>
*
* Ces coordonnées seront utilisées par <code>AvalamBoard</code> pour :
* <ul>
* <li>vérifier la légalité du coup</li>
* <li>fusionner les tours concernées</li>
* <li>mettre à jour la grille</li>
* </ul>
*
* Cette classe ne contient aucune logique de vérification : tout est délégué
* à <code>AvalamBoard.isLegal()</code> et <code>AvalamBoard.doPly()</code>.
*/
public class AvalamPly extends AbstractPly {
/** Coordonnées source */
private final int xFrom;
private final int yFrom;
/** Coordonnées destination */
private final int xTo;
private final int yTo;
/**
* Constructeur principal.
*
* @param player joueur qui joue le coup
* @param xFrom ligne d'origine
* @param yFrom colonne d'origine
* @param xTo ligne de destination
* @param yTo colonne de destination
*/
public AvalamPly(Player player, int xFrom, int yFrom, int xTo, int yTo) {
super(player);
this.xFrom = xFrom;
this.yFrom = yFrom;
this.xTo = xTo;
this.yTo = yTo;
}
/** Ligne d'origine */
public int getXFrom() {
return xFrom;
}
/** Colonne d'origine */
public int getYFrom() {
return yFrom;
}
/** Ligne de destination */
public int getXTo() {
return xTo;
}
/** Colonne de destination */
public int getYTo() {
return yTo;
}
@Override
public String toString() {
return "AvalamPly{" +
"player=" + getPlayer() +
", (" + xFrom + "," + yFrom + ") -> (" + xTo + "," + yTo + ")" +
'}';
}
}

View File

@@ -0,0 +1,140 @@
package fr.iut_fbleau.Avalam;
import fr.iut_fbleau.Avalam.logic.BoardLoader;
import fr.iut_fbleau.Avalam.ui.BoardView;
import fr.iut_fbleau.Avalam.ui.ScoreView;
import fr.iut_fbleau.Avalam.ui.TurnView;
import fr.iut_fbleau.GameAPI.Player;
import fr.iut_fbleau.GameAPI.Result;
import javax.swing.*;
import java.awt.*;
/**
* Fenêtre principale du jeu Avalam.
*
* Elle contient :
* - le plateau (BoardView)
* - l'affichage du score
* - l'affichage du joueur courant
*
* Elle interagit directement avec AvalamBoard (moteur du jeu).
*/
public class AvalamWindow extends JFrame {
/** Moteur du jeu (API GameAPI) */
private AvalamBoard board;
/** Vues graphiques */
private ScoreView scoreView;
private TurnView turnView;
private BoardView boardView;
public AvalamWindow() {
super("Avalam");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
// ----------------------------------------------------------
// Chargement du plateau initial depuis Plateau.txt
// ----------------------------------------------------------
Tower[][] initialGrid = BoardLoader.loadFromFile("fr/iut_fbleau/Res/Plateau.txt");
board = new AvalamBoard(initialGrid); // PLAYER1 commence
// ----------------------------------------------------------
// PANNEAU SCORE + TOUR
// ----------------------------------------------------------
JPanel topPanel = new JPanel(new GridLayout(2, 1));
topPanel.setBackground(new java.awt.Color(200, 200, 200));
scoreView = new ScoreView(
computeScore(Color.YELLOW),
computeScore(Color.RED)
);
turnView = new TurnView(turnMessage());
topPanel.add(scoreView);
topPanel.add(turnView);
add(topPanel, BorderLayout.NORTH);
// ----------------------------------------------------------
// PLATEAU (BoardView)
// ----------------------------------------------------------
boardView = new BoardView(board, this::onBoardUpdated);
add(boardView, BorderLayout.CENTER);
pack();
setResizable(false);
setLocationRelativeTo(null);
setVisible(true);
}
/* ================================================================
* MISES À JOUR DAPRÈS LE MOTEUR
* ================================================================ */
/**
* Appelé automatiquement après chaque coup via BoardView → controller → board.
*/
public void onBoardUpdated() {
// Mise à jour du score et du joueur courant
scoreView.updateScores(
computeScore(Color.YELLOW),
computeScore(Color.RED)
);
turnView.setTurn(turnMessage());
// Détection de fin de partie
if (board.isGameOver()) {
Result res = board.getResult();
String msg;
switch (res) {
case WIN : msg = "Le joueur jaune a gagné !";
case LOSS : msg = "Le joueur rouge a gagné !";
case DRAW : msg = "Égalité !";
default : msg = "Fin de partie.";
}
JOptionPane.showMessageDialog(this, msg, "Partie terminée",
JOptionPane.INFORMATION_MESSAGE);
}
}
/* ================================================================
* OUTILS
* ================================================================ */
/**
* Calcule le score d'une couleur : nombre de tours contrôlées.
*/
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;
}
/**
* Message du joueur dont c'est le tour.
*/
private String turnMessage() {
return "Tour du joueur : " +
(board.getCurrentPlayer() == Player.PLAYER1 ? "Jaune" : "Rouge");
}
}

View File

@@ -1,6 +1,23 @@
package fr.iut_fbleau.Avalam ; package fr.iut_fbleau.Avalam;
public enum Color{ public enum Color {
RED,
YELLOW YELLOW(255, 220, 0),
} RED(200, 40, 40);
private final java.awt.Color swingColor;
Color(int r, int g, int b) {
this.swingColor = new java.awt.Color(r, g, b);
}
public java.awt.Color getSwingColor() {
return swingColor;
}
public fr.iut_fbleau.GameAPI.Player toPlayer() {
return (this == YELLOW ?
fr.iut_fbleau.GameAPI.Player.PLAYER1 :
fr.iut_fbleau.GameAPI.Player.PLAYER2);
}
}

View File

@@ -0,0 +1,7 @@
package fr.iut_fbleau.Avalam;
public class Main {
public static void main(String[] args) {
new AvalamWindow();
}
}

View File

@@ -1,41 +1,50 @@
package fr.iut_fbleau.Avalam ; package fr.iut_fbleau.Avalam;
/** /**
* La classe <code>Tower</code> stocke la couleur de son pion haut et la hauteur de la tour. * Représente une tour dans le jeu Avalam.
* *
* @version 1.0 * Une tour possède :
* @author Aurélien * - la couleur de son sommet
* Date : 16-10-25 ; 16-10-25 * - sa hauteur (nombre de pions)
* Licence : */
*/
public class Tower { public class Tower {
//Attributs
private Color color ;
private byte height = 1 ;
//Constructeur private Color color;
public Tower(Color color) { private int height;
this.color = color ;
}
//Méthodes
public Color getColor() {
return this.color ;
}
public byte getHeight() {
return this.height ;
}
/** /** Nouvelle tour de hauteur 1 */
* Méthode qui empile une autre tour sur l'objet sur lequel le méthode est appelée. public Tower(Color color) {
* Aucune vérification de hauteur n'est effectuée. this.color = color;
*/ this.height = 1;
public void mergeTower(Tower tower) { }
this.color = tower.getColor();
this.height += tower.getHeight();
}
//Affichage /** Tour avec couleur et hauteur existantes */
public String toString() { public Tower(Color color, int height) {
return "" ; this.color = color;
} this.height = height;
}
public Color getColor() {
return color;
}
public int getHeight() {
return height;
}
/**
* Fusionne this (destination) avec src (source).
* La source monte sur la destination →
* - la couleur du sommet devient celle de src
* - la hauteur sadditionne
*/
public void mergeTower(Tower src) {
this.color = src.color;
this.height = this.height + src.height;
}
@Override
public String toString() {
return color + "(" + height + ")";
}
} }

View File

@@ -0,0 +1,60 @@
package fr.iut_fbleau.Avalam.logic;
import fr.iut_fbleau.Avalam.Color;
import fr.iut_fbleau.Avalam.Tower;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class BoardLoader {
public static Tower[][] loadFromFile(String resourcePath) {
Tower[][] grid = new Tower[9][9];
InputStream in = BoardLoader.class.getResourceAsStream("/" + resourcePath);
if (in == null) {
System.err.println("❌ Ressource introuvable : /" + resourcePath);
return grid;
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
String line;
int row = 0;
while ((line = reader.readLine()) != null && row < 9) {
// 🔥 Accepte SOIT les espaces, SOIT les virgules
line = line.replace(",", " ");
String[] parts = line.trim().split("\\s+");
for (int col = 0; col < 9; col++) {
int value = Integer.parseInt(parts[col]);
switch (value) {
case 1:
grid[row][col] = new Tower(Color.YELLOW);
break;
case 2:
grid[row][col] = new Tower(Color.RED);
break;
default:
grid[row][col] = null;
break;
}
}
row++;
}
} catch (IOException e) {
e.printStackTrace();
}
return grid;
}
}

View File

@@ -0,0 +1,136 @@
package fr.iut_fbleau.Avalam.ui;
import fr.iut_fbleau.Avalam.AvalamBoard;
import fr.iut_fbleau.Avalam.Tower;
import javax.swing.*;
import java.awt.*;
/**
* BoardView est la vue principale du plateau Avalam.
*
* Elle gère :
* - laffichage des tours (PieceLayer)
* - laffichage des coups possibles (HighlightLayer)
* - un fond graphique personnalisé
* - les clics via InteractionController
*/
public class BoardView extends JLayeredPane {
/** Référence au moteur Avalam */
private AvalamBoard board;
/** Couche daffichage du fond */
private BackgroundLayer backgroundLayer;
/** Couche daffichage des rond verts */
private HighlightLayer highlightLayer;
/** Couche daffichage des pièces */
private PieceLayer pieceLayer;
/** Contrôleur des interactions */
private InteractionController controller;
/** Décalages et dimensions pour l'affichage */
private final int size = 50;
private final int spacing = 70;
private final int xBase = 60;
private final int yBase = 60;
/** Callback vers AvalamWindow pour mises à jour (score, tour, fin) */
private Runnable boardUpdateCallback;
/**
* Constructeur.
*/
public BoardView(AvalamBoard board, Runnable boardUpdateCallback) {
this.board = board;
this.boardUpdateCallback = boardUpdateCallback;
setLayout(null);
// --- Contrôleur ---
this.controller = new InteractionController(board, this);
// --- Couche fond ---
backgroundLayer = new BackgroundLayer("fr/iut_fbleau/Res/BackgroundAvalam.png");
backgroundLayer.setBounds(0, 0, 725, 725);
add(backgroundLayer, JLayeredPane.FRAME_CONTENT_LAYER);
// --- Couche highlight ---
highlightLayer = new HighlightLayer(xBase, yBase, spacing, size);
add(highlightLayer, JLayeredPane.DEFAULT_LAYER);
// --- Couche des pièces ---
pieceLayer = new PieceLayer();
add(pieceLayer, JLayeredPane.PALETTE_LAYER);
setPreferredSize(new Dimension(725, 725));
refresh();
}
/**
* Appelé par le contrôleur après un coup.
*/
public void onBoardUpdated() {
if (boardUpdateCallback != null) {
boardUpdateCallback.run();
}
}
/**
* Rafraîchit les couches visuelles.
*/
public void refresh() {
pieceLayer.displayGrid(
boardGrid(),
xBase, yBase, spacing, size,
(r, c) -> controller.onPieceClicked(r, c)
);
highlightLayer.setLegalMoves(controller.getLegalMoves());
backgroundLayer.repaint();
highlightLayer.repaint();
pieceLayer.repaint();
repaint();
}
/**
* Récupère la grille depuis le moteur.
*/
private Tower[][] boardGrid() {
Tower[][] grid = new Tower[AvalamBoard.SIZE][AvalamBoard.SIZE];
for (int r = 0; r < AvalamBoard.SIZE; r++) {
for (int c = 0; c < AvalamBoard.SIZE; c++) {
grid[r][c] = board.getTowerAt(r, c);
}
}
return grid;
}
/**
* Composant affichant limage de fond.
*/
private static class BackgroundLayer extends JComponent {
private Image img;
public BackgroundLayer(String resourcePath) {
img = Toolkit.getDefaultToolkit().getImage(
getClass().getClassLoader().getResource(resourcePath)
);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
}
}
}
}

View File

@@ -0,0 +1,74 @@
package fr.iut_fbleau.Avalam.ui;
import javax.swing.*;
import java.awt.*;
import java.util.List;
/**
* La classe <code>HighlightLayer</code> est responsable de l'affichage des
* cases de déplacement autorisées sous forme de cercles verts.
*
* Elle n'interagit pas directement avec les pièces, mais se contente
* de dessiner en arrière-plan selon une liste de positions légales.
*
* @author
* @version 1.0
*/
public class HighlightLayer extends JPanel {
private int xBase, yBase, spacing, size;
/** Liste des positions jouables (cases en vert). */
private List<Point> legalMoves;
/**
* Constructeur.
*
* @param xBase position X de base du plateau
* @param yBase position Y de base du plateau
* @param spacing espacement entre cases
* @param size taille des pions
*/
public HighlightLayer(int xBase, int yBase, int spacing, int size) {
this.xBase = xBase;
this.yBase = yBase;
this.spacing = spacing;
this.size = size;
setOpaque(false);
setBounds(0, 0, 800, 800);
}
/**
* Définit la liste des cases légales à afficher.
*/
public void setLegalMoves(List<Point> moves) {
this.legalMoves = moves;
repaint();
}
/**
* Dessine les cercles verts autour des cases autorisées.
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (legalMoves == null) return;
Graphics2D g2 = (Graphics2D) g;
g2.setColor(new Color(0, 255, 0, 120));
for (Point p : legalMoves) {
int r = p.x;
int c = p.y;
int x = xBase + c * spacing;
int y = yBase + r * spacing;
int highlight = size + 20; // Cercle plus grand que le pion
g2.fillOval(x - 10, y - 10, highlight, highlight);
}
}
}

View File

@@ -0,0 +1,165 @@
package fr.iut_fbleau.Avalam.ui;
import fr.iut_fbleau.Avalam.AvalamBoard;
import fr.iut_fbleau.Avalam.AvalamPly;
import fr.iut_fbleau.Avalam.Tower;
import fr.iut_fbleau.GameAPI.AbstractPly;
import fr.iut_fbleau.GameAPI.Player;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Le contrôleur gère toute l'interaction entre l'utilisateur et le moteur Avalam.
*
* Son rôle :
* - gérer la sélection dune tour
* - générer les coups légaux via lAPI (iterator)
* - valider un déplacement (isLegal)
* - appliquer un coup (doPly)
* - mettre à jour le plateau (via refresh demandé au BoardView)
*
* IMPORTANT : ce contrôleur naffiche rien. Il envoie les infos à BoardView.
*/
public class InteractionController {
private AvalamBoard board;
/** Position sélectionnée (-1 si aucune) */
private int selectedRow = -1;
private int selectedCol = -1;
/** Liste des coups légaux (en Point) autour de la sélection */
private List<Point> legalMoves = new ArrayList<>();
/** Référence à la vue pour la rafraîchir après déplacements */
private BoardView view;
public InteractionController(AvalamBoard board, BoardView view) {
this.board = board;
this.view = view;
}
/** Retourne les cases jouables (pour HighlightLayer). */
public List<Point> getLegalMoves() {
return legalMoves;
}
/**
* Appelé lorsquun pion est cliqué dans BoardView.
* Gère :
* - sélection dune tour
* - désélection
* - tentative de déplacement (si clique sur un highlight)
*/
public void onPieceClicked(int r, int c) {
// Si on clique la même case ⇒ désélection
if (r == selectedRow && c == selectedCol) {
clearSelection();
view.refresh();
return;
}
// Si aucune sélection : on sélectionne un pion appartenant au joueur courant
Tower t = board.getTowerAt(r, c);
if (t != null && t.getColor().toPlayer() == board.getCurrentPlayer()) {
selectTower(r, c);
view.refresh();
return;
}
// Sinon : tentons de jouer un coup (déplacement)
if (selectedRow != -1 && selectedCol != -1) {
tryMove(r, c);
}
}
/* -------------------------------------------------------------------------
* SÉLECTION DUNE TOUR
* ---------------------------------------------------------------------- */
private void selectTower(int r, int c) {
selectedRow = r;
selectedCol = c;
computeLegalMoves();
}
private void clearSelection() {
selectedRow = -1;
selectedCol = -1;
legalMoves.clear();
}
/**
* Identifie les destinations possibles depuis la tour sélectionnée.
* Utilise lAPI officielle : board.iterator()
*/
private void computeLegalMoves() {
legalMoves.clear();
Iterator<AbstractPly> it = board.iterator();
while (it.hasNext()) {
AbstractPly p = it.next();
if (!(p instanceof AvalamPly)) continue;
AvalamPly ap = (AvalamPly) p;
// On ne garde que les coups dont la source correspond à la sélection
if (ap.getXFrom() == selectedRow && ap.getYFrom() == selectedCol) {
legalMoves.add(new Point(ap.getXTo(), ap.getYTo()));
}
}
}
/* -------------------------------------------------------------------------
* TENTATIVE DE DÉPLACEMENT
* ---------------------------------------------------------------------- */
/**
* Tente dexécuter un déplacement vers (r,c) si cest un coup légal.
*/
private void tryMove(int r, int c) {
// Vérifier si (r,c) est une destination légale
boolean isLegalDest = false;
for (Point p : legalMoves) {
if (p.x == r && p.y == c) {
isLegalDest = true;
break;
}
}
if (!isLegalDest) {
clearSelection(); // clic ailleurs = désélection
view.refresh();
return;
}
// Construire le coup
Player cur = board.getCurrentPlayer();
AvalamPly ply = new AvalamPly(cur, selectedRow, selectedCol, r, c);
// Vérifier via lAPI
if (board.isLegal(ply)) {
// Appliquer via lAPI
board.doPly(ply);
// Réinitialiser la sélection
clearSelection();
// Recalcul du score + joueur courant + fin de partie (handled in window)
view.onBoardUpdated();
} else {
// Coup impossible (rare, mais pour sécurité)
clearSelection();
}
view.refresh();
}
}

View File

@@ -0,0 +1,90 @@
package fr.iut_fbleau.Avalam.ui;
import javax.swing.*;
import java.awt.*;
/**
* La classe <code>PieceButton</code> représente graphiquement une tour Avalam.
* Il s'agit d'un bouton rond :
*
* <ul>
* <li>coloré selon le joueur</li>
* <li>affichant sa hauteur</li>
* <li>avec un effet de survol visuel</li>
* </ul>
*
* Ce composant sert d'interaction principale pour cliquer les pions.
*
* @author
* @version 1.0
*/
public class PieceButton extends JButton {
private Color color;
private int height;
private boolean hover = false;
/** Position de la tour sur la grille. */
public final int row, col;
/**
* Constructeur.
*
* @param c couleur graphique du pion
* @param height hauteur de la tour
* @param row ligne du pion
* @param col colonne du pion
*/
public PieceButton(java.awt.Color c, int height, int row, int col) {
this.color = c;
this.height = height;
this.row = row;
this.col = col;
setBorderPainted(false);
setContentAreaFilled(false);
setFocusPainted(false);
setOpaque(false);
addMouseListener(new java.awt.event.MouseAdapter() {
@Override public void mouseEntered(java.awt.event.MouseEvent e) { hover = true; repaint(); }
@Override public void mouseExited (java.awt.event.MouseEvent e) { hover = false; repaint(); }
});
}
/**
* Dessine le pion rond ainsi que son chiffre au centre.
*/
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Couleur foncée lorsque survolé
g2.setColor(hover ? color.darker() : color);
g2.fillOval(0, 0, getWidth(), getHeight());
// Hauteur affichée au centre
g2.setColor(java.awt.Color.BLACK);
g2.setFont(new Font("Arial", Font.BOLD, 18));
String txt = String.valueOf(height);
FontMetrics fm = g2.getFontMetrics();
int x = (getWidth() - fm.stringWidth(txt)) / 2;
int y = (getHeight() + fm.getAscent()) / 2 - 3;
g2.drawString(txt, x, y);
g2.dispose();
}
/**
* Rend le bouton réellement rond (zone cliquable circulaire).
*/
@Override
public boolean contains(int x, int y) {
double dx = x - getWidth()/2.0;
double dy = y - getHeight()/2.0;
return dx*dx + dy*dy <= (getWidth()/2.0)*(getWidth()/2.0);
}
}

View File

@@ -0,0 +1,77 @@
package fr.iut_fbleau.Avalam.ui;
import fr.iut_fbleau.Avalam.Tower;
import javax.swing.*;
import java.awt.*;
/**
* La classe <code>PieceLayer</code> gère l'affichage des pions
* (sous forme de <code>PieceButton</code>) sur la grille.
*
* Elle s'occupe uniquement :
* <ul>
* <li>d'afficher les pièces</li>
* <li>de positionner correctement les boutons</li>
* <li>d'attacher un callback à chaque clic</li>
* </ul>
*
* Aucune logique de jeu n'est réalisée ici.
*
* @author
* @version 1.0
*/
public class PieceLayer extends JPanel {
/**
* Constructeur.
* Initialise un panneau transparent prêt à recevoir des pions.
*/
public PieceLayer() {
setLayout(null);
setOpaque(false);
setBounds(0, 0, 800, 800);
}
/**
* Affiche la grille de tours sous forme de boutons.
*
* @param grid grille 9×9 des tours
* @param xBase offset X du plateau
* @param yBase offset Y du plateau
* @param spacing espacement entre cases
* @param size taille d'un pion
* @param clickCallback fonction appelée lors dun clic sur un pion
*/
public void displayGrid(Tower[][] grid, int xBase, int yBase,
int spacing, int size,
java.util.function.BiConsumer<Integer, Integer> clickCallback) {
removeAll();
for (int r = 0; r < 9; r++) {
for (int c = 0; c < 9; c++) {
Tower t = grid[r][c];
if (t != null) {
PieceButton pb = new PieceButton(
t.getColor().getSwingColor(),
t.getHeight(),
r, c
);
pb.setBounds(xBase + c * spacing, yBase + r * spacing, size, size);
int fr = r, fc = c;
pb.addActionListener(e -> clickCallback.accept(fr, fc));
add(pb);
}
}
}
revalidate();
repaint();
}
}

View File

@@ -0,0 +1,46 @@
package fr.iut_fbleau.Avalam.ui;
import javax.swing.*;
import java.awt.*;
/**
* La classe <code>ScoreView</code> affiche les scores actuels des deux joueurs.
*
* Elle est purement graphique : aucune logique de calcul n'est présente.
*
* @author
* @version 1.0
*/
public class ScoreView extends JPanel {
private JLabel scoreY, scoreR;
/**
* Constructeur.
*
* @param y score initial du joueur jaune
* @param r score initial du joueur rouge
*/
public ScoreView(int y, int r) {
setBackground(new Color(200,200,200));
setLayout(new FlowLayout());
scoreY = new JLabel("Score Jaune : " + y);
scoreR = new JLabel("Score Rouge : " + r);
scoreY.setFont(new Font("Arial", Font.BOLD, 18));
scoreR.setFont(new Font("Arial", Font.BOLD, 18));
add(scoreY);
add(new JLabel(" | "));
add(scoreR);
}
/**
* Met à jour l'affichage des scores.
*/
public void updateScores(int y, int r) {
scoreY.setText("Score Jaune : " + y);
scoreR.setText("Score Rouge : " + r);
}
}

View File

@@ -0,0 +1,41 @@
package fr.iut_fbleau.Avalam.ui;
import javax.swing.*;
import java.awt.*;
/**
* La classe <code>TurnView</code> affiche le joueur à qui c'est le tour.
*
* Elle agit comme une simple bannière dinformation,
* mise à jour par la logique du jeu.
*
* @author
* @version 1.0
*/
public class TurnView extends JPanel {
private JLabel text;
/**
* Constructeur.
*
* @param initial message initial à afficher
*/
public TurnView(String initial) {
setBackground(new Color(220,220,220));
text = new JLabel(initial);
text.setFont(new Font("Arial", Font.BOLD, 20));
add(text);
}
/**
* Met à jour le texte affichant le joueur courant.
*
* @param s message à afficher
*/
public void setTurn(String s) {
text.setText(s);
}
}

View File

@@ -1,28 +0,0 @@
package fr.iut_fbleau.AvalamTests ;
import fr.iut_fbleau.Avalam.Tower ;
import fr.iut_fbleau.Avalam.Color ;
/**
* La classe <code>TestPion</code>
*
* @version 1.0
* @author Aurélien
* Date : 16-10-25 ; 16-10-25
* Licence :
*/
public class TestTower {
public static void main(String[] args){
Tower t1 = new Tower(Color.RED);
Tower t2 = new Tower(Color.YELLOW);
System.out.println("Vérification données :");
System.out.println("RED = " + t1.getColor());
System.out.println("1 = " + t1.getHeight());
System.out.println("\nVérification empilement :");
t1.mergeTower(t2);
System.out.println("YELLOW = " + t1.getColor());
System.out.println("2 = " + t1.getHeight());
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,9 @@
0,0,1,2,0,0,0,0,0
0,1,2,1,2,0,0,0,0
0,2,1,2,1,2,1,0,0
0,1,2,1,2,1,2,1,2
1,2,1,2,0,2,1,2,1
2,1,2,1,2,1,2,1,0
0,0,1,2,1,2,1,2,0
0,0,0,0,2,1,2,1,0
0,0,0,0,0,2,1,0,0