ajout fonction en passant

This commit is contained in:
apoll 2025-04-16 13:27:02 +02:00
parent 6a59c1f851
commit cf06917fb5
4 changed files with 794 additions and 543 deletions

View File

@ -1,18 +1,29 @@
package backend;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Objects; // Importer pour Objects.hash si besoin
public class Board {
private int width;
private int height;
private ArrayList<Piece> pieces;
private final int width;
private final int height;
private final List<Piece> pieces;
private int selectedX;
private int selectedY;
private int turnNumber;
private boolean isTurnWhite;
private Set<String> highlightedPositions;
private int enPassantTargetX; // Nouvelle variable d'état
private int enPassantTargetY; // Nouvelle variable d'état
public Board(int colNum, int lineNum) {
if (colNum <= 0 || lineNum <= 0) {
throw new IllegalArgumentException("Board dimensions must be positive.");
}
this.width = colNum;
this.height = lineNum;
this.pieces = new ArrayList<>();
@ -20,53 +31,105 @@ public class Board {
this.selectedY = -1;
this.turnNumber = 0;
this.isTurnWhite = true;
Move.clearHighlightedPositions(); // Ensure no highlights at start
this.highlightedPositions = new HashSet<>();
this.enPassantTargetX = -1; // Initialisation
this.enPassantTargetY = -1; // Initialisation
}
public int getWidth() {
return this.width;
public Board(Board other) {
this.width = other.width;
this.height = other.height;
this.pieces = new ArrayList<>(other.pieces.size());
for (Piece p : other.pieces) {
this.pieces.add(new Piece(p.getX(), p.getY(), p.getType(), p.isWhite()));
}
this.selectedX = other.selectedX;
this.selectedY = other.selectedY;
this.turnNumber = other.turnNumber;
this.isTurnWhite = other.isTurnWhite;
this.highlightedPositions = new HashSet<>(other.highlightedPositions);
this.enPassantTargetX = other.enPassantTargetX; // Copier l'état en passant
this.enPassantTargetY = other.enPassantTargetY; // Copier l'état en passant
}
public int getHeight() {
return this.height;
public Board(String[] boardData) {
this(8, 8);
System.err.println("Board(String[] boardData) constructor is not fully implemented. Creates an empty 8x8 board.");
// TODO: Ajouter le parsing de enPassantTargetX/Y si sauvé dans le fichier
}
public int getTurnNumber() {
return turnNumber;
public int getWidth() { return this.width; }
public int getHeight() { return this.height; }
public int getTurnNumber() { return this.turnNumber; }
public boolean isTurnWhite() { return this.isTurnWhite; }
public int getSelectedX() { return this.selectedX; }
public int getSelectedY() { return this.selectedY; }
public int getEnPassantTargetX() { return this.enPassantTargetX; } // Getter
public int getEnPassantTargetY() { return this.enPassantTargetY; } // Getter
public List<Piece> getPieces() {
return new ArrayList<>(this.pieces);
}
public boolean isTurnWhite() {
return isTurnWhite;
Piece getPieceAt(int x, int y) {
if (x < 0 || x >= width || y < 0 || y >= height) {
return null;
}
for (Piece piece : this.pieces) {
if (piece.getX() == x && piece.getY() == y) {
return piece;
}
}
return null;
}
public int getSelectedX() {
return selectedX;
public boolean isSelected(int x, int y) {
return this.selectedX == x && this.selectedY == y;
}
public int getSelectedY() {
return selectedY;
public boolean isHighlighted(int x, int y) {
String position = x + "," + y;
return this.highlightedPositions.contains(position);
}
private void setHighlightedPositions(Piece piece) {
this.highlightedPositions.clear();
if (piece != null) {
Set<String> validMoves = Move.calculateValidMoves(this, piece);
this.highlightedPositions.addAll(validMoves);
}
}
private void clearHighlightedPositions() {
if (!this.highlightedPositions.isEmpty()) {
this.highlightedPositions.clear();
}
}
public void setPiece(boolean isWhite, PieceType type, int x, int y) {
if (x < 0 || x >= width || y < 0 || y >= height) {
System.out.println("setPiece: Out of bounds at (" + x + "," + y + ")");
System.err.println("setPiece Error: Position (" + x + "," + y + ") is out of bounds.");
return;
}
pieces.removeIf(piece -> {
if (piece.getX() == x && piece.getY() == y) {
System.out.println("setPiece: Removing piece at (" + x + "," + y + "): " + piece.getType() + ", isWhite=" + piece.isWhite());
return true;
Piece existingPiece = getPieceAt(x, y);
if (existingPiece != null) {
this.pieces.remove(existingPiece);
}
if (type != null) {
Piece newPiece = new Piece(x, y, type, isWhite);
this.pieces.add(newPiece);
}
return false;
});
System.out.println("setPiece: Adding " + type + ", isWhite=" + isWhite + " at (" + x + "," + y + ")");
pieces.add(new Piece(x, y, type, isWhite));
}
public void populateBoard() {
if (width != 8 || height != 8) {
System.err.println("populateBoard Warning: Method designed for 8x8 board, current size is " + width + "x" + height + ".");
}
cleanBoard();
for (int x = 0; x < 8; x++) {
for (int x = 0; x < width; x++) {
setPiece(true, PieceType.Pawn, x, 6);
setPiece(false, PieceType.Pawn, x, 1);
}
setPiece(true, PieceType.Rook, 0, 7);
setPiece(true, PieceType.Knight, 1, 7);
@ -76,9 +139,7 @@ public class Board {
setPiece(true, PieceType.Bishop, 5, 7);
setPiece(true, PieceType.Knight, 6, 7);
setPiece(true, PieceType.Rook, 7, 7);
for (int x = 0; x < 8; x++) {
setPiece(false, PieceType.Pawn, x, 1);
}
setPiece(false, PieceType.Rook, 0, 0);
setPiece(false, PieceType.Knight, 1, 0);
setPiece(false, PieceType.Bishop, 2, 0);
@ -90,15 +151,161 @@ public class Board {
}
public void cleanBoard() {
System.out.println("cleanBoard: Clearing all pieces");
pieces.clear();
selectedX = -1;
selectedY = -1;
turnNumber = 0;
isTurnWhite = true;
Move.clearHighlightedPositions();
this.pieces.clear();
this.selectedX = -1;
this.selectedY = -1;
this.turnNumber = 0;
this.isTurnWhite = true;
this.clearHighlightedPositions();
this.enPassantTargetX = -1; // Réinitialiser aussi ici
this.enPassantTargetY = -1; // Réinitialiser aussi ici
}
public void userTouch(int x, int y) {
if (x < 0 || x >= width || y < 0 || y >= height) {
this.selectedX = -1;
this.selectedY = -1;
this.clearHighlightedPositions();
return;
}
Piece clickedPiece = getPieceAt(x, y);
if (selectedX == -1 && selectedY == -1) {
if (clickedPiece != null) {
if (clickedPiece.isWhite() == this.isTurnWhite) {
this.selectedX = x;
this.selectedY = y;
this.setHighlightedPositions(clickedPiece);
} else {
this.clearHighlightedPositions();
}
} else {
this.clearHighlightedPositions();
}
}
else {
Piece selectedPiece = getPieceAt(selectedX, selectedY);
if (selectedPiece == null) {
System.err.println("Error: Selected piece at (" + selectedX + "," + selectedY + ") is missing!");
this.selectedX = -1;
this.selectedY = -1;
this.clearHighlightedPositions();
return;
}
if (x == selectedX && y == selectedY) {
this.selectedX = -1;
this.selectedY = -1;
this.clearHighlightedPositions();
}
else if (clickedPiece != null && clickedPiece.isWhite() == selectedPiece.isWhite()) {
this.selectedX = x;
this.selectedY = y;
this.setHighlightedPositions(clickedPiece);
}
else {
if (isHighlighted(x, y)) {
Piece capturedPiece = clickedPiece; // Peut être null pour en passant
Move move = new Move(selectedX, selectedY, x, y, selectedPiece, capturedPiece);
playMove(move);
this.selectedX = -1;
this.selectedY = -1;
this.clearHighlightedPositions();
} else {
this.selectedX = -1;
this.selectedY = -1;
this.clearHighlightedPositions();
}
}
}
}
public void playMove(Move move) {
if (move == null || move.getPieceMoved() == null) {
System.err.println("playMove Error: Invalid move object provided.");
return;
}
// *** Début Modification En Passant ***
// Réinitialiser la cible en passant AVANT de traiter le coup
int previousEnPassantX = this.enPassantTargetX;
int previousEnPassantY = this.enPassantTargetY;
this.enPassantTargetX = -1;
this.enPassantTargetY = -1;
// *** Fin Modification En Passant ***
Piece pieceToMove = getPieceAt(move.getFromX(), move.getFromY());
Piece capturedPiece = move.getPieceCaptured(); // La pièce sur la case d'arrivée
if (pieceToMove == null || pieceToMove.getType() != move.getPieceMoved().getType() || pieceToMove.isWhite() != move.getPieceMoved().isWhite()) {
System.err.println("playMove Error: Mismatch between move data and piece found at source square (" + move.getFromX() + "," + move.getFromY() + "). Aborting move.");
return;
}
// *** Début Modification En Passant : Détection de la capture ***
boolean isEnPassantCapture = false;
if (pieceToMove.getType() == PieceType.Pawn &&
move.getToX() == previousEnPassantX &&
move.getToY() == previousEnPassantY &&
capturedPiece == null) { // La case cible EP est vide
isEnPassantCapture = true;
// Trouver et retirer le pion capturé qui est DERRIERE la case cible
int capturedPawnY = move.getFromY(); // Il est sur la même ligne que le pion qui capture
Piece pawnToRemove = getPieceAt(move.getToX(), capturedPawnY);
if (pawnToRemove != null && pawnToRemove.getType() == PieceType.Pawn && pawnToRemove.isWhite() != pieceToMove.isWhite()) {
this.pieces.remove(pawnToRemove);
System.out.println("En Passant capture: removed pawn at (" + move.getToX() + "," + capturedPawnY + ")");
} else {
System.err.println("playMove Error: En passant capture failed, couldn't find pawn to remove at ("+ move.getToX() + "," + capturedPawnY + ")");
// Que faire? Annuler le coup? Pour l'instant on continue...
}
}
// *** Fin Modification En Passant : Détection de la capture ***
// Retirer la pièce capturée "normalement" (si pas en passant capture et pièce sur case cible)
if (capturedPiece != null && !isEnPassantCapture) {
boolean removed = this.pieces.remove(capturedPiece);
if (!removed) {
Piece actualPieceAtTarget = getPieceAt(move.getToX(), move.getToY());
if (actualPieceAtTarget != null && actualPieceAtTarget.getType() == capturedPiece.getType() && actualPieceAtTarget.isWhite() == capturedPiece.isWhite()) {
removed = this.pieces.remove(actualPieceAtTarget);
}
}
if (!removed) {
System.err.println("playMove Warning: Could not remove captured piece " + capturedPiece.getType() + " at (" + move.getToX() + "," + move.getToY() + ")");
}
}
// Mettre à jour la position de la pièce déplacée
pieceToMove.setPosition(move.getToX(), move.getToY());
// *** Début Modification En Passant : Détection du double pas ***
// Si le coup était un double pas de pion, définir la nouvelle cible en passant
if (pieceToMove.getType() == PieceType.Pawn && Math.abs(move.getToY() - move.getFromY()) == 2) {
this.enPassantTargetX = move.getToX();
// La case cible est celle que le pion a sautée
this.enPassantTargetY = (move.getFromY() + move.getToY()) / 2;
System.out.println("En Passant target set at (" + this.enPassantTargetX + "," + this.enPassantTargetY + ")");
}
// *** Fin Modification En Passant : Détection du double pas ***
// Mettre à jour le tour et le joueur actif
this.turnNumber++;
this.isTurnWhite = !this.isTurnWhite;
System.out.println(this.toString());
System.out.println("Turn " + this.turnNumber + ". " + (this.isTurnWhite ? "White" : "Black") + " to move.");
}
@Override
public String toString() {
String[][] boardRep = new String[height][width];
for (int y = 0; y < height; y++) {
@ -106,141 +313,41 @@ public class Board {
boardRep[y][x] = ".";
}
}
for (Piece piece : pieces) {
for (Piece piece : this.pieces) {
if (piece.getY() >= 0 && piece.getY() < height && piece.getX() >= 0 && piece.getX() < width) {
String symbol = piece.getType().getSummary();
symbol = piece.isWhite() ? symbol.toUpperCase() : symbol.toLowerCase();
boardRep[piece.getY()][piece.getX()] = symbol;
}
}
StringBuilder sb = new StringBuilder();
sb.append(" ");
for(int x=0; x<width; x++) sb.append(x).append(" ");
sb.append("\n");
for (int y = 0; y < height; y++) {
sb.append(y).append(" ");
for (int x = 0; x < width; x++) {
sb.append(boardRep[y][x]).append(" ");
}
sb.append("\n");
}
// Afficher la cible en passant si elle existe (pour debug)
if (enPassantTargetX != -1) {
sb.append("En Passant Target: (").append(enPassantTargetX).append(",").append(enPassantTargetY).append(")\n");
}
return sb.toString();
}
public ArrayList<Piece> getPieces() {
return new ArrayList<>(pieces);
}
public void userTouch(int x, int y) {
System.out.println("userTouch called with x=" + x + ", y=" + y);
if (x < 0 || x >= width || y < 0 || y >= height) {
System.out.println("Click out of bounds");
return;
}
Piece clickedPiece = getPieceAt(x, y);
if (selectedX == -1 && selectedY == -1) {
if (clickedPiece != null) {
System.out.println("Selected piece at (" + x + "," + y + "): " + clickedPiece.getType() + ", isWhite=" + clickedPiece.isWhite());
selectedX = x;
selectedY = y;
Move.setHighlightedPositions(this, clickedPiece);
} else {
System.out.println("No piece at (" + x + "," + y + ")");
}
} else {
System.out.println("Selected position: (" + selectedX + "," + selectedY + ")");
Piece selectedPiece = getPieceAt(selectedX, selectedY);
if (selectedPiece == null) {
System.out.println("No piece at (" + selectedX + "," + selectedY + ")");
selectedX = -1;
selectedY = -1;
Move.clearHighlightedPositions();
return;
}
// If clicking the same position, unselect
if (selectedX == x && selectedY == y) {
System.out.println("Unselecting piece");
selectedX = -1;
selectedY = -1;
Move.clearHighlightedPositions();
}
// If clicking another piece of the same color, select that piece instead
else if (clickedPiece != null && clickedPiece.isWhite() == selectedPiece.isWhite()) {
System.out.println("Switching selection to piece at (" + x + "," + y + "): " + clickedPiece.getType() + ", isWhite=" + clickedPiece.isWhite());
selectedX = x;
selectedY = y;
Move.setHighlightedPositions(this, clickedPiece);
}
// Otherwise, attempt to move
else {
if (!selectedPiece.canMoveTo(x, y, this)) {
System.out.println("Invalid move for " + selectedPiece.getType() + " from (" +
selectedX + "," + selectedY + ") to (" + x + "," + y + ")");
selectedX = -1;
selectedY = -1;
Move.clearHighlightedPositions();
return;
}
System.out.println("Moving piece from (" + selectedX + "," + selectedY + ") to (" + x + "," + y + ")");
Piece pieceAtDestination = getPieceAt(x, y);
if (pieceAtDestination != null) {
System.out.println("Capturing piece at destination: " + pieceAtDestination.getType() + ", isWhite=" + pieceAtDestination.isWhite());
} else {
System.out.println("No piece at destination");
}
Move move = new Move(selectedX, selectedY, x, y, selectedPiece, pieceAtDestination);
playMove(move);
selectedX = -1;
selectedY = -1;
Move.clearHighlightedPositions();
}
}
}
public boolean isSelected(int x, int y) {
return selectedX == x && selectedY == y;
}
public boolean isHighlighted(int x, int y) {
return Move.isHighlighted(x, y);
}
public void playMove(Move move) {
System.out.println("playMove: Moving from (" + move.getFromX() + "," + move.getFromY() + ") to (" +
move.getToX() + "," + move.getToY() + ")");
System.out.println("Before move - Pieces: " + pieces.size());
for (Piece piece : pieces) {
System.out.println("Piece at (" + piece.getX() + "," + piece.getY() + "): " + piece.getType() + ", isWhite=" + piece.isWhite());
}
pieces.removeIf(piece -> piece.getX() == move.getToX() && piece.getY() == move.getToY());
pieces.removeIf(piece -> piece.getX() == move.getFromX() && piece.getY() == move.getFromY());
Piece pieceMoved = move.getPieceMoved();
pieces.add(new Piece(move.getToX(), move.getToY(), pieceMoved.getType(), pieceMoved.isWhite()));
turnNumber++;
isTurnWhite = !isTurnWhite;
System.out.println("After move - Pieces: " + pieces.size());
for (Piece piece : pieces) {
System.out.println("Piece at (" + piece.getX() + "," + piece.getY() + "): " + piece.getType() + ", isWhite=" + piece.isWhite());
}
}
private Piece getPieceAt(int x, int y) {
for (Piece piece : pieces) {
if (piece.getX() == x && piece.getY() == y) {
return piece;
}
}
return null;
}
public String[] toFileRep() {
System.err.println("Board.toFileRep() is not implemented.");
// TODO: Ajouter la sauvegarde de enPassantTargetX/Y
return null;
}
public Board(String[] array) {
// TODO
public boolean undoLastMove() {
System.err.println("Board.undoLastMove() is not implemented.");
// TODO: Ajouter la restauration de enPassantTargetX/Y et du pion capturé en passant
return false;
}
public void undoLastMove() {
// TODO
}
public Board(Board board) {
// TODO
}
}

View File

@ -1,128 +1,353 @@
package backend;
import windowInterface.MyInterface;
import windowInterface.MyInterface; // Assurez-vous que ce chemin d'importation est correct
/**
* Représente la logique principale du jeu d'échecs.
* Gère l'état du plateau (Board), les joueurs (humain/IA), le déroulement des tours,
* et interagit avec l'interface utilisateur (MyInterface).
* Fonctionne dans un thread séparé pour permettre le jeu de l'IA sans bloquer l'UI.
*/
public class Game extends Thread {
private AutoPlayer aiPlayer;
private Board board;
// Dépendances
private final Board board; // L'état actuel de l'échiquier (final car l'instance Board ne change pas, seul son état interne)
private final AutoPlayer aiPlayer; // Le moteur d'IA
private final MyInterface mjf; // Référence à l'interface graphique pour les mises à jour
private MyInterface mjf;
private int COL_NUM = 8;
private int LINE_NUM = 8;
private int loopDelay = 250;
boolean[] activationAIFlags;
// Constantes de configuration
private static final int DEFAULT_WIDTH = 8;
private static final int DEFAULT_HEIGHT = 8;
private static final int DEFAULT_LOOP_DELAY = 250; // ms entre les vérifications de l'IA
// État de contrôle du jeu
private int loopDelay; // Délai actuel de la boucle (peut être modifié)
private final boolean[] activationAIFlags; // Index 0 pour Noir (isWhite=false), Index 1 pour Blanc (isWhite=true)
/**
* Constructeur de la partie.
* Initialise le plateau, l'IA, les flags de contrôle et la configuration initiale.
*
* @param mjfParam Référence à l'interface graphique pour pouvoir la mettre à jour. Doit être non-null.
*/
public Game(MyInterface mjfParam) {
mjf = mjfParam;
board = new Board(COL_NUM, LINE_NUM);
loopDelay = 250;
LINE_NUM = 8;
COL_NUM = 8;
activationAIFlags = new boolean[2];
aiPlayer = new AutoPlayer();
if (mjfParam == null) {
throw new IllegalArgumentException("MyInterface parameter cannot be null.");
}
this.mjf = mjfParam;
this.board = new Board(DEFAULT_WIDTH, DEFAULT_HEIGHT); // Crée un nouveau plateau 8x8
this.board.populateBoard(); // Remplit le plateau avec la configuration initiale
this.aiPlayer = new AutoPlayer(); // Initialise le joueur IA
this.activationAIFlags = new boolean[2]; // Tableau pour savoir si l'IA est active pour chaque couleur
this.activationAIFlags[0] = false; // IA désactivée pour Noir par défaut
this.activationAIFlags[1] = false; // IA désactivée pour Blanc par défaut
this.loopDelay = DEFAULT_LOOP_DELAY; // Définit le délai initial de la boucle du thread
System.out.println("Game initialized. Board populated. White's turn.");
// Afficher l'état initial
System.out.println(this.board.toString());
}
// --- Getters délégués au Board ---
/** @return La largeur du plateau. */
public int getWidth() {
return board.getWidth();
}
/** @return La hauteur du plateau. */
public int getHeight() {
return board.getHeight();
}
public void run() {
while(true) {
aiPlayerTurn();
mjf.update(board.getTurnNumber(), board.isTurnWhite());
try {
Thread.sleep(loopDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private boolean isAITurn() {
return activationAIFlags[board.isTurnWhite()?1:0];
}
private void aiPlayerTurn() {
if(isAITurn()) {
board.playMove(aiPlayer.computeBestMove(new Board(board)));
}
}
public void clickCoords(int x, int y) {
int width = this.getWidth();
int height = this.getHeight();
if(0>x || 0>y || x>width || y>height) {
System.out.println("Click out of bounds");
return;
}
if(!isAITurn()) {
if (board.getSelectedX() == -1 && board.getSelectedY() == -1) {
Piece piece = null;
for (Piece p : board.getPieces()) {
if (p.getX() == x && p.getY() == y) {
piece = p;
break;
}
}
if (piece != null) {
boolean isWhiteTurn = board.isTurnWhite();
if (piece.isWhite() != isWhiteTurn) {
System.out.println("Cannot select a" + (piece.isWhite() ? "white" : "black") + "piece on" + (isWhiteTurn ? "White" : "Black") + "'s turn");
return;
}
}
}
board.userTouch(x, y);
}
}
public void setPiece(boolean isWhite, PieceType type, int x, int y) {
board.setPiece(isWhite, type, x, y);
}
public String[] getFileRepresentation() {
return board.toFileRep();
}
public void setLoopDelay(int delay) {
this.loopDelay = delay;
}
public void setDefaultSetup() {
board.cleanBoard();
board.populateBoard();
}
public void setBoard(String[] array) {
board = new Board(array);
}
/**
* @return Une collection itérable des pièces actuellement sur le plateau.
* Renvoie une copie pour éviter les modifications externes non contrôlées.
*/
public Iterable<Piece> getPieces() {
return board.getPieces();
}
/**
* Vérifie si la case (x, y) correspond à la pièce actuellement sélectionnée.
* @param x Coordonnée x.
* @param y Coordonnée y.
* @return true si la pièce à (x,y) est sélectionnée, false sinon.
*/
public boolean isSelected(int x, int y) {
return board.isSelected(x, y);
}
/**
* Vérifie si la case (x, y) est mise en surbrillance comme destination possible.
* @param x Coordonnée x.
* @param y Coordonnée y.
* @return true si la case (x,y) est en surbrillance, false sinon.
*/
public boolean isHighlighted(int x, int y) {
return board.isHighlighted(x, y);
}
public void undoLastMove() {
board.undoLastMove();
/** @return true si c'est le tour des blancs, false si c'est le tour des noirs. */
public boolean isWhiteTurn() {
return board.isTurnWhite();
}
/** @return Le numéro du tour actuel (commence à 0 ou 1 selon l'implémentation de Board). */
public int getTurnNumber() {
return board.getTurnNumber();
}
// --- Logique principale du Thread ---
/**
* Boucle principale du jeu exécutée dans le thread.
* Vérifie périodiquement si c'est au tour de l'IA de jouer,
* met à jour l'interface graphique et attend.
*/
@Override
public void run() {
System.out.println("Game thread started.");
while (!Thread.currentThread().isInterrupted()) { // Continue tant que le thread n'est pas interrompu
try {
// 1. Gérer le tour de l'IA si applicable
aiPlayerTurn();
// 2. Mettre à jour l'interface graphique
// (Numéro de tour, à qui le tour, et potentiellement repaint)
// On le fait à chaque itération pour refléter l'état même si aucun coup n'est joué.
mjf.update(board.getTurnNumber(), board.isTurnWhite());
// Note: mjf.update() devrait idéalement déclencher un repaint dans l'UI.
// 3. Pause avant la prochaine vérification
Thread.sleep(loopDelay);
} catch (InterruptedException e) {
System.err.println("Game thread interrupted during sleep or operation.");
Thread.currentThread().interrupt(); // Important: Rétablir le statut d'interruption
// Sortir de la boucle car le thread doit s'arrêter
} catch (Exception e) {
// Capturer d'autres exceptions pour la robustesse
System.err.println("Unexpected error in game loop: " + e.getMessage());
e.printStackTrace();
// Optionnel : Mettre une pause plus longue pour éviter de spammer les logs en cas d'erreur répétée
try { Thread.sleep(5000); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); }
}
}
System.out.println("Game thread finished.");
}
/**
* Vérifie si le joueur dont c'est le tour est actuellement contrôlé par l'IA.
*
* @return true si c'est le tour de l'IA, false si c'est un tour humain.
*/
private boolean isAITurn() {
int playerIndex = board.isTurnWhite() ? 1 : 0; // 1 pour Blanc, 0 pour Noir
return activationAIFlags[playerIndex];
}
/**
* Si c'est le tour de l'IA, lui demande de calculer un coup et le joue sur le plateau.
*/
private void aiPlayerTurn() {
if (isAITurn()) {
String colorTurn = board.isTurnWhite() ? "White" : "Black";
System.out.println("AI's turn (" + colorTurn + "). Computing move...");
// Créer une copie *indépendante* du plateau pour l'analyse par l'IA.
// Ceci est crucial pour que l'IA puisse explorer des coups sans modifier l'état réel du jeu.
Board boardCopy = new Board(board); // Utilise le constructeur de copie de Board
// Demander à l'IA de calculer le meilleur coup sur la copie
Move bestMove = aiPlayer.computeBestMove(boardCopy);
// Si l'IA retourne un coup (elle n'est pas échec et mat ou pat)
if (bestMove != null) {
System.out.println("AI computed move: " + bestMove.getPieceMoved().getType() +
" from (" + bestMove.getFromX() + "," + bestMove.getFromY() +
") to (" + bestMove.getToX() + "," + bestMove.getToY() + ")");
// Jouer le coup sur le plateau *principal*
board.playMove(bestMove);
// playMove met à jour l'état interne du board (pièces, tour, etc.)
// L'UI sera mise à jour à la prochaine itération de run()
} else {
// L'IA n'a trouvé aucun coup (peut indiquer échec et mat, pat, ou une erreur)
System.err.println("AI returned null move! Possible stalemate, checkmate, or error in AI for " + colorTurn + ".");
// Optionnel: Désactiver l'IA ou afficher un message de fin de partie.
// Pour l'instant, le jeu continue (ou se bloque si c'est vraiment mat/pat).
}
}
}
// --- Gestion des interactions utilisateur ---
/**
* Gère un clic de l'utilisateur sur la case (x, y) de l'échiquier.
* Vérifie si le clic est dans les limites et si ce n'est pas au tour de l'IA.
* Si les conditions sont remplies, délègue toute la logique d'interaction
* (sélection, désélection, déplacement) à la méthode `userTouch` du `Board`.
*
* @param x Coordonnée x (colonne) de la case cliquée.
* @param y Coordonnée y (ligne) de la case cliquée.
*/
public void clickCoords(int x, int y) {
// System.out.println("Game received click at (" + x + "," + y + ")"); // Debug
// 1. Ignorer si clic hors limites
if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) {
System.out.println("Click ignored: out of bounds.");
return;
}
// 2. Ignorer si c'est le tour de l'IA (le joueur humain ne peut pas interagir)
if (isAITurn()) {
System.out.println("Click ignored: AI's turn.");
return;
}
// 3. Si c'est un tour humain et clic dans les limites, déléguer au Board
// Board.userTouch gère la sélection, la mise en surbrillance, la désélection et le jeu du coup.
board.userTouch(x, y);
// L'état du jeu (sélection, highlights, pièces) peut avoir changé.
// La boucle `run` mettra à jour l'interface graphique.
// Si une mise à jour VISUELLE immédiate est cruciale après chaque clic humain :
// mjf.update(board.getTurnNumber(), board.isTurnWhite()); // Forcer update + repaint
}
// --- Méthodes de Contrôle et Configuration ---
/**
* Active ou désactive l'IA pour la couleur spécifiée.
*
* @param isWhite true pour contrôler l'IA blanche, false pour l'IA noire.
*/
public void toggleAI(boolean isWhite) {
this.activationAIFlags[isWhite?1:0] = !this.activationAIFlags[isWhite?1:0];
int playerIndex = isWhite ? 1 : 0;
activationAIFlags[playerIndex] = !activationAIFlags[playerIndex]; // Bascule l'état
String color = isWhite ? "White" : "Black";
String status = activationAIFlags[playerIndex] ? "enabled" : "disabled";
System.out.println("AI for " + color + " player is now " + status + ".");
// Si on active l'IA et que c'est son tour, elle jouera à la prochaine itération de run()
}
/**
* Modifie le délai (en millisecondes) entre les itérations de la boucle du thread.
* Un délai plus court rend l'IA plus réactive (si elle calcule vite),
* un délai plus long économise des ressources CPU.
*
* @param delay Le nouveau délai en millisecondes (doit être positif).
*/
public void setLoopDelay(int delay) {
if (delay > 0) {
this.loopDelay = delay;
System.out.println("Game loop delay set to " + delay + " ms.");
} else {
System.err.println("Invalid loop delay requested: " + delay + ". Must be positive.");
}
}
/**
* Réinitialise le plateau à la configuration de départ standard des échecs.
* Efface l'état actuel et remet les pièces à leur place initiale.
*/
public void setDefaultSetup() {
System.out.println("Setting default board setup...");
board.cleanBoard(); // Nettoie le plateau (pièces, sélection, highlights, tour)
board.populateBoard(); // Met en place les pièces
System.out.println("Board reset to initial state.");
System.out.println(this.board.toString());
// Mettre à jour l'interface immédiatement pour refléter le changement
mjf.update(board.getTurnNumber(), board.isTurnWhite());
}
// --- Méthodes déléguées au Board (pour manipulation externe, sauvegarde/chargement, etc.) ---
/**
* Place directement une pièce sur le plateau.
* Attention : Utiliser principalement pour le débogage ou la mise en place de positions spécifiques.
* Écrase toute pièce existante à cet endroit.
*
* @param isWhite Couleur de la pièce (true=blanc).
* @param type Type de la pièce (Pawn, Rook...).
* @param x Coordonnée x.
* @param y Coordonnée y.
*/
public void setPiece(boolean isWhite, PieceType type, int x, int y) {
board.setPiece(isWhite, type, x, y);
// Mettre à jour l'interface pour voir le changement
mjf.update(board.getTurnNumber(), board.isTurnWhite());
}
/**
* Récupère une représentation du plateau sous forme de tableau de chaînes de caractères.
* Utile pour la sauvegarde ou le débogage.
* (Nécessite que la méthode `toFileRep` soit implémentée dans la classe Board).
*
* @return Un tableau de String représentant l'état du plateau, ou null si non supporté.
*/
public String[] getFileRepresentation() {
// TODO: Implémenter Board.toFileRep() pour que cela fonctionne.
System.out.println("Getting file representation from board...");
return board.toFileRep();
}
/**
* Configure l'état du plateau à partir d'une représentation sous forme de tableau de chaînes.
* Remplace complètement l'état actuel du plateau.
* (Nécessite que le constructeur `Board(String[] array)` soit implémenté).
*
* @param boardData Le tableau de String décrivant le nouvel état du plateau.
*/
public void setBoard(String[] boardData) {
System.out.println("Setting board state from string array...");
// TODO: Implémenter le constructeur Board(String[] array) pour que cela fonctionne.
// Ceci remplacera l'instance actuelle du board ou modifiera son état interne.
// Si on remplace l'instance, il faut s'assurer que la référence 'board' est mise à jour.
// Exemple (si le constructeur existe) :
// this.board = new Board(boardData);
try {
// Tentative de créer un nouveau board (si le constructeur existe)
// Note: Ceci ne fonctionnera que si 'board' n'est PAS final.
// Si 'board' est final, il faudrait une méthode dans Board pour charger l'état.
// Exemple: this.board.loadFromArray(boardData);
System.err.println("setBoard(String[]) is not fully implemented yet (requires Board constructor or load method).");
// Pour l'instant, on ne fait rien pour éviter une erreur si Board(String[]) n'existe pas
// this.board = new Board(boardData);
} catch (Exception e) {
System.err.println("Failed to set board from string array: " + e.getMessage());
e.printStackTrace();
}
// Dans tous les cas, mettre à jour l'interface
mjf.update(this.board.getTurnNumber(), this.board.isTurnWhite());
}
/**
* Tente d'annuler le dernier coup joué.
* La possibilité d'annuler dépend de si le `Board` gère un historique des coups.
* (Nécessite que la méthode `undoLastMove` soit implémentée dans la classe Board).
*/
public void undoLastMove() {
// Optionnel: Ajouter une logique pour empêcher l'annulation pendant le tour de l'IA
// if (isAITurn()) {
// System.out.println("Undo ignored: AI is thinking or has just played.");
// return;
// }
System.out.println("Requesting undo last move...");
// TODO: Implémenter Board.undoLastMove() pour que cela fonctionne.
boolean success = board.undoLastMove(); // Supposons que undoLastMove renvoie un booléen
if (success) {
System.out.println("Last move undone successfully.");
System.out.println(this.board.toString()); // Afficher le nouvel état
} else {
System.out.println("Undo failed (no history or cannot undo).");
}
// Mettre à jour l'interface quelle que soit l'issue
mjf.update(board.getTurnNumber(), board.isTurnWhite());
}
}

View File

@ -1,225 +1,71 @@
package backend;
import java.util.HashSet;
import java.util.Set;
public class Move {
private int fromX;
private int fromY;
private int toX;
private int toY;
private Piece pieceMoved;
private Piece pieceCaptured;
private static String[] highlightedPositions = new String[0]; // Store highlighted positions
private final int fromX;
private final int fromY;
private final int toX;
private final int toY;
private final Piece pieceMoved;
private final Piece pieceCaptured;
public Move(int fromX, int fromY, int toX, int toY, Piece pieceMoved, Piece pieceCaptured) {
this.fromX = fromX;
this.fromY = fromY;
this.toX = toX;
this.toY = toY;
if (pieceMoved == null) {
throw new IllegalArgumentException("pieceMoved cannot be null in a Move.");
}
this.pieceMoved = pieceMoved;
this.pieceCaptured = pieceCaptured;
}
public int getFromX() {
return fromX;
}
public int getFromX() { return fromX; }
public int getFromY() { return fromY; }
public int getToX() { return toX; }
public int getToY() { return toY; }
public Piece getPieceMoved() { return pieceMoved; }
public Piece getPieceCaptured() { return pieceCaptured; }
public int getFromY() {
return fromY;
}
public static Set<String> calculateValidMoves(Board board, Piece piece) {
Set<String> validMoves = new HashSet<>();
public int getToX() {
return toX;
if (piece == null || board == null) {
return validMoves;
}
public int getToY() {
return toY;
}
public Piece getPieceMoved() {
return pieceMoved;
}
public Piece getPieceCaptured() {
return pieceCaptured;
}
// Static method to calculate and set highlighted positions for a piece
public static void setHighlightedPositions(Board board, Piece piece) {
if (piece == null) {
highlightedPositions = new String[0];
} else {
highlightedPositions = calculateValidMoves(board, piece);
}
}
// Static method to clear highlighted positions
public static void clearHighlightedPositions() {
highlightedPositions = new String[0];
}
// Static method to check if a position is highlighted
public static boolean isHighlighted(int x, int y) {
String position = x + "," + y;
for (int i = 0; i < highlightedPositions.length; i++) {
if (highlightedPositions[i] != null && highlightedPositions[i].equals(position)) {
return true;
}
}
return false;
}
// Static method to calculate all valid moves for a piece
public static String[] calculateValidMoves(Board board, Piece piece) {
String[] validMoves = new String[32]; // Allocate space for up to 32 moves
int[] moveCount = new int[1]; // Use an array to simulate a mutable counter
moveCount[0] = 0;
int x = piece.getX();
int y = piece.getY();
switch (piece.getType()) {
case Pawn:
calculatePawnMoves(board, piece, x, y, validMoves, moveCount);
calculatePawnMoves(board, piece, x, y, validMoves);
break;
case Rook:
calculateRookMoves(board, piece, x, y, validMoves, moveCount);
calculateSlidingMoves(board, piece, x, y, validMoves, new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}});
break;
case Knight:
calculateKnightMoves(board, piece, x, y, validMoves, moveCount);
calculateKnightMoves(board, piece, x, y, validMoves);
break;
case Bishop:
calculateBishopMoves(board, piece, x, y, validMoves, moveCount);
calculateSlidingMoves(board, piece, x, y, validMoves, new int[][]{{1, 1}, {1, -1}, {-1, 1}, {-1, -1}});
break;
case Queen:
calculateQueenMoves(board, piece, x, y, validMoves, moveCount);
calculateSlidingMoves(board, piece, x, y, validMoves, new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}});
break;
case King:
calculateKingMoves(board, piece, x, y, validMoves, moveCount);
calculateKingMoves(board, piece, x, y, validMoves);
break;
}
// Trim the array to the actual number of moves
String[] result = new String[moveCount[0]];
for (int i = 0; i < moveCount[0]; i++) {
result[i] = validMoves[i];
}
return result;
return validMoves;
}
private static void calculatePawnMoves(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) {
int direction = piece.isWhite() ? -1 : 1; // White pawns move up, black pawns move down
int startRow = piece.isWhite() ? 6 : 1; // Starting row for double move
// Move forward one square
if (isValidPosition(board, x, y + direction) && getPieceAt(board, x, y + direction) == null) {
addMoveIfNotDuplicate(x, y + direction, validMoves, moveCount);
// Move forward two squares from starting position
if (y == startRow && getPieceAt(board, x, y + 2 * direction) == null) {
addMoveIfNotDuplicate(x, y + 2 * direction, validMoves, moveCount);
}
}
// Capture diagonally
if (isValidPosition(board, x - 1, y + direction)) {
Piece target = getPieceAt(board, x - 1, y + direction);
if (target != null && target.isWhite() != piece.isWhite()) {
addMoveIfNotDuplicate(x - 1, y + direction, validMoves, moveCount);
}
}
if (isValidPosition(board, x + 1, y + direction)) {
Piece target = getPieceAt(board, x + 1, y + direction);
if (target != null && target.isWhite() != piece.isWhite()) {
addMoveIfNotDuplicate(x + 1, y + direction, validMoves, moveCount);
}
}
}
private static void calculateRookMoves(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) {
// Horizontal: left
for (int i = x - 1; i >= 0; i--) {
if (!addMoveIfValid(board, piece, i, y, validMoves, moveCount)) break;
}
// Horizontal: right
for (int i = x + 1; i < board.getWidth(); i++) {
if (!addMoveIfValid(board, piece, i, y, validMoves, moveCount)) break;
}
// Vertical: up
for (int j = y - 1; j >= 0; j--) {
if (!addMoveIfValid(board, piece, x, j, validMoves, moveCount)) break;
}
// Vertical: down
for (int j = y + 1; j < board.getHeight(); j++) {
if (!addMoveIfValid(board, piece, x, j, validMoves, moveCount)) break;
}
}
private static void calculateKnightMoves(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) {
int[][] knightMoves = {
{-2, -1}, {-2, 1}, {-1, -2}, {-1, 2},
{1, -2}, {1, 2}, {2, -1}, {2, 1}
};
for (int i = 0; i < knightMoves.length; i++) {
int newX = x + knightMoves[i][0];
int newY = y + knightMoves[i][1];
addMoveIfValid(board, piece, newX, newY, validMoves, moveCount);
}
}
private static void calculateBishopMoves(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) {
// Diagonal: up-left
for (int i = 1; x - i >= 0 && y - i >= 0; i++) {
if (!addMoveIfValid(board, piece, x - i, y - i, validMoves, moveCount)) break;
}
// Diagonal: up-right
for (int i = 1; x + i < board.getWidth() && y - i >= 0; i++) {
if (!addMoveIfValid(board, piece, x + i, y - i, validMoves, moveCount)) break;
}
// Diagonal: down-left
for (int i = 1; x - i >= 0 && y + i < board.getHeight(); i++) {
if (!addMoveIfValid(board, piece, x - i, y + i, validMoves, moveCount)) break;
}
// Diagonal: down-right
for (int i = 1; x + i < board.getWidth() && y + i < board.getHeight(); i++) {
if (!addMoveIfValid(board, piece, x + i, y + i, validMoves, moveCount)) break;
}
}
private static void calculateQueenMoves(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) {
// Queen moves like a rook + bishop
calculateRookMoves(board, piece, x, y, validMoves, moveCount);
calculateBishopMoves(board, piece, x, y, validMoves, moveCount);
}
private static void calculateKingMoves(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) {
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
if (dx == 0 && dy == 0) continue;
int newX = x + dx;
int newY = y + dy;
addMoveIfValid(board, piece, newX, newY, validMoves, moveCount);
}
}
}
private static boolean addMoveIfValid(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) {
if (!isValidPosition(board, x, y)) return false;
Piece target = getPieceAt(board, x, y);
if (target == null || target.isWhite() != piece.isWhite()) {
addMoveIfNotDuplicate(x, y, validMoves, moveCount);
return target == null; // Continue if the square is empty, stop if we hit a piece
}
return false; // Stop if we hit a piece of the same color
}
private static void addMoveIfNotDuplicate(int x, int y, String[] validMoves, int[] moveCount) {
if (moveCount[0] >= validMoves.length) return; // Prevent array overflow
String position = x + "," + y;
// Check for duplicates
for (int i = 0; i < moveCount[0]; i++) {
if (validMoves[i] != null && validMoves[i].equals(position)) {
return; // Position already exists
}
}
validMoves[moveCount[0]] = position;
moveCount[0]++;
private static void addMove(int x, int y, Set<String> moves) {
moves.add(x + "," + y);
}
private static boolean isValidPosition(Board board, int x, int y) {
@ -227,12 +73,127 @@ public class Move {
}
private static Piece getPieceAt(Board board, int x, int y) {
Piece[] pieces = board.getPieces().toArray(new Piece[0]);
for (int i = 0; i < pieces.length; i++) {
if (pieces[i].getX() == x && pieces[i].getY() == y) {
return pieces[i];
return board.getPieceAt(x, y);
}
private static void calculateSlidingMoves(Board board, Piece piece, int x, int y, Set<String> validMoves, int[][] directions) {
for (int[] dir : directions) {
int dx = dir[0];
int dy = dir[1];
for (int i = 1; ; i++) {
int nextX = x + i * dx;
int nextY = y + i * dy;
if (!isValidPosition(board, nextX, nextY)) {
break;
}
Piece targetPiece = getPieceAt(board, nextX, nextY);
if (targetPiece == null) {
addMove(nextX, nextY, validMoves);
} else {
if (targetPiece.isWhite() != piece.isWhite()) {
addMove(nextX, nextY, validMoves);
}
break;
}
}
return null;
}
}
private static void calculatePawnMoves(Board board, Piece piece, int x, int y, Set<String> validMoves) {
int forwardDirection = piece.isWhite() ? -1 : 1;
int startRow = piece.isWhite() ? 6 : 1;
// Avance d'une case
int oneStepY = y + forwardDirection;
if (isValidPosition(board, x, oneStepY) && getPieceAt(board, x, oneStepY) == null) {
addMove(x, oneStepY, validMoves);
// Avance de deux cases
int twoStepsY = y + 2 * forwardDirection;
if (y == startRow && isValidPosition(board, x, twoStepsY) && getPieceAt(board, x, twoStepsY) == null) {
addMove(x, twoStepsY, validMoves);
}
}
// Captures diagonales normales
int[] captureXs = {x - 1, x + 1};
for (int captureX : captureXs) {
if (isValidPosition(board, captureX, oneStepY)) {
Piece target = getPieceAt(board, captureX, oneStepY);
if (target != null && target.isWhite() != piece.isWhite()) {
addMove(captureX, oneStepY, validMoves);
}
}
}
// *** Début Modification En Passant ***
// Vérifier la capture en passant
int enPassantTargetX = board.getEnPassantTargetX();
int enPassantTargetY = board.getEnPassantTargetY();
if (enPassantTargetX != -1 && enPassantTargetY != -1) { // Si une cible EP existe
// Vérifier si la cible est sur la case diagonale avant
if (enPassantTargetY == oneStepY) { // La cible doit être sur la rangée devant le pion
if (Math.abs(x - enPassantTargetX) == 1) { // La cible doit être sur une colonne adjacente
// Vérifier qu'il y a bien un pion adverse à capturer à côté de nous (sur la case EP virtuelle)
// Le pion à capturer est sur (enPassantTargetX, y)
Piece adjacentPawn = getPieceAt(board, enPassantTargetX, y);
if (adjacentPawn != null && adjacentPawn.getType() == PieceType.Pawn && adjacentPawn.isWhite() != piece.isWhite()) {
addMove(enPassantTargetX, enPassantTargetY, validMoves);
System.out.println("En passant move calculated for pawn at ("+x+","+y+") to ("+enPassantTargetX+","+enPassantTargetY+")"); // Debug
}
}
}
}
// *** Fin Modification En Passant ***
}
private static void calculateKnightMoves(Board board, Piece piece, int x, int y, Set<String> validMoves) {
int[][] knightMoves = {
{-2, -1}, {-2, 1}, {-1, -2}, {-1, 2},
{1, -2}, {1, 2}, {2, -1}, {2, 1}
};
for (int[] move : knightMoves) {
int nextX = x + move[0];
int nextY = y + move[1];
if (isValidPosition(board, nextX, nextY)) {
Piece target = getPieceAt(board, nextX, nextY);
if (target == null || target.isWhite() != piece.isWhite()) {
addMove(nextX, nextY, validMoves);
}
}
}
}
private static void calculateKingMoves(Board board, Piece piece, int x, int y, Set<String> validMoves) {
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
if (dx == 0 && dy == 0) continue;
int nextX = x + dx;
int nextY = y + dy;
if (isValidPosition(board, nextX, nextY)) {
Piece target = getPieceAt(board, nextX, nextY);
if (target == null || target.isWhite() != piece.isWhite()) {
addMove(nextX, nextY, validMoves);
}
}
}
}
// TODO: Ajouter calcul du roque ici
}
@Override
public String toString() {
String captureStr = (pieceCaptured != null) ? "x" + pieceCaptured.getType().getSummary() : "";
return pieceMoved.getType().getSummary() +
"(" + fromX + "," + fromY + ")" +
"-" + captureStr +
"(" + toX + "," + toY + ")";
}
}

View File

@ -1,163 +1,121 @@
package backend;
import java.util.ArrayList;
import java.util.Objects;
public class Piece {
private int x;
private int y;
private PieceType type;
private boolean isWhite;
private final PieceType type;
private final boolean isWhite;
public Piece(int x, int y, PieceType type, boolean isWhite) {
if (type == null) {
throw new IllegalArgumentException("PieceType cannot be null.");
}
this.x = x;
this.y = y;
this.type = type;
this.isWhite = isWhite;
// this.hasMoved = false; // Initialiser si ajoute
}
public int getX() {
return this.x;
}
public int getX() { return this.x; }
public int getY() { return this.y; }
public PieceType getType() { return this.type; }
public boolean isWhite() { return this.isWhite; }
public int getY() {
return this.y;
}
public PieceType getType() {
return this.type;
}
public boolean isWhite() {
return this.isWhite;
}
// Methode pour mettre a jour la position (utilisee dans Board.playMove)
public void setPosition(int newX, int newY) {
void setPosition(int newX, int newY) {
this.x = newX;
this.y = newY;
// this.hasMoved = true; // Mettre à jour si la variable hasMoved existe
}
public boolean canMoveTo(int toX, int toY, Board board) {
if (board == null) return false;
if (this.x == toX && this.y == toY) return false;
if (!Move.isValidPosition(board, toX, toY)) return false;
if (x == toX && y == toY) {
return false;
}
if (toX < 0 || toX >= board.getWidth() || toY < 0 || toY >= board.getHeight()) {
return false;
}
Piece pieceAtDestination = getPieceAt(board, toX, toY);
Piece pieceAtDestination = board.getPieceAt(toX, toY);
if (pieceAtDestination != null && pieceAtDestination.isWhite() == this.isWhite) {
return false;
}
// Calculs specifiques par type de piece
int dx = Math.abs(toX - x);
int dy = Math.abs(toY - y); // Note: dy calcule ici, mais canPawnMoveTo recalcule avec signe
int dx = Math.abs(toX - this.x);
int dy = Math.abs(toY - this.y);
switch (type) {
switch (this.type) {
case Pawn:
return canPawnMoveTo(toX, toY, board, pieceAtDestination);
case Rook:
return (dx == 0 || dy == 0) && isPathClear(x, y, toX, toY, board);
return (dx == 0 || dy == 0) && isPathClear(this.x, this.y, toX, toY, board);
case Knight:
return (dx == 2 && dy == 1) || (dx == 1 && dy == 2);
case Bishop:
return (dx == dy) && isPathClear(x, y, toX, toY, board);
return (dx == dy) && isPathClear(this.x, this.y, toX, toY, board);
case Queen:
return (dx == 0 || dy == 0 || dx == dy) && isPathClear(x, y, toX, toY, board);
return (dx == 0 || dy == 0 || dx == dy) && isPathClear(this.x, this.y, toX, toY, board);
case King:
return dx <= 1 && dy <= 1;
default:
System.err.println("Erreur: Type de pièce inconnu dans canMoveTo: " + this.type);
return false;
}
}
private boolean canPawnMoveTo(int toX, int toY, Board board, Piece pieceAtDestination) {
int deltaX = toX - x;
int deltaY = toY - y;
int forwardDirection = isWhite ? -1 : 1;
int startingRow = isWhite ? 6 : 1;
int deltaX = toX - this.x;
int deltaY = toY - this.y;
int forwardDirection = this.isWhite ? -1 : 1;
int startingRow = this.isWhite ? 6 : 1;
if (deltaX == 0 && deltaY == forwardDirection && pieceAtDestination == null) {
return true;
}
if (deltaX == 0 && deltaY == 2 * forwardDirection && y == startingRow && pieceAtDestination == null) {
int intermediateY = y + forwardDirection;
if (getPieceAt(board, x, intermediateY) == null) {
if (this.y == startingRow && deltaX == 0 && deltaY == 2 * forwardDirection && pieceAtDestination == null) {
if (board.getPieceAt(this.x, this.y + forwardDirection) == null) {
return true;
}
}
if (Math.abs(deltaX) == 1 && deltaY == forwardDirection && pieceAtDestination != null ) {
return true;
}
return false;
}
private Piece getPieceAt(Board board, int targetX, int targetY) {
for (Piece p : board.getPieces()) {
if (p.getX() == targetX && p.getY() == targetY) {
return p;
}
}
return null;
}
private boolean isPathClear(int fromX, int fromY, int toX, int toY, Board board) {
int dx = toX - fromX;
int dy = toY - fromY;
int steps = Math.max(Math.abs(dx), Math.abs(dy));
if (steps <= 1) {
return true;
}
if (steps <= 1) return true;
int stepX = Integer.signum(dx);
int stepY = Integer.signum(dy);
for (int i = 1; i < steps; i++) {
int checkX = fromX + i * stepX;
int checkY = fromY + i * stepY;
if (getPieceAt(board, checkX, checkY) != null) {
if (board.getPieceAt(checkX, checkY) != null) {
return false;
}
}
return true;
}
@Override
public String toString() {
return type.name() + "(" + (isWhite ? "W" : "B") + ")@[" + x + "," + y + "]";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Piece piece = (Piece) o;
return x == piece.x && y == piece.y && isWhite == piece.isWhite && type == piece.type;
}
@Override
public int hashCode() {
return Objects.hash(x, y, type, isWhite);
}
}