From 4a8e03daefb496778411d68fd6e80be2b737980d Mon Sep 17 00:00:00 2001 From: pierrepagot Date: Fri, 16 May 2025 10:03:02 +0200 Subject: [PATCH 1/3] En passant method --- src/backend/Board.java | 679 ++++++++++++++++++++--------------------- src/backend/Piece.java | 54 ++-- 2 files changed, 360 insertions(+), 373 deletions(-) diff --git a/src/backend/Board.java b/src/backend/Board.java index 1934f34..9dc1323 100644 --- a/src/backend/Board.java +++ b/src/backend/Board.java @@ -1,350 +1,329 @@ -package backend; - -import java.util.ArrayList; - -public class Board { - - 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 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 getPieces() { - ArrayList 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) { - System.out.println("pipi au caca"); - 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("pipi"); - //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; - 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 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()); - } - - - /* additional functionality to implement later */ - public void undoLastMove() { - // TODO - } - - public void playMove(Move move) { - // TODO - } - - public Board(Board board) { - // TODO - } -} +package backend; + +import java.util.ArrayList; + +public class Board { + private int selectedX = -1; + private int selectedY = -1; + private int turnNumber = 0; + public int width; + public int height; + private Piece[][] board; + private ArrayList highlightedPositions = new ArrayList<>(); + private int[] lastPawnDoubleMove = null; // Added for en passant tracking + + public Board(int colNum, int lineNum) { + this.width = colNum; + this.height = lineNum; + this.board = new Piece[width][height]; + clearConsole(); + System.out.println(toString()); + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public void setPiece(boolean isWhite, PieceType type, int x, int y) { + board[x][y] = new Piece(x, y, type, isWhite); + } + + public boolean isTurnWhite() { + return turnNumber % 2 == 0; + } + + public int getTurnNumber() { + return turnNumber; + } + + public void incrementTurn() { + turnNumber++; + } + + 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; + } + } + } + + public Piece getPiece(int x, int y) { + if (!isInBounds(x, y)) return null; + return board[x][y]; + } + + private void clearConsole() { + for (int i = 0; i < 50; i++) { + System.out.println(); + } + } + + public boolean isEnPassantTarget(int x, int y) { + if (lastPawnDoubleMove == null) return false; + if (lastPawnDoubleMove[2] != turnNumber - 1) return false; + return (lastPawnDoubleMove[0] == x && lastPawnDoubleMove[1] == y); + } + + public void userTouch(int x, int y) { + if (selectedX == -1 && selectedY == -1) { + if (board[x][y] != null && board[x][y].isWhite() == isTurnWhite()) { + selectedX = x; + selectedY = y; + highlightedPositions = getValidMoves(board[x][y]); + } + } else { + if (x == selectedX && y == selectedY) { + selectedX = -1; + selectedY = -1; + highlightedPositions.clear(); + } else { + boolean valid = false; + for (int[] pos : highlightedPositions) { + if (pos[0] == x && pos[1] == y) { + valid = true; + break; + } + } + + if (valid) { + Piece pieceToMove = board[selectedX][selectedY]; + + // Check for en passant capture + if (pieceToMove.getType() == PieceType.Pawn && + x != selectedX && + board[x][y] == null) { + board[x][selectedY] = null; // Capture the pawn + } + + // Handle castling + 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) { + board[3][y] = board[0][y]; + board[0][y] = null; + board[3][y].setMoved(true); + board[3][y].setX(3); + board[3][y].setY(y); + } + } + + // Track pawn double move for en passant + if (pieceToMove.getType() == PieceType.Pawn && + Math.abs(y - selectedY) == 2) { + lastPawnDoubleMove = new int[]{x, y, turnNumber}; + } else { + lastPawnDoubleMove = null; + } + + // Move the piece + board[x][y] = new Piece(x, y, pieceToMove.getType(), pieceToMove.isWhite()); + board[x][y].setMoved(true); + board[selectedX][selectedY] = null; + incrementTurn(); + } + + selectedX = -1; + selectedY = -1; + highlightedPositions.clear(); + System.out.println(toString()); + } + } + } + + public boolean isSelected(int x, int y) { + return (x == selectedX && y == selectedY); + } + + public boolean isHighlighted(int x, int y) { + for (int[] pos : highlightedPositions) { + if (pos[0] == x && pos[1] == y) { + return true; + } + } + return false; + } + + public boolean isInBounds(int x, int y) { + return x >= 0 && x < width && y >= 0 && y < height; + } + + private ArrayList getValidMoves(Piece piece) { + return piece.getValidMoves(this); + } + + public ArrayList getPieces() { + ArrayList pieces = new ArrayList<>(); + 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; + } + + public String toString() { + StringBuilder str = new StringBuilder(); + str.append(" A B C D E F G H\n"); + + for (int y = 0; y < height; y++) { + str.append(8 - y).append(" "); + + for (int x = 0; x < width; x++) { + if (board[x][y] == null) { + str.append("- "); + } else { + Piece piece = board[x][y]; + char pieceChar; + + switch (piece.getType()) { + case King: pieceChar = 'K'; break; + case Queen: pieceChar = 'Q'; break; + case Bishop: pieceChar = 'B'; break; + case Knight: pieceChar = 'N'; break; + case Rook: pieceChar = 'R'; break; + case Pawn: pieceChar = 'P'; break; + default: pieceChar = '?'; break; + } + + if (!piece.isWhite()) { + pieceChar = Character.toLowerCase(pieceChar); + } + + str.append(pieceChar).append(" "); + } + } + str.append("\n"); + } + + str.append("Turn ").append(getTurnNumber()).append(": "); + str.append(isTurnWhite() ? "White" : "Black"); + + return str.toString(); + } + + public String[] toFileRep() { + String[] rep = new String[height + 1]; + + for (int y = 0; y < height; y++) { + StringBuilder line = new StringBuilder(); + for (int x = 0; x < width; x++) { + if (board[x][y] == null) { + line.append("-"); + } else { + Piece piece = board[x][y]; + String pieceChar = piece.getType().getSummary(); + + if (!piece.isWhite()) { + pieceChar = pieceChar.toLowerCase(); + } + + line.append(pieceChar); + } + + if (x < width - 1) { + line.append(","); + } + } + + rep[y] = line.toString(); + } + + rep[height] = isTurnWhite() ? "W" : "B"; + + return rep; + } + + public Board(String[] fileRepresentation) { + this.height = fileRepresentation.length - 1; + this.width = fileRepresentation[0].split(",").length; + this.board = new Piece[width][height]; + this.selectedX = -1; + this.selectedY = -1; + this.highlightedPositions = new ArrayList<>(); + + for (int y = 0; y < height; y++) { + String[] squares = fileRepresentation[y].split(","); + + for (int x = 0; x < width; x++) { + String squareData = squares[x]; + + if (squareData.equals("-")) { + continue; + } + + char pieceChar = squareData.charAt(0); + boolean isWhite = Character.isUpperCase(pieceChar); + + if (!isWhite) { + pieceChar = Character.toUpperCase(pieceChar); + } + + PieceType type = PieceType.fromSummary(pieceChar); + setPiece(isWhite, type, x, y); + } + } + + String turnInfo = fileRepresentation[height]; + this.turnNumber = turnInfo.equals("W") ? 0 : 1; + + clearConsole(); + System.out.println(toString()); + } + + public void undoLastMove() { + // TODO + } + + public void playMove(Move move) { + // TODO + } + + public Board(Board board) { + // TODO + } +} \ No newline at end of file diff --git a/src/backend/Piece.java b/src/backend/Piece.java index e681720..7186eeb 100644 --- a/src/backend/Piece.java +++ b/src/backend/Piece.java @@ -54,33 +54,41 @@ public class Piece { int y = this.getY(); switch (type) { - case Pawn: - int direction = isWhite() ? -1 : 1; - int nextY = y + direction; + case Pawn: + int direction = isWhite() ? -1 : 1; + int nextY = y + direction; - // forward move - if (board.isInBounds(x, nextY) && board.getPiece(x, nextY) == null) { - moves.add(new int[]{x, nextY}); + // forward move + if (board.isInBounds(x, nextY) && board.getPiece(x, nextY) == null) { + moves.add(new int[]{x, nextY}); - // double move from starting position - int startRow = isWhite() ? 6 : 1; - int doubleStepY = y + 2 * direction; - if (y == startRow && board.isInBounds(x, doubleStepY) && board.getPiece(x, doubleStepY) == null) { - moves.add(new int[]{x, doubleStepY}); + // double move from starting position + int startRow = isWhite() ? 6 : 1; + int doubleStepY = y + 2 * direction; + if (y == startRow && board.isInBounds(x, doubleStepY) && board.getPiece(x, doubleStepY) == null) { + moves.add(new int[]{x, doubleStepY}); + } + } + + // diagonal captures (including en passant) + for (int dx = -1; dx <= 1; dx += 2) { + int nx = x + dx; + if (board.isInBounds(nx, nextY)) { + Piece target = board.getPiece(nx, nextY); + if (target != null && target.isWhite() != this.isWhite()) { + moves.add(new int[]{nx, nextY}); + } + // Check for en passant + else if (target == null && + board.getPiece(nx, y) != null && + board.getPiece(nx, y).getType() == PieceType.Pawn && + board.getPiece(nx, y).isWhite() != this.isWhite() && + board.isEnPassantTarget(nx, y)) { + moves.add(new int[]{nx, nextY}); } } - - // diagonal captures - for (int dx = -1; dx <= 1; dx += 2) { - int nx = x + dx; - if (board.isInBounds(nx, nextY)) { - Piece target = board.getPiece(nx, nextY); - if (target != null && target.isWhite() != this.isWhite()) { - moves.add(new int[]{nx, nextY}); - } - } - } - break; + } + break; case Rook: addLinearMoves(board, moves, x, y, 1, 0); From d734e3f13d6f7f9415b212e4e8ca8db0189c7c49 Mon Sep 17 00:00:00 2001 From: lrave Date: Fri, 16 May 2025 10:57:53 +0200 Subject: [PATCH 2/3] Promotion --- src/backend/Board.java | 24 +++++++++++++++++++++++- src/backend/Piece.java | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/backend/Board.java b/src/backend/Board.java index e38846c..3124a83 100644 --- a/src/backend/Board.java +++ b/src/backend/Board.java @@ -2,8 +2,10 @@ package backend; import java.util.ArrayList; -public class Board { +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; @@ -208,8 +210,13 @@ public class Board { 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(); @@ -331,7 +338,22 @@ public class Board { 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() { diff --git a/src/backend/Piece.java b/src/backend/Piece.java index e681720..579727d 100644 --- a/src/backend/Piece.java +++ b/src/backend/Piece.java @@ -195,4 +195,5 @@ public class Piece { ny += dy; } } + } \ No newline at end of file From a2567d48a5db77cf2aca153cad39c9b7115135c8 Mon Sep 17 00:00:00 2001 From: lrave Date: Fri, 16 May 2025 11:27:12 +0200 Subject: [PATCH 3/3] commenting --- src/backend/Board.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/Board.java b/src/backend/Board.java index 3124a83..fed66d8 100644 --- a/src/backend/Board.java +++ b/src/backend/Board.java @@ -5,8 +5,8 @@ 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 final Scanner scanner = new Scanner(System.in); // to be able to ask user input and analyze it (from library scanner) + private int selectedX = -1; // negative value means impossible x and y that way before the first move, acts unselected private int selectedY = -1; private int turnNumber = 0; // tracks current turn