596 lines
17 KiB
Java
596 lines
17 KiB
Java
package backend;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
public class Board {
|
|
|
|
private int width;
|
|
private int height;
|
|
private ArrayList<Piece> pieces;
|
|
private int turnNumber;
|
|
private boolean turnWhite;
|
|
private Integer selectedX = null;
|
|
private Integer selectedY = null;
|
|
private ArrayList<int[]> highlightedSquares = new ArrayList<>();
|
|
private ArrayList<Move> moveHistory = new ArrayList<>();
|
|
private int[] enPassantTarget = null;
|
|
|
|
|
|
/*public Board(int colNum, int lineNum) {
|
|
this.width = colNum;
|
|
this.height = lineNum;
|
|
this.pieces = new ArrayList<>();
|
|
}
|
|
*/
|
|
public Board(int colNum, int lineNum) {
|
|
this.width = colNum;
|
|
this.height = lineNum;
|
|
this.pieces = new ArrayList<>();
|
|
this.turnNumber = 0;
|
|
this.turnWhite = true; // White starts first in chess
|
|
}
|
|
public int getWidth() {
|
|
|
|
return width;
|
|
}
|
|
|
|
public int getHeight() {
|
|
|
|
return height;// the above part set the checked board
|
|
}
|
|
|
|
public int getTurnNumber() {
|
|
|
|
return this.turnNumber;
|
|
}
|
|
|
|
public boolean isTurnWhite() {
|
|
|
|
return this.turnWhite;
|
|
}
|
|
|
|
public void setPiece(boolean isWhite, PieceType type, int x, int y) {
|
|
pieces.add(new Piece(isWhite, type, x, y));
|
|
}
|
|
|
|
public void populateBoard() {
|
|
cleanBoard(); // make sure it's empty first
|
|
|
|
// Place white pawns
|
|
for (int x = 0; x < 8; x++) {
|
|
setPiece(false, PieceType.Pawn, x, 1);
|
|
}
|
|
|
|
// Place black pawns
|
|
for (int x = 0; x < 8; x++) {
|
|
setPiece(true, PieceType.Pawn, x, 6);
|
|
}
|
|
|
|
// Back rows (R, N, B, K, Q, B, N, R)
|
|
PieceType[] backRow = {
|
|
PieceType.Rook, PieceType.Knight, PieceType.Bishop, PieceType.Queen,
|
|
PieceType.King, PieceType.Bishop, PieceType.Knight, PieceType.Rook
|
|
};
|
|
|
|
// White back row
|
|
for (int x = 0; x < 8; x++) {
|
|
setPiece(false, backRow[x], x, 0);
|
|
}
|
|
|
|
// Black back row
|
|
for (int x = 0; x < 8; x++) {
|
|
setPiece(true, backRow[x], x, 7);
|
|
}
|
|
}
|
|
|
|
|
|
public void cleanBoard() {
|
|
//TODO
|
|
pieces.clear();
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
String[][] grid = new String[height][width];
|
|
|
|
// Fill the grid with empty squares
|
|
for (int y = 0; y < height; y++) {
|
|
for (int x = 0; x < width; x++) {
|
|
grid[y][x] = "--";
|
|
}
|
|
}
|
|
|
|
// Place pieces on the grid
|
|
for (Piece piece : pieces) {
|
|
String color = piece.isWhite() ? "W" : "B";
|
|
String type = piece.getType().toString().substring(0, 1); // e.g., "P" for Pawn
|
|
grid[piece.getY()][piece.getX()] = color + type;
|
|
}
|
|
|
|
// Build the string row by row
|
|
StringBuilder sb = new StringBuilder();
|
|
for (int y = 0; y < height; y++) {
|
|
for (int x = 0; x < width; x++) {
|
|
sb.append(grid[y][x]).append(" ");
|
|
}
|
|
sb.append("\n");
|
|
}
|
|
|
|
return sb.toString();
|
|
}
|
|
|
|
|
|
public ArrayList<Piece> getPieces() {
|
|
return pieces; // this refers to the instance variable
|
|
}
|
|
|
|
public void userTouch(int x, int y) {
|
|
Piece clickedPiece = getPieceAt(x, y);
|
|
|
|
// No selection yet
|
|
if (selectedX == null || selectedY == null) {
|
|
if (clickedPiece != null && clickedPiece.isWhite() == turnWhite) {
|
|
selectedX = x;
|
|
selectedY = y;
|
|
highlightedSquares = computeLegalMoves(clickedPiece);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Clicked the same square again → unselect
|
|
if (selectedX == x && selectedY == y) {
|
|
selectedX = null;
|
|
selectedY = null;
|
|
highlightedSquares.clear();
|
|
return;
|
|
}
|
|
|
|
// Otherwise, try to move
|
|
Piece selectedPiece = getPieceAt(selectedX, selectedY);
|
|
if (selectedPiece == null) {
|
|
// Somehow no piece at selected position (safety check)
|
|
selectedX = null;
|
|
selectedY = null;
|
|
highlightedSquares.clear();
|
|
return;
|
|
}
|
|
|
|
// Check if clicked destination is valid
|
|
boolean validMove = false;
|
|
for (int[] pos : highlightedSquares) {
|
|
if (pos[0] == x && pos[1] == y) {
|
|
validMove = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!validMove) {
|
|
// Invalid destination → reset
|
|
selectedX = null;
|
|
selectedY = null;
|
|
highlightedSquares.clear();
|
|
return;
|
|
}
|
|
|
|
// Move is valid → capture if needed
|
|
Piece target = getPieceAt(x, y);
|
|
|
|
// Log the move for undo
|
|
Move move = new Move(
|
|
selectedPiece.getType(),
|
|
selectedPiece.isWhite(),
|
|
selectedX, selectedY,
|
|
x, y,
|
|
target // may be null
|
|
);
|
|
moveHistory.add(move);
|
|
|
|
// Remove captured piece if any
|
|
if (target != null) {
|
|
pieces.remove(target);
|
|
}
|
|
|
|
// Remove the original piece
|
|
pieces.remove(selectedPiece);
|
|
|
|
// Add new piece at destination
|
|
setPiece(selectedPiece.isWhite(), selectedPiece.getType(), x, y);
|
|
|
|
// Update turn
|
|
turnNumber++;
|
|
turnWhite = !turnWhite;
|
|
|
|
// Clear selection and highlights
|
|
selectedX = null;
|
|
selectedY = null;
|
|
highlightedSquares.clear();
|
|
|
|
// Check for en passant capture BEFORE updating enPassantTarget!
|
|
if (selectedPiece.getType() == PieceType.Pawn &&
|
|
enPassantTarget != null &&
|
|
x == enPassantTarget[0] &&
|
|
y == enPassantTarget[1]) {
|
|
|
|
int capturedY = selectedPiece.isWhite() ? y - 1 : y + 1;
|
|
Piece capturedPawn = getPieceAt(x, capturedY);
|
|
|
|
// Validate that it's an enemy pawn that just moved two steps
|
|
if (capturedPawn != null &&
|
|
capturedPawn.getType() == PieceType.Pawn &&
|
|
capturedPawn.isWhite() != selectedPiece.isWhite()) {
|
|
|
|
// Remove the pawn captured en passant
|
|
pieces.remove(capturedPawn);
|
|
}
|
|
}
|
|
|
|
|
|
// Update enPassantTarget
|
|
if (selectedPiece.getType() == PieceType.Pawn && selectedY != null && Math.abs(y - selectedY) == 2) {
|
|
int middleY = (y + selectedY) / 2;
|
|
enPassantTarget = new int[]{x, middleY};
|
|
} else {
|
|
enPassantTarget = null;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
public boolean isSelected(int x, int y) {
|
|
return selectedX != null && selectedY != null && selectedX == x && selectedY == y;
|
|
}
|
|
|
|
|
|
/* saving-loading feature :*/
|
|
|
|
public String[] toFileRep() {
|
|
String[] fileRep = new String[height + 1]; // 8 rows + 1 for turn
|
|
String[][] grid = new String[height][width];
|
|
|
|
// Fill with empty squares
|
|
for (int y = 0; y < height; y++) {
|
|
for (int x = 0; x < width; x++) {
|
|
grid[y][x] = "--";
|
|
}
|
|
}
|
|
|
|
// Fill with pieces
|
|
for (Piece piece : pieces) {
|
|
String color = piece.isWhite() ? "W" : "B";
|
|
String type = piece.getType().toString().substring(0, 1); // P, R, N, B, Q, K
|
|
grid[piece.getY()][piece.getX()] = color + type;
|
|
}
|
|
|
|
// Convert grid to CSV-like strings
|
|
for (int y = 0; y < height; y++) {
|
|
StringBuilder row = new StringBuilder();
|
|
for (int x = 0; x < width; x++) {
|
|
row.append(grid[y][x]);
|
|
if (x < width - 1) {
|
|
row.append(",");
|
|
}
|
|
}
|
|
fileRep[y] = row.toString();
|
|
}
|
|
|
|
// Last line = turn
|
|
fileRep[height] = turnWhite ? "W" : "B";
|
|
|
|
return fileRep;
|
|
}
|
|
|
|
|
|
public Board(String[] array) {
|
|
this.width = 8;
|
|
this.height = 8;
|
|
this.pieces = new ArrayList<>();
|
|
this.turnNumber = 0;
|
|
this.turnWhite = array[8].equals("W");
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
String[] row = array[y].split(",");
|
|
for (int x = 0; x < width; x++) {
|
|
String code = row[x];
|
|
if (!code.equals("--")) {
|
|
boolean isWhite = code.charAt(0) == 'W';
|
|
char typeChar = code.charAt(1);
|
|
PieceType type = null;
|
|
|
|
switch (typeChar) {
|
|
case 'P':
|
|
type = PieceType.Pawn;
|
|
break;
|
|
case 'R':
|
|
type = PieceType.Rook;
|
|
break;
|
|
case 'N':
|
|
type = PieceType.Knight;
|
|
break;
|
|
case 'B':
|
|
type = PieceType.Bishop;
|
|
break;
|
|
case 'Q':
|
|
type = PieceType.Queen;
|
|
break;
|
|
case 'K':
|
|
type = PieceType.King;
|
|
break;
|
|
}
|
|
|
|
if (type != null) {
|
|
setPiece(isWhite, type, x, y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* The following methods require more work ! */
|
|
|
|
public boolean isHighlighted(int x, int y) {
|
|
for (int[] pos : highlightedSquares) {
|
|
if (pos[0] == x && pos[1] == y) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void undoLastMove() {
|
|
if (moveHistory.isEmpty()) return;
|
|
|
|
Move lastMove = moveHistory.remove(moveHistory.size() - 1);
|
|
|
|
// Remove piece from destination
|
|
Piece movedPiece = getPieceAt(lastMove.getToX(), lastMove.getToY());
|
|
if (movedPiece != null) {
|
|
pieces.remove(movedPiece);
|
|
}
|
|
|
|
// Restore moved piece to original location
|
|
setPiece(lastMove.isWhite(), lastMove.getType(), lastMove.getFromX(), lastMove.getFromY());
|
|
|
|
// Restore captured piece if there was one
|
|
if (lastMove.getCapturedPiece() != null) {
|
|
Piece cap = lastMove.getCapturedPiece();
|
|
setPiece(cap.isWhite(), cap.getType(), cap.getX(), cap.getY());
|
|
}
|
|
|
|
turnNumber--;
|
|
turnWhite = !turnWhite;
|
|
}
|
|
|
|
public Board(Board board) {
|
|
this.width = board.getWidth();
|
|
this.height = board.getHeight();
|
|
this.turnNumber = board.getTurnNumber();
|
|
this.turnWhite = board.isTurnWhite();
|
|
|
|
this.pieces = new ArrayList<>();
|
|
for (Piece p : board.getPieces()) {
|
|
// Create a new Piece with the same values (deep copy)
|
|
Piece copy = new Piece(p.isWhite(), p.getType(), p.getX(), p.getY());
|
|
this.pieces.add(copy);
|
|
}
|
|
|
|
// Optional: copy selected square if needed
|
|
this.selectedX = board.selectedX;
|
|
this.selectedY = board.selectedY;
|
|
|
|
// Optional: copy highlighted squares
|
|
this.highlightedSquares = new ArrayList<>();
|
|
for (int[] square : board.highlightedSquares) {
|
|
this.highlightedSquares.add(new int[]{square[0], square[1]});
|
|
}
|
|
|
|
// Optional: copy move history (use only if Move has a proper copy strategy)
|
|
this.moveHistory = new ArrayList<>(board.moveHistory);
|
|
}
|
|
|
|
|
|
public void playMove(Move move) {
|
|
// Get the piece to move
|
|
Piece pieceToMove = getPieceAt(move.getFromX(), move.getFromY());
|
|
if (pieceToMove == null) return; // Invalid move, nothing to move
|
|
|
|
// Capture if there's a piece at the destination
|
|
Piece captured = getPieceAt(move.getToX(), move.getToY());
|
|
if (captured != null) {
|
|
pieces.remove(captured);
|
|
}
|
|
|
|
// Remove the original piece
|
|
pieces.remove(pieceToMove);
|
|
|
|
// Add the moved piece at new location
|
|
setPiece(move.isWhite(), move.getType(), move.getToX(), move.getToY());
|
|
|
|
// Save move to history
|
|
moveHistory.add(move);
|
|
|
|
// Update turn
|
|
turnNumber++;
|
|
turnWhite = !turnWhite;
|
|
|
|
// Clear selection and highlights (optional safety)
|
|
selectedX = null;
|
|
selectedY = null;
|
|
highlightedSquares.clear();
|
|
}
|
|
|
|
|
|
|
|
private Piece getPieceAt(int x, int y) {
|
|
for (Piece p : pieces) {
|
|
if (p.getX() == x && p.getY() == y) {
|
|
return p;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private boolean isEmpty(int x, int y) {
|
|
return getPieceAt(x, y) == null;
|
|
}
|
|
|
|
private boolean isEnemy(int x, int y, boolean isWhite) {
|
|
Piece p = getPieceAt(x, y);
|
|
return p != null && p.isWhite() != isWhite;
|
|
}
|
|
|
|
|
|
public ArrayList<int[]> computeLegalMoves(Piece piece) {
|
|
ArrayList<int[]> moves = new ArrayList<>();
|
|
int x = piece.getX();
|
|
int y = piece.getY();
|
|
PieceType type = piece.getType();
|
|
|
|
if (type == PieceType.Pawn) {
|
|
int dir = piece.isWhite() ? -1 : 1;
|
|
int nextY = y + dir;
|
|
|
|
// Move forward if square is empty
|
|
if (isEmpty(x, nextY)) {
|
|
moves.add(new int[]{x, nextY});
|
|
|
|
// First move double step
|
|
int startRow = piece.isWhite() ? 6 : 1;
|
|
int twoStepsY = y + 2 * dir;
|
|
if (y == startRow && isEmpty(x, twoStepsY)) {
|
|
moves.add(new int[]{x, twoStepsY});
|
|
}
|
|
if (nextY == 7 ){
|
|
setPiece(false , PieceType.Queen, x,nextY);
|
|
pieces.remove(piece);
|
|
}
|
|
if (nextY==0 ){
|
|
setPiece(true , PieceType.Queen, x,nextY);
|
|
pieces.remove(piece);
|
|
}
|
|
}
|
|
|
|
// Diagonal capture
|
|
if (isEnemy(x - 1, nextY, piece.isWhite())) {
|
|
moves.add(new int[]{x - 1, nextY});
|
|
}
|
|
if (isEnemy(x + 1, nextY, piece.isWhite())) {
|
|
moves.add(new int[]{x + 1, nextY});
|
|
}
|
|
|
|
// En passant
|
|
if (enPassantTarget != null) {
|
|
int targetX = enPassantTarget[0];
|
|
int targetY = enPassantTarget[1];
|
|
if (nextY == targetY && Math.abs(x - targetX) == 1) {
|
|
moves.add(new int[]{targetX, targetY});
|
|
}
|
|
}
|
|
|
|
// Promotion check (you shouldn't promote here — only on move execution)
|
|
// Do NOT change piece type inside this method
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (type == PieceType.Rook) {
|
|
// Directions : haut, bas, gauche, droite
|
|
int[][] directions = {{1,0},{-1,0},{0,1},{0,-1}};
|
|
for (int[] dir : directions) {
|
|
int nx = x + dir[0];
|
|
int ny = y + dir[1];
|
|
while (nx >= 0 && nx < width && ny >= 0 && ny < height) {
|
|
if (isEmpty(nx, ny)) {
|
|
moves.add(new int[]{nx, ny});
|
|
} else {
|
|
if (isEnemy(nx, ny, piece.isWhite())) {
|
|
moves.add(new int[]{nx, ny});
|
|
}
|
|
break;
|
|
}
|
|
nx += dir[0];
|
|
ny += dir[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type == PieceType.Bishop) {
|
|
// Directions : diagonales
|
|
int[][] directions = {{1,1},{1,-1},{-1,1},{-1,-1}};
|
|
for (int[] dir : directions) {
|
|
int nx = x + dir[0];
|
|
int ny = y + dir[1];
|
|
while (nx >= 0 && nx < width && ny >= 0 && ny < height) {
|
|
if (isEmpty(nx, ny)) {
|
|
moves.add(new int[]{nx, ny});
|
|
} else {
|
|
if (isEnemy(nx, ny, piece.isWhite())) {
|
|
moves.add(new int[]{nx, ny});
|
|
}
|
|
break;
|
|
}
|
|
nx += dir[0];
|
|
ny += dir[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type == PieceType.Queen) {
|
|
// Combine Rook + Bishop
|
|
int[][] directions = {{1,0},{-1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};
|
|
for (int[] dir : directions) {
|
|
int nx = x + dir[0];
|
|
int ny = y + dir[1];
|
|
while (nx >= 0 && nx < width && ny >= 0 && ny < height) {
|
|
if (isEmpty(nx, ny)) {
|
|
moves.add(new int[]{nx, ny});
|
|
} else {
|
|
if (isEnemy(nx, ny, piece.isWhite())) {
|
|
moves.add(new int[]{nx, ny});
|
|
}
|
|
break;
|
|
}
|
|
nx += dir[0];
|
|
ny += dir[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type == PieceType.Knight) {
|
|
// 8 mouvements possibles
|
|
int[][] jumps = {{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}};
|
|
for (int[] jump : jumps) {
|
|
int nx = x + jump[0];
|
|
int ny = y + jump[1];
|
|
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
|
|
if (isEmpty(nx, ny) || isEnemy(nx, ny, piece.isWhite())) {
|
|
moves.add(new int[]{nx, ny});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type == PieceType.King) {
|
|
// 8 directions, une seule case
|
|
int[][] directions = {{1,0},{-1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};
|
|
for (int[] dir : directions) {
|
|
int nx = x + dir[0];
|
|
int ny = y + dir[1];
|
|
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
|
|
if (isEmpty(nx, ny) || isEnemy(nx, ny, piece.isWhite())) {
|
|
moves.add(new int[]{nx, ny});
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
return moves;
|
|
|
|
|
|
}
|
|
|
|
} |