Compare commits
12 Commits
Plateau
...
25f8dcdc76
| Author | SHA1 | Date | |
|---|---|---|---|
| 25f8dcdc76 | |||
| 823b0fcae0 | |||
| 9f78dad6e5 | |||
| 830729ca21 | |||
| d95d52785e | |||
| ddf9b00c0a | |||
| c1e5de9ed2 | |||
| 1921b523c6 | |||
| d23aeb266f | |||
|
|
7db1583766 | ||
|
|
8ccd0229b0 | ||
| 32217b8e39 |
25
Makefile
25
Makefile
@@ -2,6 +2,10 @@
|
|||||||
SRC_DIR = fr
|
SRC_DIR = fr
|
||||||
BIN_DIR = bin
|
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 ===
|
# === Recherche automatique des fichiers .java dans tous les sous-dossiers ===
|
||||||
SOURCES := $(shell find $(SRC_DIR) -name "*.java")
|
SOURCES := $(shell find $(SRC_DIR) -name "*.java")
|
||||||
|
|
||||||
@@ -19,11 +23,20 @@ JAVAFLAGS = -cp $(BIN_DIR)
|
|||||||
all: build
|
all: build
|
||||||
|
|
||||||
# === Compilation ===
|
# === Compilation ===
|
||||||
build:
|
build: compile resources
|
||||||
|
@echo "✔ Compilation terminée."
|
||||||
|
|
||||||
|
compile:
|
||||||
@echo "===> Compilation du projet Avalam..."
|
@echo "===> Compilation du projet Avalam..."
|
||||||
@mkdir -p $(BIN_DIR)
|
@mkdir -p $(BIN_DIR)
|
||||||
@$(JC) $(JCFLAGS) $(SOURCES)
|
@$(JC) $(JCFLAGS) $(SOURCES)
|
||||||
@echo "✔ Compilation terminée."
|
|
||||||
|
# === 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 ===
|
# === Exécution ===
|
||||||
run:
|
run:
|
||||||
@@ -38,3 +51,11 @@ clean:
|
|||||||
|
|
||||||
# === Recompile + run ===
|
# === Recompile + run ===
|
||||||
re: clean build 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)/"
|
||||||
|
|||||||
64
README.md
64
README.md
@@ -10,6 +10,70 @@ 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**.
|
||||||
|
|
||||||
|
## Compilation et exécution
|
||||||
|
|
||||||
|
### 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?)
|
## 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.
|
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.
|
||||||
|
|||||||
172
fr/iut_fbleau/Avalam/AvalamBoard.java
Normal file
172
fr/iut_fbleau/Avalam/AvalamBoard.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
80
fr/iut_fbleau/Avalam/AvalamPly.java
Normal file
80
fr/iut_fbleau/Avalam/AvalamPly.java
Normal 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 + ")" +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,42 +1,140 @@
|
|||||||
package fr.iut_fbleau.Avalam;
|
package fr.iut_fbleau.Avalam;
|
||||||
|
|
||||||
import fr.iut_fbleau.Avalam.logic.*;
|
import fr.iut_fbleau.Avalam.logic.BoardLoader;
|
||||||
import fr.iut_fbleau.Avalam.ui.*;
|
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 javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fenêtre principale du jeu Avalam.
|
* 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 {
|
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() {
|
public AvalamWindow() {
|
||||||
super("Avalam - Plateau Graphique");
|
super("Avalam");
|
||||||
|
|
||||||
setSize(750, 800);
|
|
||||||
setLocationRelativeTo(null);
|
|
||||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
|
||||||
Tower[][] grid = BoardLoader.loadFromFile("fr/iut_fbleau/Res/Plateau.txt");
|
// ----------------------------------------------------------
|
||||||
GameState gs = new GameState(grid);
|
// Chargement du plateau initial depuis Plateau.txt
|
||||||
ScoreManager sm = new ScoreManager();
|
// ----------------------------------------------------------
|
||||||
|
Tower[][] initialGrid = BoardLoader.loadFromFile("fr/iut_fbleau/Res/Plateau.txt");
|
||||||
|
|
||||||
int y = sm.count(Color.COLOR1, grid);
|
board = new AvalamBoard(initialGrid); // PLAYER1 commence
|
||||||
int r = sm.count(Color.COLOR2, grid);
|
|
||||||
|
|
||||||
ScoreView scoreView = new ScoreView(y, r);
|
// ----------------------------------------------------------
|
||||||
TurnView turnView = new TurnView("Tour du joueur : Jaune");
|
// PANNEAU SCORE + TOUR
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
JPanel topPanel = new JPanel(new GridLayout(2, 1));
|
||||||
|
topPanel.setBackground(new java.awt.Color(200, 200, 200));
|
||||||
|
|
||||||
BoardView boardView = new BoardView(gs);
|
scoreView = new ScoreView(
|
||||||
|
computeScore(Color.YELLOW),
|
||||||
|
computeScore(Color.RED)
|
||||||
|
);
|
||||||
|
|
||||||
JPanel top = new JPanel(new GridLayout(2,1));
|
turnView = new TurnView(turnMessage());
|
||||||
top.add(scoreView);
|
|
||||||
top.add(turnView);
|
topPanel.add(scoreView);
|
||||||
|
topPanel.add(turnView);
|
||||||
|
|
||||||
|
add(topPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
// PLATEAU (BoardView)
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
boardView = new BoardView(board, this::onBoardUpdated);
|
||||||
|
|
||||||
add(top, BorderLayout.NORTH);
|
|
||||||
add(boardView, BorderLayout.CENTER);
|
add(boardView, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
pack();
|
||||||
|
setResizable(false);
|
||||||
|
setLocationRelativeTo(null);
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* MISES À JOUR D’APRÈ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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,23 @@
|
|||||||
package fr.iut_fbleau.Avalam;
|
package fr.iut_fbleau.Avalam;
|
||||||
|
|
||||||
/**
|
|
||||||
* L'énumération <code>Color</code> représente la couleur du sommet d'une tour Avalam.
|
|
||||||
*
|
|
||||||
* COLOR1 : couleur du Joueur Jaune
|
|
||||||
* COLOR2 : couleur du Joueur Rouge
|
|
||||||
*
|
|
||||||
* Les couleurs Swing associées peuvent être modifiées ici pour changer tout le jeu.
|
|
||||||
*
|
|
||||||
* @version 3.0
|
|
||||||
*/
|
|
||||||
public enum Color {
|
public enum Color {
|
||||||
|
|
||||||
COLOR1(java.awt.Color.YELLOW), // Joueur Jaune
|
YELLOW(255, 220, 0),
|
||||||
COLOR2(java.awt.Color.RED); // Joueur Rouge
|
RED(200, 40, 40);
|
||||||
|
|
||||||
/** Couleur Swing associée */
|
|
||||||
private final java.awt.Color swingColor;
|
private final java.awt.Color swingColor;
|
||||||
|
|
||||||
Color(java.awt.Color col) {
|
Color(int r, int g, int b) {
|
||||||
this.swingColor = col;
|
this.swingColor = new java.awt.Color(r, g, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public java.awt.Color getSwingColor() {
|
public java.awt.Color getSwingColor() {
|
||||||
return this.swingColor;
|
return swingColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public fr.iut_fbleau.GameAPI.Player toPlayer() {
|
||||||
|
return (this == YELLOW ?
|
||||||
|
fr.iut_fbleau.GameAPI.Player.PLAYER1 :
|
||||||
|
fr.iut_fbleau.GameAPI.Player.PLAYER2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
* - sa hauteur (nombre de pions)
|
||||||
*/
|
*/
|
||||||
public class Tower {
|
public class Tower {
|
||||||
//Attributs
|
|
||||||
private Color color ;
|
|
||||||
private byte height = 1 ;
|
|
||||||
|
|
||||||
//Constructeur
|
private Color color;
|
||||||
|
private int height;
|
||||||
|
|
||||||
|
/** Nouvelle tour de hauteur 1 */
|
||||||
public Tower(Color color) {
|
public Tower(Color color) {
|
||||||
this.color = color;
|
this.color = color;
|
||||||
|
this.height = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tour avec couleur et hauteur existantes */
|
||||||
|
public Tower(Color color, int height) {
|
||||||
|
this.color = color;
|
||||||
|
this.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Méthodes
|
|
||||||
public Color getColor() {
|
public Color getColor() {
|
||||||
return this.color ;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte getHeight() {
|
public int getHeight() {
|
||||||
return this.height ;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Méthode qui empile une autre tour sur l'objet sur lequel le méthode est appelée.
|
* Fusionne this (destination) avec src (source).
|
||||||
* Aucune vérification de hauteur n'est effectuée.
|
* La source monte sur la destination →
|
||||||
|
* - la couleur du sommet devient celle de src
|
||||||
|
* - la hauteur s’additionne
|
||||||
*/
|
*/
|
||||||
public void mergeTower(Tower tower) {
|
public void mergeTower(Tower src) {
|
||||||
this.color = tower.getColor();
|
this.color = src.color;
|
||||||
this.height += tower.getHeight();
|
this.height = this.height + src.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "" ;
|
return color + "(" + height + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,56 +2,56 @@ package fr.iut_fbleau.Avalam.logic;
|
|||||||
|
|
||||||
import fr.iut_fbleau.Avalam.Color;
|
import fr.iut_fbleau.Avalam.Color;
|
||||||
import fr.iut_fbleau.Avalam.Tower;
|
import fr.iut_fbleau.Avalam.Tower;
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
/**
|
import java.io.BufferedReader;
|
||||||
* La classe <code>BoardLoader</code> est responsable du chargement d'un plateau Avalam
|
import java.io.IOException;
|
||||||
* depuis un fichier texte externe (généralement Plateau.txt).
|
import java.io.InputStream;
|
||||||
*
|
import java.io.InputStreamReader;
|
||||||
* Le fichier doit contenir une matrice 9×9 de valeurs numériques séparées par des virgules :
|
|
||||||
* - 0 : case vide (trou)
|
|
||||||
* - 1 : pion appartenant au joueur COLOR1 (Jaune)
|
|
||||||
* - 2 : pion appartenant au joueur COLOR2 (Rouge)
|
|
||||||
*
|
|
||||||
* Aucun contrôle de cohérence avancé n'est effectué, le chargeur se contente
|
|
||||||
* d’interpréter les valeurs comme demandé.
|
|
||||||
*
|
|
||||||
* @author
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
|
||||||
public class BoardLoader {
|
public class BoardLoader {
|
||||||
|
|
||||||
/**
|
public static Tower[][] loadFromFile(String resourcePath) {
|
||||||
* Charge un plateau Avalam depuis un fichier.
|
|
||||||
*
|
|
||||||
* @param path chemin du fichier plateau.
|
|
||||||
* @return un tableau 9×9 contenant des objets <code>Tower</code> ou <code>null</code>.
|
|
||||||
*/
|
|
||||||
public static Tower[][] loadFromFile(String path) {
|
|
||||||
|
|
||||||
Tower[][] grid = new Tower[9][9];
|
Tower[][] grid = new Tower[9][9];
|
||||||
|
|
||||||
try (BufferedReader br = new BufferedReader(new FileReader(new File(path)))) {
|
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;
|
String line;
|
||||||
int row = 0;
|
int row = 0;
|
||||||
|
|
||||||
while ((line = br.readLine()) != null && row < 9) {
|
while ((line = reader.readLine()) != null && row < 9) {
|
||||||
|
|
||||||
String[] parts = line.split(",");
|
// 🔥 Accepte SOIT les espaces, SOIT les virgules
|
||||||
|
line = line.replace(",", " ");
|
||||||
|
String[] parts = line.trim().split("\\s+");
|
||||||
|
|
||||||
for (int col = 0; col < 9; col++) {
|
for (int col = 0; col < 9; col++) {
|
||||||
int v = Integer.parseInt(parts[col]);
|
int value = Integer.parseInt(parts[col]);
|
||||||
|
|
||||||
// Interprétation des valeurs
|
switch (value) {
|
||||||
if (v == 1) grid[row][col] = new Tower(Color.COLOR1);
|
case 1:
|
||||||
else if (v == 2) grid[row][col] = new Tower(Color.COLOR2);
|
grid[row][col] = new Tower(Color.YELLOW);
|
||||||
else grid[row][col] = null; // Case vide
|
break;
|
||||||
|
case 2:
|
||||||
|
grid[row][col] = new Tower(Color.RED);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
grid[row][col] = null;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
package fr.iut_fbleau.Avalam.logic;
|
|
||||||
|
|
||||||
import fr.iut_fbleau.Avalam.Color;
|
|
||||||
import fr.iut_fbleau.Avalam.Tower;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* La classe <code>GameState</code> représente l'état courant du jeu Avalam :
|
|
||||||
* - la grille 9×9
|
|
||||||
* - le joueur dont c'est le tour
|
|
||||||
*
|
|
||||||
* Elle ne contient aucune logique de déplacement ni de vérification des règles ;
|
|
||||||
* son rôle est uniquement de stocker et de fournir l'état du jeu.
|
|
||||||
*
|
|
||||||
* @author
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
|
||||||
public class GameState {
|
|
||||||
|
|
||||||
/** Grille du plateau : 9×9 tours ou cases vides. */
|
|
||||||
private Tower[][] grid;
|
|
||||||
|
|
||||||
/** Joueur dont c’est le tour (COLOR1 ou COLOR2). */
|
|
||||||
private Color currentPlayer = Color.COLOR1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructeur.
|
|
||||||
*
|
|
||||||
* @param grid la grille initiale du plateau.
|
|
||||||
*/
|
|
||||||
public GameState(Tower[][] grid) {
|
|
||||||
this.grid = grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retourne la tour présente à la position (r,c), ou null.
|
|
||||||
*/
|
|
||||||
public Tower get(int r, int c) {
|
|
||||||
return grid[r][c];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retourne la grille complète.
|
|
||||||
*/
|
|
||||||
public Tower[][] getGrid() {
|
|
||||||
return grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retourne le joueur dont c'est le tour.
|
|
||||||
*/
|
|
||||||
public Color getCurrentPlayer() {
|
|
||||||
return currentPlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change le joueur courant : COLOR1 → COLOR2 → COLOR1.
|
|
||||||
*/
|
|
||||||
public void switchPlayer() {
|
|
||||||
currentPlayer = (currentPlayer == Color.COLOR1) ? Color.COLOR2 : Color.COLOR1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si une position (r,c) est dans les limites du plateau.
|
|
||||||
*
|
|
||||||
* @return vrai si la position est comprise dans un plateau 9×9.
|
|
||||||
*/
|
|
||||||
public boolean isInside(int r, int c) {
|
|
||||||
return r >= 0 && r < 9 && c >= 0 && c < 9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package fr.iut_fbleau.Avalam.logic;
|
|
||||||
|
|
||||||
import fr.iut_fbleau.Avalam.Color;
|
|
||||||
import fr.iut_fbleau.Avalam.Tower;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* La classe <code>ScoreManager</code> gère le calcul des scores
|
|
||||||
* selon les règles officielles d’Avalam :
|
|
||||||
*
|
|
||||||
* Un joueur gagne 1 point par tour dont le sommet (couleur) lui appartient,
|
|
||||||
* indépendamment de la hauteur de la tour.
|
|
||||||
*
|
|
||||||
* Cette classe n’a aucune responsabilité autre que compter.
|
|
||||||
*
|
|
||||||
* @author
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
|
||||||
public class ScoreManager {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compte le nombre de tours dont le sommet appartient à la couleur donnée.
|
|
||||||
*
|
|
||||||
* @param c couleur du joueur (COLOR1 ou COLOR2)
|
|
||||||
* @param grid grille 9×9 contenant des tours ou null
|
|
||||||
* @return score du joueur
|
|
||||||
*/
|
|
||||||
public static int count(Color c, Tower[][] grid) {
|
|
||||||
int score = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 9; i++)
|
|
||||||
for (int j = 0; j < 9; j++)
|
|
||||||
if (grid[i][j] != null && grid[i][j].getColor() == c)
|
|
||||||
score++;
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package fr.iut_fbleau.Avalam.logic;
|
|
||||||
|
|
||||||
import fr.iut_fbleau.Avalam.Color;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* La classe <code>TurnManager</code> gère le déroulement des tours d’Avalam.
|
|
||||||
*
|
|
||||||
* Son rôle est simple :
|
|
||||||
* - identifier le joueur dont c’est le tour
|
|
||||||
* - passer au joueur suivant
|
|
||||||
*
|
|
||||||
* Elle ne contient pas de logique de mouvement, ni de validation.
|
|
||||||
*
|
|
||||||
* @author
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
|
||||||
public class TurnManager {
|
|
||||||
|
|
||||||
/** Joueur dont c'est le tour (COLOR1 commence la partie). */
|
|
||||||
private Color current = Color.COLOR1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retourne le joueur actuel.
|
|
||||||
*/
|
|
||||||
public Color getPlayer() {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Passe au joueur suivant.
|
|
||||||
*/
|
|
||||||
public void next() {
|
|
||||||
current = (current == Color.COLOR1) ? Color.COLOR2 : Color.COLOR1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,84 +1,136 @@
|
|||||||
package fr.iut_fbleau.Avalam.ui;
|
package fr.iut_fbleau.Avalam.ui;
|
||||||
|
|
||||||
import fr.iut_fbleau.Avalam.logic.GameState;
|
import fr.iut_fbleau.Avalam.AvalamBoard;
|
||||||
|
import fr.iut_fbleau.Avalam.Tower;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* La classe <code>BoardView</code> représente l'affichage complet du plateau Avalam.
|
* BoardView est la vue principale du plateau Avalam.
|
||||||
* Elle s’appuie sur une architecture en couches (layered pane) pour séparer proprement :
|
|
||||||
*
|
*
|
||||||
* <ul>
|
* Elle gère :
|
||||||
* <li><b>HighlightLayer</b> : les cases jouables mises en surbrillance</li>
|
* - l’affichage des tours (PieceLayer)
|
||||||
* <li><b>PieceLayer</b> : les pions affichés sous forme de boutons ronds</li>
|
* - l’affichage des coups possibles (HighlightLayer)
|
||||||
* </ul>
|
* - un fond graphique personnalisé
|
||||||
*
|
* - les clics via InteractionController
|
||||||
* La vue ne contient pas la logique de jeu : elle la délègue entièrement
|
|
||||||
* à un <code>InteractionController</code>.
|
|
||||||
*
|
|
||||||
* @author
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
*/
|
||||||
public class BoardView extends JLayeredPane {
|
public class BoardView extends JLayeredPane {
|
||||||
|
|
||||||
/** Taille des pions affichés. */
|
/** Référence au moteur Avalam */
|
||||||
private final int size = 50;
|
private AvalamBoard board;
|
||||||
|
|
||||||
/** Distance entre deux cases de la grille. */
|
/** Couche d’affichage du fond */
|
||||||
private final int spacing = 70;
|
private BackgroundLayer backgroundLayer;
|
||||||
|
|
||||||
/** Décalages pour centrer l'affichage. */
|
/** Couche d’affichage des rond verts */
|
||||||
private final int xBase = 60, yBase = 60;
|
|
||||||
|
|
||||||
/** Accès à l'état du jeu. */
|
|
||||||
private GameState state;
|
|
||||||
|
|
||||||
/** Couche d'affichage des ronds verts. */
|
|
||||||
private HighlightLayer highlightLayer;
|
private HighlightLayer highlightLayer;
|
||||||
|
|
||||||
/** Couche d'affichage des pions. */
|
/** Couche d’affichage des pièces */
|
||||||
private PieceLayer pieceLayer;
|
private PieceLayer pieceLayer;
|
||||||
|
|
||||||
/** Gestionnaire d'interactions utilisateur. */
|
/** Contrôleur des interactions */
|
||||||
private InteractionController controller;
|
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 de la vue du plateau.
|
* Constructeur.
|
||||||
*
|
|
||||||
* @param state l'état du jeu Avalam
|
|
||||||
*/
|
*/
|
||||||
public BoardView(GameState state) {
|
public BoardView(AvalamBoard board, Runnable boardUpdateCallback) {
|
||||||
this.state = state;
|
this.board = board;
|
||||||
|
this.boardUpdateCallback = boardUpdateCallback;
|
||||||
|
|
||||||
setLayout(null);
|
setLayout(null);
|
||||||
|
|
||||||
controller = new InteractionController(state);
|
// --- 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);
|
highlightLayer = new HighlightLayer(xBase, yBase, spacing, size);
|
||||||
pieceLayer = new PieceLayer();
|
|
||||||
|
|
||||||
add(highlightLayer, JLayeredPane.DEFAULT_LAYER);
|
add(highlightLayer, JLayeredPane.DEFAULT_LAYER);
|
||||||
|
|
||||||
|
// --- Couche des pièces ---
|
||||||
|
pieceLayer = new PieceLayer();
|
||||||
add(pieceLayer, JLayeredPane.PALETTE_LAYER);
|
add(pieceLayer, JLayeredPane.PALETTE_LAYER);
|
||||||
|
|
||||||
|
setPreferredSize(new Dimension(725, 725));
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Met à jour l'affichage des couches en fonction de l'état actuel du jeu.
|
* 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() {
|
public void refresh() {
|
||||||
|
|
||||||
pieceLayer.displayGrid(
|
pieceLayer.displayGrid(
|
||||||
state.getGrid(),
|
boardGrid(),
|
||||||
xBase, yBase, spacing, size,
|
xBase, yBase, spacing, size,
|
||||||
|
(r, c) -> controller.onPieceClicked(r, c)
|
||||||
// Callback appelé lorsqu’un pion est cliqué
|
|
||||||
(r, c) -> {
|
|
||||||
controller.onPieceClicked(r, c);
|
|
||||||
highlightLayer.setLegalMoves(controller.getLegalMoves());
|
|
||||||
highlightLayer.repaint();
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
highlightLayer.setLegalMoves(controller.getLegalMoves());
|
||||||
|
|
||||||
|
backgroundLayer.repaint();
|
||||||
highlightLayer.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 l’image 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,116 +1,165 @@
|
|||||||
package fr.iut_fbleau.Avalam.ui;
|
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.Avalam.Tower;
|
||||||
import fr.iut_fbleau.Avalam.logic.GameState;
|
|
||||||
|
|
||||||
import java.awt.*;
|
import fr.iut_fbleau.GameAPI.AbstractPly;
|
||||||
|
import fr.iut_fbleau.GameAPI.Player;
|
||||||
|
|
||||||
|
import java.awt.Point;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* La classe <code>InteractionController</code> gère entièrement
|
* Le contrôleur gère toute l'interaction entre l'utilisateur et le moteur Avalam.
|
||||||
* la logique d'interaction de l'utilisateur :
|
|
||||||
*
|
*
|
||||||
* <ul>
|
* Son rôle :
|
||||||
* <li>Sélection d'un pion</li>
|
* - gérer la sélection d’une tour
|
||||||
* <li>Annulation de la sélection</li>
|
* - générer les coups légaux via l’API (iterator)
|
||||||
* <li>Calcul des cases de déplacement légales</li>
|
* - valider un déplacement (isLegal)
|
||||||
* </ul>
|
* - appliquer un coup (doPly)
|
||||||
|
* - mettre à jour le plateau (via refresh demandé au BoardView)
|
||||||
*
|
*
|
||||||
* Aucun affichage n'est réalisé ici ; la vue (BoardView) récupère
|
* IMPORTANT : ce contrôleur n’affiche rien. Il envoie les infos à BoardView.
|
||||||
* simplement les données calculées pour les afficher.
|
|
||||||
*
|
|
||||||
* @author
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
*/
|
||||||
public class InteractionController {
|
public class InteractionController {
|
||||||
|
|
||||||
private GameState state;
|
private AvalamBoard board;
|
||||||
|
|
||||||
/** Position du pion sélectionné (ou -1 s'il n'y en a pas). */
|
/** Position sélectionnée (-1 si aucune) */
|
||||||
private int selectedRow = -1, selectedCol = -1;
|
private int selectedRow = -1;
|
||||||
|
private int selectedCol = -1;
|
||||||
|
|
||||||
/** Liste des déplacements possibles depuis la sélection. */
|
/** Liste des coups légaux (en Point) autour de la sélection */
|
||||||
private List<Point> legalMoves = new ArrayList<>();
|
private List<Point> legalMoves = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/** Référence à la vue pour la rafraîchir après déplacements */
|
||||||
* Constructeur.
|
private BoardView view;
|
||||||
*
|
|
||||||
* @param state état du jeu à manipuler
|
public InteractionController(AvalamBoard board, BoardView view) {
|
||||||
*/
|
this.board = board;
|
||||||
public InteractionController(GameState state) {
|
this.view = view;
|
||||||
this.state = state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Retourne les cases jouables (pour HighlightLayer). */
|
||||||
* Retourne les cases jouables calculées.
|
|
||||||
*/
|
|
||||||
public List<Point> getLegalMoves() {
|
public List<Point> getLegalMoves() {
|
||||||
return legalMoves;
|
return legalMoves;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gère le clic sur un pion donné.
|
* Appelé lorsqu’un pion est cliqué dans BoardView.
|
||||||
*
|
* Gère :
|
||||||
* @param r ligne de la case cliquée
|
* - sélection d’une tour
|
||||||
* @param c colonne de la case cliquée
|
* - désélection
|
||||||
|
* - tentative de déplacement (si clique sur un highlight)
|
||||||
*/
|
*/
|
||||||
public void onPieceClicked(int r, int c) {
|
public void onPieceClicked(int r, int c) {
|
||||||
|
|
||||||
Tower t = state.get(r, c);
|
// Si on clique la même case ⇒ désélection
|
||||||
if (t == null) return;
|
|
||||||
|
|
||||||
// Annulation si on reclique la sélection
|
|
||||||
if (r == selectedRow && c == selectedCol) {
|
if (r == selectedRow && c == selectedCol) {
|
||||||
selectedRow = -1;
|
clearSelection();
|
||||||
selectedCol = -1;
|
view.refresh();
|
||||||
legalMoves.clear();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interdiction de jouer le pion adverse
|
// Si aucune sélection : on sélectionne un pion appartenant au joueur courant
|
||||||
if (t.getColor() != state.getCurrentPlayer()) {
|
Tower t = board.getTowerAt(r, c);
|
||||||
selectedRow = -1;
|
|
||||||
selectedCol = -1;
|
if (t != null && t.getColor().toPlayer() == board.getCurrentPlayer()) {
|
||||||
legalMoves.clear();
|
selectTower(r, c);
|
||||||
|
view.refresh();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sinon : tentons de jouer un coup (déplacement)
|
||||||
|
if (selectedRow != -1 && selectedCol != -1) {
|
||||||
|
tryMove(r, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* SÉLECTION D’UNE TOUR
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
private void selectTower(int r, int c) {
|
||||||
selectedRow = r;
|
selectedRow = r;
|
||||||
selectedCol = c;
|
selectedCol = c;
|
||||||
|
|
||||||
computeLegalMoves();
|
computeLegalMoves();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void clearSelection() {
|
||||||
|
selectedRow = -1;
|
||||||
|
selectedCol = -1;
|
||||||
|
legalMoves.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calcule toutes les cases accessibles à partir du pion sélectionné.
|
* Identifie les destinations possibles depuis la tour sélectionnée.
|
||||||
|
* Utilise l’API officielle : board.iterator()
|
||||||
*/
|
*/
|
||||||
private void computeLegalMoves() {
|
private void computeLegalMoves() {
|
||||||
|
|
||||||
legalMoves.clear();
|
legalMoves.clear();
|
||||||
|
|
||||||
Tower[][] grid = state.getGrid();
|
Iterator<AbstractPly> it = board.iterator();
|
||||||
Tower src = grid[selectedRow][selectedCol];
|
|
||||||
int h = src.getHeight();
|
|
||||||
|
|
||||||
int[] d = {-1, 0, 1};
|
while (it.hasNext()) {
|
||||||
|
AbstractPly p = it.next();
|
||||||
|
|
||||||
for (int dr : d) {
|
if (!(p instanceof AvalamPly)) continue;
|
||||||
for (int dc : d) {
|
AvalamPly ap = (AvalamPly) p;
|
||||||
|
|
||||||
if (dr == 0 && dc == 0) continue;
|
// On ne garde que les coups dont la source correspond à la sélection
|
||||||
|
if (ap.getXFrom() == selectedRow && ap.getYFrom() == selectedCol) {
|
||||||
int nr = selectedRow + dr;
|
legalMoves.add(new Point(ap.getXTo(), ap.getYTo()));
|
||||||
int nc = selectedCol + dc;
|
|
||||||
|
|
||||||
if (!state.isInside(nr, nc)) continue;
|
|
||||||
|
|
||||||
Tower dest = grid[nr][nc];
|
|
||||||
if (dest == null) continue;
|
|
||||||
if (h + dest.getHeight() > 5) continue;
|
|
||||||
|
|
||||||
legalMoves.add(new Point(nr, nc));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* TENTATIVE DE DÉPLACEMENT
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tente d’exécuter un déplacement vers (r,c) si c’est 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 l’API
|
||||||
|
if (board.isLegal(ply)) {
|
||||||
|
|
||||||
|
// Appliquer via l’API
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
fr/iut_fbleau/Res/BackgroundAvalam.png
Normal file
BIN
fr/iut_fbleau/Res/BackgroundAvalam.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Reference in New Issue
Block a user