OOP_Group2B4_Project/src/backend/Board.java

544 lines
16 KiB
Java

package backend;
import java.util.ArrayList;
import java.util.Stack;
public class Board {
private Piece lastDoubleStepPawn = null;
private int width;
private int height;
private ArrayList<Piece> Pieces;
private Stack<Move> moveHistory = new Stack<>();
// ── NEW FIELDS FOR userTouch ───────────────────────────────────────
private boolean hasSelection = false; // did we already click to pick up a piece?
private int selectedX, selectedY; // if so, which square is “in hand”?
private int turnNumber = 0; // how many half-moves have been played?
private boolean turnWhite = true; // true = White to play, false = Black
// ─────────────────────────────────────────────────────────────
public String[] toFileRep() {
String[] lines = new String[height + 1];
for (int y = 0; y < height; y++) {
StringBuilder sb = new StringBuilder();
for (int x = 0; x < width; x++) {
Piece p = getPieceAt(x, y);
if (p == null) {
sb.append("..");
} else {
sb.append(p.isWhite() ? "W" : "B");
sb.append(p.getType().getSummary());
}
if (x < width - 1) sb.append(",");
}
lines[y] = sb.toString();
}
lines[height] = isTurnWhite() ? "W" : "B";
return lines;
}
public Board(String[] array) {
this.width = 8;
this.height = 8;
this.Pieces = new ArrayList<>();
this.moveHistory = new Stack<>();
this.hasSelection = false;
this.turnNumber = 0;
for (int y = 0; y < 8; y++) {
String[] cells = array[y].split(",");
for (int x = 0; x < 8; x++) {
String cell = cells[x];
if (!cell.equals("..")) {
boolean isWhite = cell.charAt(0) == 'W';
PieceType type = PieceType.fromSummary(cell.charAt(1));
Pieces.add(new Piece(x, y, isWhite, type));
}
}
}
this.turnWhite = array[8].equals("W");
}
public Board(int width, int height) {
this.width = width;
this.height = height;
this.Pieces = new ArrayList<>();
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getTurnNumber() {
//TODO
return turnNumber;
}
public boolean isTurnWhite() {
//TODO
return turnWhite;
}
public void setPiece(boolean isWhite, PieceType type, int x, int y) {
// 1) Remove any piece already at this square
for (int i = 0; i < Pieces.size(); i++) {
Piece p = Pieces.get(i);
if (p.getX() == x && p.getY() == y) {
Pieces.remove(i);
break;
}
}
// 2) Add the new piece
Pieces.add(new Piece(x, y, isWhite, type));
}
public void populateBoard() {
// black
Pieces.add(new Piece(0,0,false,PieceType.Rook));
Pieces.add(new Piece(1,0,false,PieceType.Knight));
Pieces.add(new Piece(2,0,false,PieceType.Bishop));
Pieces.add(new Piece(3,0,false,PieceType.Queen));
Pieces.add(new Piece(4,0,false,PieceType.King));
Pieces.add(new Piece(5,0,false,PieceType.Bishop));
Pieces.add(new Piece(6,0,false,PieceType.Knight));
Pieces.add(new Piece(7,0,false,PieceType.Rook));
Pieces.add(new Piece(0,1,false,PieceType.Pawn));
Pieces.add(new Piece(1,1,false,PieceType.Pawn));
Pieces.add(new Piece(2,1,false,PieceType.Pawn));
Pieces.add(new Piece(3,1,false,PieceType.Pawn));
Pieces.add(new Piece(4,1,false,PieceType.Pawn));
Pieces.add(new Piece(5,1,false,PieceType.Pawn));
Pieces.add(new Piece(6,1,false,PieceType.Pawn));
Pieces.add(new Piece(7,1,false,PieceType.Pawn));
// white
Pieces.add(new Piece(0, 7, true, PieceType.Rook));
Pieces.add(new Piece(1, 7, true, PieceType.Knight));
Pieces.add(new Piece(2, 7, true, PieceType.Bishop));
Pieces.add(new Piece(3, 7, true, PieceType.Queen));
Pieces.add(new Piece(4, 7, true, PieceType.King));
Pieces.add(new Piece(5, 7, true, PieceType.Bishop));
Pieces.add(new Piece(6, 7, true, PieceType.Knight));
Pieces.add(new Piece(7, 7, true, PieceType.Rook));
Pieces.add(new Piece(0,6,true,PieceType.Pawn));
Pieces.add(new Piece(1,6,true,PieceType.Pawn));
Pieces.add(new Piece(2,6,true,PieceType.Pawn));
Pieces.add(new Piece(3,6,true,PieceType.Pawn));
Pieces.add(new Piece(4,6,true,PieceType.Pawn));
Pieces.add(new Piece(5,6,true,PieceType.Pawn));
Pieces.add(new Piece(6,6,true,PieceType.Pawn));
Pieces.add(new Piece(7,6,true,PieceType.Pawn));
}
public ArrayList<Piece> getPieces(){
return Pieces;
}
public void cleanBoard() {
Pieces.clear();
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Piece found = null;
for (Piece p : Pieces) {
if (p.getX() == x && p.getY() == y) {
found = p;
break;
}
}
if (found != null) {
String color = found.isWhite() ? "W" : "B";
sb.append(color).append(found.getType().getSummary()).append(" ");
} else {
sb.append(".. ");
}
}
sb.append("\n");
}
return sb.toString();
}
public void userTouch(int x, int y) {
// 1) Find if you clicked on a piece at (x,y)
Piece clicked = null;
for (Piece p : Pieces) {
if (p.getX() == x && p.getY() == y) {
clicked = p;
break;
}
}
// 2) If nothing is selected, select a piece of the correct color
if (!hasSelection) {
if (clicked != null && clicked.isWhite() == turnWhite) {
hasSelection = true;
selectedX = x;
selectedY = y;
}
return;
}
// 3) If you click the same square again → deselect
if (x == selectedX && y == selectedY) {
hasSelection = false;
return;
}
// 4) If the square is not highlighted (illegal), ignore it
if (!isHighlighted(x, y)) {
return;
}
// 5) Do the move
// Capture (including en passant)
Piece captured = null;
for (Piece p : Pieces) {
if (p.getX() == x && p.getY() == y) {
captured = p;
break;
}
}
// Get the piece to move (only declare once!)
Piece toMove = null;
for (Piece p : Pieces) {
if (p.getX() == selectedX && p.getY() == selectedY) {
toMove = p;
break;
}
}
// En passant condition
if (captured == null && toMove != null && toMove.getType() == PieceType.Pawn) {
int dir = toMove.isWhite() ? -1 : 1;
if (lastDoubleStepPawn != null &&
lastDoubleStepPawn.getX() == x &&
lastDoubleStepPawn.getY() == y - dir &&
lastDoubleStepPawn.isWhite() != toMove.isWhite()) {
Pieces.removeIf(p -> p.getX() == lastDoubleStepPawn.getX() && p.getY() == lastDoubleStepPawn.getY());
captured = lastDoubleStepPawn;
}
}
// Normal capture
if (captured != null) {
Pieces.remove(captured);
}
// Move the selected piece
if (toMove != null) {
Pieces.removeIf(p -> p.getX() == selectedX && p.getY() == selectedY);
// Check for promotion
boolean isWhite = toMove.isWhite();
PieceType type = toMove.getType();
if (type == PieceType.Pawn && ((isWhite && y == 0) || (!isWhite && y == 7))) {
// Promote to queen
Pieces.add(new Piece(x, y, isWhite, PieceType.Queen));
} else {
// Normal move
Pieces.add(new Piece(x, y, isWhite, type));
}
// Track if this move is a double pawn move
if (type == PieceType.Pawn && Math.abs(y - toMove.getY()) == 2) {
lastDoubleStepPawn = new Piece(x, y, isWhite, PieceType.Pawn);
} else {
lastDoubleStepPawn = null;
}
// Update state
turnNumber++;
turnWhite = !turnWhite;
hasSelection = false;
Move move = new Move(toMove, x, y, captured); // captured may be null
moveHistory.push(move);
System.out.println(this);
}
}
public boolean isSelected(int x, int y) {
// return true if the exact square is currently picked up
return hasSelection && selectedX == x && selectedY == y;
// hasSelection is true the moment the user clicks on a piece for the first time
}
/* The following methods require more work ! */
public boolean isHighlighted(int x, int y) {
if (!hasSelection) return false;
// Find the selected piece
Piece selected = null;
for (Piece p : Pieces) {
if (p.getX() == selectedX && p.getY() == selectedY) {
selected = p;
break;
}
}
if (selected == null) return false;
int sx = selected.getX();
int sy = selected.getY();
boolean isWhite = selected.isWhite();
// PAWN LOGIC
if (selected.getType() == PieceType.Pawn) {
int dir = isWhite ? -1 : 1;
// One square forward
if (x == sx && y == sy + dir) {
if (getPieceAt(x, y) == null) return true;
}
// Two squares from initial rank
if ((isWhite && sy == 6) || (!isWhite && sy == 1)) {
if (x == sx && y == sy + 2 * dir) {
if (getPieceAt(sx, sy + dir) == null && getPieceAt(x, y) == null) {
return true;
}
}
}
// Capture diagonally
if ((x == sx + 1 || x == sx - 1) && y == sy + dir) {
Piece target = getPieceAt(x, y);
if (target != null && target.isWhite() != isWhite) return true;
}
// En passant highlight
if ((x == sx + 1 || x == sx - 1) && y == sy + dir) {
if (lastDoubleStepPawn != null &&
lastDoubleStepPawn.getX() == x &&
lastDoubleStepPawn.getY() == sy &&
lastDoubleStepPawn.isWhite() != isWhite) {
return true;
}
}
}
// ROOK LOGIC
if (selected.getType() == PieceType.Rook) {
int[][] directions = { {1,0}, {-1,0}, {0,1}, {0,-1} };
for (int[] d : directions) {
int cx = sx + d[0];
int cy = sy + d[1];
while (cx >= 0 && cx < width && cy >= 0 && cy < height) {
Piece target = getPieceAt(cx, cy);
if (target == null) {
if (cx == x && cy == y) return true;
} else {
if (target.isWhite() != isWhite && cx == x && cy == y) return true;
break;
}
cx += d[0];
cy += d[1];
}
}
}
if (selected.getType() == PieceType.Knight) {
int[][] moves = {
{1, 2}, {2, 1}, {2, -1}, {1, -2},
{-1, -2}, {-2, -1}, {-2, 1}, {-1, 2}
};
for (int[] m : moves) {
int nx = sx + m[0];
int ny = sy + m[1];
if (nx == x && ny == y && nx >= 0 && nx < width && ny >= 0 && ny < height) {
Piece target = getPieceAt(nx, ny);
if (target == null || target.isWhite() != isWhite) {
return true;
}
}
}
}
// BISHOP LOGIC
if (selected.getType() == PieceType.Bishop) {
int[][] directions = { {1,1}, {-1,1}, {1,-1}, {-1,-1} };
for (int[] d : directions) {
int cx = sx + d[0];
int cy = sy + d[1];
while (cx >= 0 && cx < width && cy >= 0 && cy < height) {
Piece target = getPieceAt(cx, cy);
if (target == null) {
if (cx == x && cy == y) return true;
} else {
if (target.isWhite() != isWhite && cx == x && cy == y) return true;
break; // Stop if any piece blocks the diagonal
}
cx += d[0];
cy += d[1];
}
}
}
// QUEEN LOGIC
if (selected.getType() == PieceType.Queen) {
int[][] directions = {
{1, 0}, {-1, 0}, {0, 1}, {0, -1}, // Rook directions
{1, 1}, {-1, 1}, {1, -1}, {-1, -1} // Bishop directions
};
for (int[] d : directions) {
int cx = sx + d[0];
int cy = sy + d[1];
while (cx >= 0 && cx < width && cy >= 0 && cy < height) {
Piece target = getPieceAt(cx, cy);
if (target == null) {
if (cx == x && cy == y) return true;
} else {
if (target.isWhite() != isWhite && cx == x && cy == y) return true;
break; // Stop if a piece blocks further movement
}
cx += d[0];
cy += d[1];
}
}
}
// KING LOGIC
if (selected.getType() == PieceType.King) {
int[][] directions = {
{1, 0}, {-1, 0}, {0, 1}, {0, -1},
{1, 1}, {-1, 1}, {1, -1}, {-1, -1}
};
for (int[] d : directions) {
int nx = sx + d[0];
int ny = sy + d[1];
if (nx == x && ny == y && nx >= 0 && nx < width && ny >= 0 && ny < height) {
Piece target = getPieceAt(nx, ny);
if (target == null || target.isWhite() != isWhite) {
return true;
}
}
}
}
return false;
}
public void undoLastMove() {
if (moveHistory.isEmpty()) return;
// Get the last move from the history
Move last = moveHistory.pop();
// Remove the piece from the destination square
Pieces.removeIf(p -> p.getX() == last.getToX() && p.getY() == last.getToY());
// Restore the moved piece to its original position
Piece moved = last.getMovedPiece();
Pieces.add(new Piece(last.getFromX(), last.getFromY(), moved.isWhite(), moved.getType()));
// Restore the captured piece if it existed
Piece captured = last.getCapturedPiece();
if (captured != null) {
Pieces.add(new Piece(captured.getX(), captured.getY(), captured.isWhite(), captured.getType()));
}
// Revert turn number and color
turnNumber--;
turnWhite = !turnWhite;
// Clear selection
hasSelection = false;
}
public Board(Board board) {
this.width = board.width;
this.height = board.height;
this.turnNumber = board.turnNumber;
this.turnWhite = board.turnWhite;
this.hasSelection = board.hasSelection;
this.selectedX = board.selectedX;
this.selectedY = board.selectedY;
// Deep copy of pieces
this.Pieces = new ArrayList<>();
for (Piece p : board.Pieces) {
this.Pieces.add(new Piece(p.getX(), p.getY(), p.isWhite(), p.getType()));
}
// Optional: clear move history or leave empty to avoid shared reference
this.moveHistory = new Stack<>();
}
public void playMove(Move move) {
// Remove the piece from the destination (if a capture)
Pieces.removeIf(p -> p.getX() == move.getToX() && p.getY() == move.getToY());
// Remove the moved piece from its old position
Pieces.removeIf(p -> p.getX() == move.getFromX() && p.getY() == move.getFromY());
// Add the moved piece to the new position
Piece moved = move.getMovedPiece();
boolean isWhite = moved.isWhite();
int toY = move.getToY();
PieceType type = moved.getType();
// Check for promotion
if (type == PieceType.Pawn && ((isWhite && toY == 0) || (!isWhite && toY == 7))) {
Pieces.add(new Piece(move.getToX(), toY, isWhite, PieceType.Queen));
} else {
Pieces.add(new Piece(move.getToX(), toY, isWhite, type));
}
// Save the move to history
moveHistory.push(move);
// Update turn
turnNumber++;
turnWhite = !turnWhite;
// Clear selection
hasSelection = false;
}
Piece getPieceAt(int x, int y) {
for (Piece p : Pieces) {
if (p.getX() == x && p.getY() == y) return p;
}
return null;
}
public void selectPiece(int x, int y) {
hasSelection = true;
selectedX = x;
selectedY = y;
}
}