371 lines
14 KiB
Java
371 lines
14 KiB
Java
package backend;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Scanner;
|
|
|
|
public class Board {
|
|
private final Scanner scanner = new Scanner(System.in);
|
|
private int selectedX = -1; // negative value means impossible x and y so unselected
|
|
private int selectedY = -1;
|
|
|
|
private int turnNumber = 0; // tracks current turn
|
|
public int width; // enables to define the dimensions of board (public because used in console)
|
|
public int height;
|
|
private Piece[][] board; // 2D array chess board
|
|
private ArrayList<int[]> highlightedPositions = new ArrayList<>(); // list of valid positions to highlight
|
|
|
|
public Board(int colNum, int lineNum) {
|
|
this.width = colNum;
|
|
this.height = lineNum;
|
|
this.board = new Piece[width][height]; // first empty board *********REVIEW************
|
|
clearConsole();
|
|
System.out.println(toString()); // print the chess at the beginning of the game
|
|
}
|
|
|
|
public int getWidth() {
|
|
return width;
|
|
}
|
|
|
|
public int getHeight() {
|
|
return height;
|
|
}
|
|
|
|
// new piece on the board at x,y (More specifically changes the empty cell of coordinates x,y with a new chess piece)
|
|
public void setPiece(boolean isWhite, PieceType type, int x, int y) {
|
|
board[x][y] = new Piece(x, y, type, isWhite);
|
|
}
|
|
|
|
public boolean isTurnWhite() {
|
|
if (turnNumber % 2 == 0) { // even turns including 0 are white's ones (% calculates the reminder of the euclidean division)
|
|
return true;
|
|
} else { // same reasoning, odd turns are black's ones
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public int getTurnNumber() { // this class enables to obtain the current turn number while increment adds 1 to this value for each turn
|
|
return turnNumber; // Necessarly in two functions to get rid of an infinite loop ****WHY****
|
|
}
|
|
|
|
public void incrementTurn() {
|
|
turnNumber++;
|
|
}
|
|
|
|
// set up the classic chess board taking it as a matrix and putting each corresponding starting piece at its place 0,0 is the top left spot of the board
|
|
public void populateBoard() {
|
|
// Black
|
|
setPiece(false, PieceType.Rook, 0, 0);
|
|
setPiece(false, PieceType.Knight, 1, 0);
|
|
setPiece(false, PieceType.Bishop, 2, 0);
|
|
setPiece(false, PieceType.Queen, 3, 0);
|
|
setPiece(false, PieceType.King, 4, 0);
|
|
setPiece(false, PieceType.Bishop, 5, 0);
|
|
setPiece(false, PieceType.Knight, 6, 0);
|
|
setPiece(false, PieceType.Rook, 7, 0);
|
|
|
|
// Black pawns
|
|
for (int i = 0; i < 8; i++) {
|
|
setPiece(false, PieceType.Pawn, i, 1);
|
|
}
|
|
|
|
// White pawns
|
|
for (int i = 0; i < 8; i++) {
|
|
setPiece(true, PieceType.Pawn, i, 6);
|
|
}
|
|
|
|
// White
|
|
setPiece(true, PieceType.Rook, 0, 7);
|
|
setPiece(true, PieceType.Knight, 1, 7);
|
|
setPiece(true, PieceType.Bishop, 2, 7);
|
|
setPiece(true, PieceType.Queen, 3, 7);
|
|
setPiece(true, PieceType.King, 4, 7);
|
|
setPiece(true, PieceType.Bishop, 5, 7);
|
|
setPiece(true, PieceType.Knight, 6, 7);
|
|
setPiece(true, PieceType.Rook, 7, 7);
|
|
}
|
|
|
|
public void cleanBoard() {
|
|
for (int x = 0; x < width; x++) {
|
|
for (int y = 0; y < height; y++) {
|
|
board[x][y] = null; // each position becomes empty
|
|
}
|
|
}
|
|
}
|
|
public Piece getPiece(int x, int y) {
|
|
if (!isInBounds (x, y)) return null;
|
|
return board [x][y];
|
|
}
|
|
|
|
private void clearConsole() { // ***************CONSOLE
|
|
for (int i = 0; i < 50; i++) {
|
|
System.out.println(); // Print 50 empty lines to "clear" the console
|
|
}
|
|
}
|
|
|
|
public String toString() {
|
|
StringBuilder str = new StringBuilder();
|
|
str.append(" A B C D E F G H\n"); // columns letter at the top
|
|
|
|
// representation of the rows
|
|
for (int y = 0; y < height; y++) {
|
|
str.append(8 - y).append(" "); // row number on the left
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
if (board[x][y] == null) {
|
|
str.append("- "); // empty positions
|
|
} else {
|
|
// convert each piece of both color into a character
|
|
Piece piece = board[x][y];
|
|
char pieceChar;
|
|
|
|
switch (piece.getType()) { // switch function avoids too many if-else
|
|
case King: pieceChar = 'K'; break;
|
|
case Queen: pieceChar = 'Q'; break;
|
|
case Bishop: pieceChar = 'B'; break;
|
|
case Knight: pieceChar = 'N'; break; // N because we already have King
|
|
case Rook: pieceChar = 'R'; break;
|
|
case Pawn: pieceChar = 'P'; break;
|
|
default: pieceChar = '?'; break; // safety net
|
|
}
|
|
|
|
// Make black pieces in lowercase
|
|
if (!piece.isWhite()) {
|
|
pieceChar = Character.toLowerCase(pieceChar);
|
|
}
|
|
|
|
str.append(pieceChar).append(" "); // gives structure to the output
|
|
}
|
|
}
|
|
str.append("\n"); // change of row
|
|
}
|
|
|
|
// Additional infos for a proper output
|
|
str.append("Turn ").append(getTurnNumber()).append(": ");
|
|
str.append(isTurnWhite() ? "White" : "Black");
|
|
|
|
return str.toString();
|
|
}
|
|
|
|
// list the placement of the pieces on the board
|
|
public ArrayList<Piece> getPieces() {
|
|
ArrayList<Piece> pieces = new ArrayList<>();
|
|
// collect infos for the non-empty positions
|
|
for (int x = 0; x < width; x++) {
|
|
for (int y = 0; y < height; y++) {
|
|
if (board[x][y] != null) {
|
|
pieces.add(board[x][y]);
|
|
}
|
|
}
|
|
}
|
|
return pieces;
|
|
}
|
|
|
|
// user clicks on the board
|
|
public void userTouch(int x, int y) {
|
|
if (selectedX == -1 && selectedY == -1) { // This condition is only possible at the very start of the game
|
|
// check if the position is empty and the color
|
|
if (board[x][y] != null && board[x][y].isWhite() == isTurnWhite()) {
|
|
// select it as active location
|
|
selectedX = x;
|
|
selectedY = y;
|
|
highlightedPositions = getValidMoves(board[x][y]); // compute moves
|
|
}
|
|
} else {
|
|
if (x == selectedX && y == selectedY) {
|
|
// unselect it if the destination is unvalid (not highlighted)
|
|
selectedX = -1;
|
|
selectedY = -1;
|
|
highlightedPositions.clear();
|
|
} else {
|
|
// move if valid destination
|
|
boolean valid = false;
|
|
for (int[] pos : highlightedPositions) {
|
|
if (pos[0] == x && pos[1] == y) {
|
|
valid = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (valid) {
|
|
Piece pieceToMove = board[selectedX][selectedY];
|
|
|
|
if(pieceToMove.getType() == PieceType.King && Math.abs(x - selectedX)==2) {
|
|
y = selectedY;
|
|
if (x == 6) {
|
|
board[5][y] = board[7][y];
|
|
board[7][y] = null;
|
|
board[5][y].setMoved(true);
|
|
board[5][y].setX(5);
|
|
board[5][y].setY(y);
|
|
} else if (x == 2) {
|
|
//System.out.println(board [3][y]);
|
|
//System.out.println(board [0][y]);
|
|
board [3][y] = board[0][y];
|
|
board [0][y] = null;
|
|
//System.out.println(board [3][y]);
|
|
//System.out.println(board [0][y]);
|
|
board[3][y].setMoved(true);
|
|
board[3][y].setX(3);
|
|
board[3][y].setY(y);
|
|
}
|
|
}
|
|
|
|
board[x][y] = new Piece(x, y, pieceToMove.getType(), pieceToMove.isWhite());
|
|
board[selectedX][selectedY] = null;
|
|
if (pieceToMove.getType() == PieceType.Pawn && (y == 0 || y == 7)) {
|
|
PieceType promotionType = askPromotionChoice(pieceToMove.isWhite());
|
|
board[x][y] = new Piece(x, y, promotionType, pieceToMove.isWhite());
|
|
}
|
|
board[x][y].setMoved(true);
|
|
incrementTurn();
|
|
|
|
}
|
|
|
|
// reset selection
|
|
selectedX = -1;
|
|
selectedY = -1;
|
|
highlightedPositions.clear();
|
|
clearConsole();
|
|
System.out.println(toString());
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean isSelected(int x, int y) {
|
|
return (x == selectedX && y == selectedY); // true if matching position
|
|
}
|
|
|
|
public boolean isHighlighted(int x, int y) {
|
|
for (int[] pos : highlightedPositions) {
|
|
if (pos[0] == x && pos[1] == y) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* utility methods */
|
|
public boolean isInBounds(int x, int y) {
|
|
return x >= 0 && x < width && y >= 0 && y < height;
|
|
}
|
|
|
|
private ArrayList<int[]> getValidMoves(Piece piece) {
|
|
return piece.getValidMoves(this);
|
|
}
|
|
|
|
public String[] toFileRep() {
|
|
String[] rep = new String[height + 1]; // height lines for the board + 1 for turn info
|
|
|
|
// taking each row
|
|
for (int y = 0; y < height; y++) {
|
|
StringBuilder line = new StringBuilder();
|
|
//now each column
|
|
for (int x = 0; x < width; x++) {
|
|
if (board[x][y] == null) {
|
|
line.append("-"); //empty square
|
|
} else {
|
|
// Convert each piece to a character using the same logic as toString()
|
|
Piece piece = board[x][y];
|
|
String pieceChar = piece.getType().getSummary(); //get the character representation
|
|
|
|
// Make black pieces lowercase, just like in toString()
|
|
if (!piece.isWhite()) {
|
|
pieceChar = pieceChar.toLowerCase();
|
|
}
|
|
|
|
line.append(pieceChar);
|
|
}
|
|
|
|
// Add a comma after each square except the last one
|
|
if (x < width - 1) {
|
|
line.append(",");
|
|
}
|
|
}
|
|
|
|
rep[y] = line.toString();//store line
|
|
}
|
|
|
|
// Add turn information as the last line
|
|
rep[height] = isTurnWhite() ? "W" : "B";
|
|
|
|
return rep;
|
|
}
|
|
|
|
public Board(String[] fileRepresentation) {
|
|
// Determine board dimensions from the file
|
|
this.height = fileRepresentation.length - 1; // Last line is turn info
|
|
this.width = fileRepresentation[0].split(",").length;//width counting with commas
|
|
this.board = new Piece[width][height];
|
|
this.selectedX = -1;//no position selected at beginning
|
|
this.selectedY = -1;
|
|
this.highlightedPositions = new ArrayList<>();//no highlighted position at beginning
|
|
|
|
// Process each row
|
|
for (int y = 0; y < height; y++) {
|
|
String[] squares = fileRepresentation[y].split(",");//split rows with commas
|
|
|
|
for (int x = 0; x < width; x++) {//take the square infos
|
|
String squareData = squares[x];
|
|
|
|
// Skip empty squares
|
|
if (squareData.equals("-")) {
|
|
continue;
|
|
}
|
|
|
|
char pieceChar = squareData.charAt(0);//take the character of the piece
|
|
|
|
// Determine color by case: uppercase = white, lowercase = black
|
|
boolean isWhite = Character.isUpperCase(pieceChar);
|
|
|
|
// Convert to uppercase for the fromSummary method if it's lowercase
|
|
if (!isWhite) {
|
|
pieceChar = Character.toUpperCase(pieceChar);
|
|
}
|
|
|
|
// Get piece type
|
|
PieceType type = PieceType.fromSummary(pieceChar);
|
|
|
|
// Create the piece
|
|
setPiece(isWhite, type, x, y);
|
|
}
|
|
}
|
|
|
|
// Set turn information
|
|
String turnInfo = fileRepresentation[height];
|
|
this.turnNumber = turnInfo.equals("W") ? 0 : 1;//white=even, black=odd
|
|
|
|
clearConsole();
|
|
System.out.println(toString());
|
|
}
|
|
private PieceType askPromotionChoice(boolean isWhite) {
|
|
System.out.println("Choose piece to promote to:");
|
|
System.out.println("Enter Q for Queen, R for Rook, B for Bishop, N for Knight");
|
|
|
|
while (true) {
|
|
String input = scanner.nextLine().trim().toUpperCase();
|
|
switch (input) {
|
|
case "Q": return PieceType.Queen;
|
|
case "R": return PieceType.Rook;
|
|
case "B": return PieceType.Bishop;
|
|
case "N": return PieceType.Knight;
|
|
default:
|
|
System.out.println("Invalid choice. Please enter Q, R, B, or N.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* additional functionality to implement later */
|
|
public void undoLastMove() {
|
|
// TODO
|
|
}
|
|
|
|
public void playMove(Move move) {
|
|
// TODO
|
|
}
|
|
|
|
public Board(Board board) {
|
|
// TODO
|
|
}
|
|
}
|