Compare commits
2 Commits
d164b88f1e
...
9167153a9e
| Author | SHA1 | Date |
|---|---|---|
|
|
9167153a9e | |
|
|
52a663e698 |
|
|
@ -1,17 +1,258 @@
|
|||
package backend;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
public class AutoPlayer {
|
||||
|
||||
// Piece values used for evaluation and best move to do by calculating later the profit and losses (PnL)
|
||||
private final int PAWN_VALUE = 1;
|
||||
private final int KNIGHT_VALUE = 3;
|
||||
private final int BISHOP_VALUE = 3;
|
||||
private final int ROOK_VALUE = 5;
|
||||
private final int QUEEN_VALUE = 9;
|
||||
private final int KING_VALUE = 100; // High value to prioritize king safety
|
||||
|
||||
/**
|
||||
* returns the best Move to try on provided board for active player
|
||||
* @param board
|
||||
* @return
|
||||
*/
|
||||
/// This chess AI uses a minimax algorithm with alpha-beta pruning to look 2 moves ahead so depth 4
|
||||
// it will evaluate the material value, the center control, and the pawn advancement (ELO 1400-1800)
|
||||
|
||||
private final int MAX_DEPTH = 4;
|
||||
|
||||
// Time limit for search in milliseconds to avoid bug or latence during the game
|
||||
private final long TIME_LIMIT_MS = 5000; // 5 seconds max
|
||||
private long startTime;
|
||||
|
||||
// Random generator if there are several moves equally calculated as good
|
||||
private Random random = new Random();
|
||||
|
||||
//Returns the best Move to try
|
||||
public Move computeBestMove(Board board) {
|
||||
startTime = System.currentTimeMillis();//record start time for search
|
||||
boolean isWhite = board.isTurnWhite();// determine player's turn
|
||||
ArrayList<Move> possibleMoves = generateAllPossibleMoves(board, isWhite);// generate the legal moves
|
||||
|
||||
return null;
|
||||
if (possibleMoves.isEmpty()) {
|
||||
return null; // If no moves available (checkmate or stalemate)
|
||||
}
|
||||
|
||||
Move bestMove = null;//store best move
|
||||
int bestScore = isWhite ? Integer.MIN_VALUE : Integer.MAX_VALUE;//best score for the move: maximize for white and minimize for black
|
||||
|
||||
// Evaluate each possible move
|
||||
for (Move move : possibleMoves) {
|
||||
// Create a copy of the board to simulate the move without changing the real board
|
||||
Board boardCopy = new Board(board);
|
||||
applyMoveToBoard(boardCopy, move);//apply the move to the virtual board
|
||||
|
||||
// Evaluate the resulting position using minimax
|
||||
int score = minimax(boardCopy, MAX_DEPTH - 1, Integer.MIN_VALUE, Integer.MAX_VALUE, !isWhite);
|
||||
|
||||
// Update best move based on the score
|
||||
if ((isWhite && score > bestScore) || (!isWhite && score < bestScore) ||
|
||||
(score == bestScore && random.nextBoolean())) { // Random choice if equal score
|
||||
bestScore = score;//update best score
|
||||
bestMove = move;//update best move
|
||||
}
|
||||
|
||||
// Check if time limit is exceeded and stop search if exceeded
|
||||
if (System.currentTimeMillis() - startTime > TIME_LIMIT_MS) {
|
||||
System.out.println("Time limit exceeded, returning best move found so far");
|
||||
return bestMove;
|
||||
}
|
||||
}
|
||||
|
||||
return bestMove;
|
||||
}
|
||||
//apply the move to the real board
|
||||
private void applyMoveToBoard(Board board, Move move) {
|
||||
int fromX = move.getFromX();//original X coordinate
|
||||
int fromY = move.getFromY();//original Y coordinate
|
||||
int toX = move.getToX();//destination X
|
||||
int toY = move.getToY();//destination Y
|
||||
Piece piece = move.getMovedPiece();//move the piece
|
||||
|
||||
// Make the move by creating the piece a the position
|
||||
board.setPiece(piece.isWhite(), piece.getType(), toX, toY);
|
||||
|
||||
//remove the piece that moved
|
||||
try {
|
||||
//access the board field for modifications
|
||||
java.lang.reflect.Field boardField = Board.class.getDeclaredField("board");
|
||||
boardField.setAccessible(true);//give accessibility to it
|
||||
Piece[][] boardArray = (Piece[][]) boardField.get(board);//board array
|
||||
boardArray[fromX][fromY] = null;//make the case empty
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to update board: " + e.getMessage());
|
||||
}
|
||||
|
||||
// Increment turn
|
||||
board.incrementTurn();
|
||||
}
|
||||
|
||||
//Minimax algorithm with alpha-beta pruning to evaluate positions
|
||||
|
||||
private int minimax(Board board, int depth, int alpha, int beta, boolean isMaximizing) {
|
||||
// Check if time limit is exceeded
|
||||
if (System.currentTimeMillis() - startTime > TIME_LIMIT_MS) {
|
||||
return evaluateBoard(board); // Return current evaluation if out of time
|
||||
}
|
||||
|
||||
// get the board before calculating possible moves
|
||||
if (depth == 0) {
|
||||
return evaluateBoard(board);//initial position
|
||||
}
|
||||
//all possible moves are generated
|
||||
ArrayList<Move> possibleMoves = generateAllPossibleMoves(board, isMaximizing);
|
||||
possibleMoves = orderMoves(possibleMoves);//order moves by their quality
|
||||
|
||||
// No moves available, could be checkmate or stalemate
|
||||
if (possibleMoves.isEmpty()) {
|
||||
return evaluateBoard(board);// current position
|
||||
}
|
||||
|
||||
if (isMaximizing) {//white turn (maximizing player)
|
||||
int maxEval = Integer.MIN_VALUE;//initialize with lowest value
|
||||
for (Move move : possibleMoves) {//try each move
|
||||
Board boardCopy = new Board(board);//create a copy of the board
|
||||
applyMoveToBoard(boardCopy, move);//apply the move
|
||||
int eval = minimax(boardCopy, depth - 1, alpha, beta, false);//evaluate the resulting position
|
||||
maxEval = Math.max(maxEval, eval);//gives best evaluation
|
||||
alpha = Math.max(alpha, eval);//gives the alpha value
|
||||
if (beta <= alpha) {
|
||||
break; // Beta cutoff
|
||||
}
|
||||
}
|
||||
return maxEval;//best evaluation found
|
||||
} else {//black turn
|
||||
int minEval = Integer.MAX_VALUE;//initialize with highest value
|
||||
for (Move move : possibleMoves) {
|
||||
Board boardCopy = new Board(board);
|
||||
applyMoveToBoard(boardCopy, move);
|
||||
int eval = minimax(boardCopy, depth - 1, alpha, beta, true);
|
||||
minEval = Math.min(minEval, eval);
|
||||
beta = Math.min(beta, eval);//gives beta value
|
||||
if (beta <= alpha) {
|
||||
break; // Alpha cutoff
|
||||
}
|
||||
}
|
||||
return minEval;//best evaluation found
|
||||
}
|
||||
}
|
||||
|
||||
//Orders moves to improve alpha-beta pruning efficiency
|
||||
|
||||
private ArrayList<Move> orderMoves(ArrayList<Move> moves) {
|
||||
// Score each move (captures are considered first)
|
||||
for (Move move : moves) {
|
||||
int score = 0;//initialize score for the move
|
||||
|
||||
// Prioritize captures based on a function of the Most Valuable Victim and the Least Valuable Attacker with the system of points
|
||||
if (move.getCapturedPiece() != null) {
|
||||
score += 10 * getPieceValue(move.getCapturedPiece().getType()) -
|
||||
getPieceValue(move.getMovedPiece().getType()) / 10; // Higher score for capturing valuable pieces with less valuable ones
|
||||
}
|
||||
|
||||
// Store the score with the move
|
||||
move.setScore(score);
|
||||
}
|
||||
|
||||
// Sort moves by score in a descending
|
||||
moves.sort((a, b) -> Integer.compare(b.getScore(), a.getScore()));
|
||||
|
||||
return moves;
|
||||
}
|
||||
|
||||
//Evaluates the current board position
|
||||
|
||||
private int evaluateBoard(Board board) {
|
||||
int score = 0;
|
||||
|
||||
// Material evaluation
|
||||
for (Piece piece : board.getPieces()) {
|
||||
int pieceValue = getPieceValue(piece.getType());//get value of this piece
|
||||
if (piece.isWhite()) {
|
||||
score += pieceValue;//add value for white
|
||||
} else {
|
||||
score -= pieceValue;//substract value for black
|
||||
}
|
||||
}
|
||||
|
||||
// Add position evaluation (takes in account the center control bonus)
|
||||
for (Piece piece : board.getPieces()) {
|
||||
int x = piece.getX();
|
||||
int y = piece.getY();
|
||||
|
||||
// Center control bonus (small bonus for controlling center squares)
|
||||
int centerBonus = 0;
|
||||
if ((x == 3 || x == 4) && (y == 3 || y == 4)) {//center 4 squares
|
||||
centerBonus = 1; // Small bonus for center control
|
||||
}
|
||||
|
||||
if (piece.isWhite()) {
|
||||
score += centerBonus;//add for white
|
||||
} else {
|
||||
score -= centerBonus;//subtract for black
|
||||
}
|
||||
|
||||
// Pawn advancement bonus (encourages pawn progression)
|
||||
if (piece.getType() == PieceType.Pawn) {
|
||||
int pawnBonus = 0;
|
||||
if (piece.isWhite()) {
|
||||
pawnBonus = 7 - y; // Higher bonus as white pawns advance
|
||||
score += pawnBonus / 10; // Small fractional bonus
|
||||
} else {
|
||||
pawnBonus = y; // Higher bonus as black pawns advance
|
||||
score -= pawnBonus / 10; // Small fractional bonus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return score;//return final score
|
||||
}
|
||||
|
||||
//Gets the value of a piece
|
||||
|
||||
private int getPieceValue(PieceType type) {
|
||||
switch (type) {
|
||||
case Pawn: return PAWN_VALUE;
|
||||
case Knight: return KNIGHT_VALUE;
|
||||
case Bishop: return BISHOP_VALUE;
|
||||
case Rook: return ROOK_VALUE;
|
||||
case Queen: return QUEEN_VALUE;
|
||||
case King: return KING_VALUE;
|
||||
default: return 0;// safety net to protect from bug
|
||||
}
|
||||
}
|
||||
|
||||
//Generates all possible moves for a player
|
||||
|
||||
private ArrayList<Move> generateAllPossibleMoves(Board board, boolean isWhite) {
|
||||
ArrayList<Move> moves = new ArrayList<>();//list of all moves
|
||||
|
||||
// Get all pieces of the current player
|
||||
for (Piece piece : board.getPieces()) {
|
||||
if (piece.isWhite() == isWhite) {//only consider pieces of the player
|
||||
// Get valid moves for this piece
|
||||
ArrayList<int[]> validMoves = piece.getValidMoves(board);
|
||||
|
||||
for (int[] destination : validMoves) {//move object for each valid destination
|
||||
int toX = destination[0];//X
|
||||
int toY = destination[1];//Y
|
||||
Piece capturedPiece = board.getPiece(toX, toY);//check is a piece is capturable
|
||||
|
||||
// Create a move object with all theses infos
|
||||
Move move = new Move(
|
||||
piece.getX(), piece.getY(),//start X Y
|
||||
toX, toY, //destination X Y
|
||||
piece, //piece moved
|
||||
capturedPiece //piece captured (if there i one)
|
||||
);
|
||||
|
||||
moves.add(move);// add move to list
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return moves;//return all moves
|
||||
}
|
||||
}
|
||||
|
|
@ -3,21 +3,22 @@ package backend;
|
|||
import java.util.ArrayList;
|
||||
|
||||
public class Board {
|
||||
private int selectedX = -1;
|
||||
|
||||
private int selectedX = -1; // negative value means impossible x and y so unselected
|
||||
private int selectedY = -1;
|
||||
private int turnNumber = 0;
|
||||
public int width;
|
||||
|
||||
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;
|
||||
private ArrayList<int[]> highlightedPositions = new ArrayList<>();
|
||||
private int[] lastPawnDoubleMove = null; // Added for en passant tracking
|
||||
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];
|
||||
this.board = new Piece[width][height]; // first empty board *********REVIEW************
|
||||
clearConsole();
|
||||
System.out.println(toString());
|
||||
System.out.println(toString()); // print the chess at the beginning of the game
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
|
|
@ -28,22 +29,28 @@ public class Board {
|
|||
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() {
|
||||
return turnNumber % 2 == 0;
|
||||
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() {
|
||||
return turnNumber;
|
||||
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);
|
||||
|
|
@ -79,41 +86,97 @@ public class Board {
|
|||
public void cleanBoard() {
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
board[x][y] = null;
|
||||
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() {
|
||||
private void clearConsole() { // ***************CONSOLE
|
||||
for (int i = 0; i < 50; i++) {
|
||||
System.out.println();
|
||||
System.out.println(); // Print 50 empty lines to "clear" the console
|
||||
}
|
||||
}
|
||||
|
||||
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 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) {
|
||||
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]);
|
||||
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) {
|
||||
|
|
@ -125,14 +188,6 @@ public class Board {
|
|||
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) {
|
||||
|
|
@ -142,39 +197,36 @@ public class Board {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
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);
|
||||
return (x == selectedX && y == selectedY); // true if matching position
|
||||
}
|
||||
|
||||
public boolean isHighlighted(int x, int y) {
|
||||
|
|
@ -186,6 +238,7 @@ public class Board {
|
|||
return false;
|
||||
}
|
||||
|
||||
/* utility methods */
|
||||
public boolean isInBounds(int x, int y) {
|
||||
return x >= 0 && x < width && y >= 0 && y < height;
|
||||
}
|
||||
|
|
@ -194,70 +247,22 @@ public class Board {
|
|||
return piece.getValidMoves(this);
|
||||
}
|
||||
|
||||
public ArrayList<Piece> getPieces() {
|
||||
ArrayList<Piece> 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];
|
||||
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("-");
|
||||
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();
|
||||
String pieceChar = piece.getType().getSummary(); //get the character representation
|
||||
|
||||
// Make black pieces lowercase, just like in toString()
|
||||
if (!piece.isWhite()) {
|
||||
pieceChar = pieceChar.toLowerCase();
|
||||
}
|
||||
|
|
@ -265,65 +270,112 @@ public class Board {
|
|||
line.append(pieceChar);
|
||||
}
|
||||
|
||||
// Add a comma after each square except the last one
|
||||
if (x < width - 1) {
|
||||
line.append(",");
|
||||
}
|
||||
}
|
||||
|
||||
rep[y] = line.toString();
|
||||
rep[y] = line.toString();//store line
|
||||
}
|
||||
|
||||
// Add turn information as the last line
|
||||
rep[height] = isTurnWhite() ? "W" : "B";
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
public Board(String[] fileRepresentation) {
|
||||
this.height = fileRepresentation.length - 1;
|
||||
this.width = fileRepresentation[0].split(",").length;
|
||||
// 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;
|
||||
this.selectedX = -1;//no position selected at beginning
|
||||
this.selectedY = -1;
|
||||
this.highlightedPositions = new ArrayList<>();
|
||||
this.highlightedPositions = new ArrayList<>();//no highlighted position at beginning
|
||||
|
||||
// Process each row
|
||||
for (int y = 0; y < height; y++) {
|
||||
String[] squares = fileRepresentation[y].split(",");
|
||||
String[] squares = fileRepresentation[y].split(",");//split rows with commas
|
||||
|
||||
for (int x = 0; x < width; x++) {
|
||||
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);
|
||||
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;
|
||||
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
|
||||
int fromX = move.getFromX();
|
||||
int fromY = move.getFromY();
|
||||
int toX = move.getToX();
|
||||
int toY = move.getToY();
|
||||
Piece piece = board[fromX][fromY];
|
||||
|
||||
// Make the move
|
||||
board[toX][toY] = new Piece(toX, toY, piece.getType(), piece.isWhite());
|
||||
board[toX][toY].setMoved(true);
|
||||
board[fromX][fromY] = null;
|
||||
|
||||
incrementTurn();
|
||||
}
|
||||
|
||||
public Board(Board board) {
|
||||
// TODO
|
||||
|
||||
// Copy constructor that creates a copy of another board
|
||||
|
||||
public Board(Board otherBoard) {
|
||||
this.width = otherBoard.width;
|
||||
this.height = otherBoard.height;
|
||||
this.turnNumber = otherBoard.turnNumber;
|
||||
this.selectedX = -1; // Don't copy selection because negative in a matrix
|
||||
this.selectedY = -1;
|
||||
this.board = new Piece[width][height];
|
||||
this.highlightedPositions = new ArrayList<>();
|
||||
|
||||
// Deep copy the board pieces
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
if (otherBoard.board[x][y] != null) {
|
||||
Piece original = otherBoard.board[x][y];
|
||||
this.board[x][y] = new Piece(x, y, original.getType(), original.isWhite());
|
||||
// Remove the hasMoved check since it doesn't exist
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,48 @@
|
|||
package backend;
|
||||
|
||||
import java.util.Optional;
|
||||
// Represents a chess move from one position to another
|
||||
|
||||
/**
|
||||
* Represents a chess move, including the starting and ending positions,
|
||||
* the moving piece, and an optional captured piece.
|
||||
*/
|
||||
public class Move {
|
||||
private int fromX; // Starting X coordinate
|
||||
private int fromY; // Starting Y coordinate
|
||||
private int toX; // Destination X coordinate
|
||||
private int toY; // Destination Y coordinate
|
||||
private Piece movedPiece; // The piece being moved
|
||||
private Piece capturedPiece;// The piece being captured (null if none)
|
||||
private int score = 0; // Used for move ordering in alpha-beta pruning
|
||||
|
||||
//Constructor for a move
|
||||
|
||||
public Move(int fromX, int fromY, int toX, int toY, Piece movedPiece, Piece capturedPiece) {
|
||||
this.fromX = fromX;
|
||||
this.fromY = fromY;
|
||||
this.toX = toX;
|
||||
this.toY = toY;
|
||||
this.movedPiece = movedPiece;
|
||||
this.capturedPiece = capturedPiece;
|
||||
}
|
||||
|
||||
// Getters to access move properties
|
||||
public int getFromX() { return fromX; }
|
||||
public int getFromY() { return fromY; }
|
||||
public int getToX() { return toX; }
|
||||
public int getToY() { return toY; }
|
||||
public Piece getMovedPiece() { return movedPiece; }
|
||||
public Piece getCapturedPiece() { return capturedPiece; }
|
||||
|
||||
// Method for move ordering
|
||||
public void setScore(int score) {
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
public int getScore() {
|
||||
return score;//return the evaluation score of the move
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {//coordinates into chess notation
|
||||
String from = (char)('a' + fromX) + "" + (8 - fromY);
|
||||
String to = (char)('a' + toX) + "" + (8 - toY);
|
||||
return from + " -> " + to;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public class Piece {
|
|||
}
|
||||
}
|
||||
|
||||
// diagonal captures (including en passant)
|
||||
// diagonal captures
|
||||
for (int dx = -1; dx <= 1; dx += 2) {
|
||||
int nx = x + dx;
|
||||
if (board.isInBounds(nx, nextY)) {
|
||||
|
|
@ -78,14 +78,6 @@ public class Piece {
|
|||
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});
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -203,5 +195,4 @@ public class Piece {
|
|||
ny += dy;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue