Final Part (utilisateur is Louis Flichy)

This commit is contained in:
Utilisateur 2025-05-14 13:44:57 +02:00
parent 9e86e6ee48
commit 0a3f54876a
4 changed files with 43 additions and 369 deletions

View File

@ -2,9 +2,9 @@ BR,BN,BB,BQ,BK,BB,BN,BR
BP,BP,BP,BP,BP,BP,BP,BP
--,--,--,--,--,--,--,--
--,--,--,--,--,--,--,--
--,--,--,--,--,--,--,WP
--,--,--,--,--,--,--,--
--,--,--,--,--,--,--,--
WP,WP,WP,WP,WP,WP,WP,WP
WP,WP,WP,WP,WP,WP,WP,--
WR,WN,WB,WQ,WK,WB,WN,WR
W
000000,-1,-1
B
000000,7,5

View File

@ -9,6 +9,7 @@ import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Scanner;
import backend.AutoPlayer;
public class Main {
public static void main(String[] args) {
@ -16,7 +17,16 @@ public class Main {
Board testBoard = new Board(8, 8);
testBoard.populateBoard();
System.out.println(testBoard.toString());
AutoPlayer cpu = new AutoPlayer();
Move cpuMove = cpu.computeBestMove(testBoard);
if (cpuMove != null) {
testBoard.playMove(cpuMove);
System.out.println("AutoPlayer plays: "
+ cpuMove.getFromX() + "," + cpuMove.getFromY()
+ " -> "
+ cpuMove.getToX() + "," + cpuMove.getToY());
System.out.println(testBoard.toString());
}
// Test saving board to file
testSavingBoard(testBoard, "default.board");

View File

@ -4,373 +4,37 @@ import java.util.ArrayList;
import java.util.Random;
public class AutoPlayer {
private static final int DEFAULT_DEPTH = 3;
private int searchDepth;
private Random random = new Random(); // For choosing between equally valued moves
// Piece values for the evaluation function
private static final int PAWN_VALUE = 100;
private static final int KNIGHT_VALUE = 320;
private static final int BISHOP_VALUE = 330;
private static final int ROOK_VALUE = 500;
private static final int QUEEN_VALUE = 900;
private static final int KING_VALUE = 20000;
// Piece-Square tables for positional evaluation
private static final int[][] PAWN_TABLE = {
{0, 0, 0, 0, 0, 0, 0, 0},
{50, 50, 50, 50, 50, 50, 50, 50},
{10, 10, 20, 30, 30, 20, 10, 10},
{5, 5, 10, 25, 25, 10, 5, 5},
{0, 0, 0, 20, 20, 0, 0, 0},
{5, -5,-10, 0, 0,-10, -5, 5},
{5, 10, 10,-20,-20, 10, 10, 5},
{0, 0, 0, 0, 0, 0, 0, 0}
};
private static final int[][] KNIGHT_TABLE = {
{-50,-40,-30,-30,-30,-30,-40,-50},
{-40,-20, 0, 0, 0, 0,-20,-40},
{-30, 0, 10, 15, 15, 10, 0,-30},
{-30, 5, 15, 20, 20, 15, 5,-30},
{-30, 0, 15, 20, 20, 15, 0,-30},
{-30, 5, 10, 15, 15, 10, 5,-30},
{-40,-20, 0, 5, 5, 0,-20,-40},
{-50,-40,-30,-30,-30,-30,-40,-50}
};
private static final int[][] BISHOP_TABLE = {
{-20,-10,-10,-10,-10,-10,-10,-20},
{-10, 0, 0, 0, 0, 0, 0,-10},
{-10, 0, 10, 10, 10, 10, 0,-10},
{-10, 5, 5, 10, 10, 5, 5,-10},
{-10, 0, 5, 10, 10, 5, 0,-10},
{-10, 5, 5, 5, 5, 5, 5,-10},
{-10, 0, 5, 0, 0, 5, 0,-10},
{-20,-10,-10,-10,-10,-10,-10,-20}
};
private static final int[][] ROOK_TABLE = {
{0, 0, 0, 0, 0, 0, 0, 0},
{5, 10, 10, 10, 10, 10, 10, 5},
{-5, 0, 0, 0, 0, 0, 0, -5},
{-5, 0, 0, 0, 0, 0, 0, -5},
{-5, 0, 0, 0, 0, 0, 0, -5},
{-5, 0, 0, 0, 0, 0, 0, -5},
{-5, 0, 0, 0, 0, 0, 0, -5},
{0, 0, 0, 5, 5, 0, 0, 0}
};
private static final int[][] QUEEN_TABLE = {
{-20,-10,-10, -5, -5,-10,-10,-20},
{-10, 0, 0, 0, 0, 0, 0,-10},
{-10, 0, 5, 5, 5, 5, 0,-10},
{-5, 0, 5, 5, 5, 5, 0, -5},
{0, 0, 5, 5, 5, 5, 0, -5},
{-10, 5, 5, 5, 5, 5, 0,-10},
{-10, 0, 5, 0, 0, 0, 0,-10},
{-20,-10,-10, -5, -5,-10,-10,-20}
};
private static final int[][] KING_TABLE_MIDDLEGAME = {
{-30,-40,-40,-50,-50,-40,-40,-30},
{-30,-40,-40,-50,-50,-40,-40,-30},
{-30,-40,-40,-50,-50,-40,-40,-30},
{-30,-40,-40,-50,-50,-40,-40,-30},
{-20,-30,-30,-40,-40,-30,-30,-20},
{-10,-20,-20,-20,-20,-20,-20,-10},
{20, 20, 0, 0, 0, 0, 20, 20},
{20, 30, 10, 0, 0, 10, 30, 20}
};
private static final int[][] KING_TABLE_ENDGAME = {
{-50,-40,-30,-20,-20,-30,-40,-50},
{-30,-20,-10, 0, 0,-10,-20,-30},
{-30,-10, 20, 30, 30, 20,-10,-30},
{-30,-10, 30, 40, 40, 30,-10,-30},
{-30,-10, 30, 40, 40, 30,-10,-30},
{-30,-10, 20, 30, 30, 20,-10,-30},
{-30,-30, 0, 0, 0, 0,-30,-30},
{-50,-30,-30,-30,-30,-30,-30,-50}
};
public AutoPlayer() {
this(DEFAULT_DEPTH);
}
public AutoPlayer(int searchDepth) {
this.searchDepth = searchDepth;
}
/**
* Computes the best move for the current player based on the board state
* @param board The current board state
* @return The best move according to the minimax algorithm with alpha-beta pruning
*/
private Random rand = new Random();
public Move computeBestMove(Board board) {
boolean isWhite = board.isTurnWhite();
ArrayList<Move> possibleMoves = generateAllMoves(board, isWhite);
if (possibleMoves.isEmpty()) {
return null; // No moves available, likely checkmate or stalemate
}
Move bestMove = null;
int bestScore = Integer.MIN_VALUE;
for (Move move : possibleMoves) {
Board tempBoard = new Board(board); // Create a copy of the board
tempBoard.playMove(move); // Apply the move to the copy
// Negamax with alpha-beta pruning
int score = -negamax(tempBoard, searchDepth - 1, Integer.MIN_VALUE + 1, Integer.MAX_VALUE - 1, !isWhite);
if (score > bestScore) {
bestScore = score;
bestMove = move;
} else if (score == bestScore && random.nextBoolean()) {
// Add some randomness between equal-valued moves
bestMove = move;
}
}
return bestMove;
}
/**
* Implements the negamax algorithm with alpha-beta pruning
*/
private int negamax(Board board, int depth, int alpha, int beta, boolean isWhite) {
if (depth == 0 || isGameOver(board)) {
return evaluateBoard(board, isWhite);
}
ArrayList<Move> possibleMoves = generateAllMoves(board, isWhite);
if (possibleMoves.isEmpty()) {
if (board.isInCheck(isWhite)) {
return -20000; // Checkmate (worst possible outcome)
} else {
return 0; // Stalemate (neutral)
}
}
int bestScore = Integer.MIN_VALUE;
for (Move move : possibleMoves) {
Board tempBoard = new Board(board);
tempBoard.playMove(move);
int score = -negamax(tempBoard, depth - 1, -beta, -alpha, !isWhite);
bestScore = Math.max(bestScore, score);
alpha = Math.max(alpha, bestScore);
if (alpha >= beta) {
break; // Alpha-beta pruning
}
}
return bestScore;
}
/**
* Checks if the game is over (checkmate or stalemate)
*/
private boolean isGameOver(Board board) {
return board.isCheckmate() || board.isStalemate();
}
/**
* Generates all possible moves for the given player
*/
private ArrayList<Move> generateAllMoves(Board board, boolean isWhite) {
ArrayList<Move> allMoves = new ArrayList<>();
ArrayList<Piece> pieces = board.getPieces();
for (Piece piece : pieces) {
if (piece.isWhite() == isWhite) {
ArrayList<int[]> validPositions = getValidMoves(board, piece);
for (int[] position : validPositions) {
int toX = position[0];
int toY = position[1];
boolean isEnPassant = false;
boolean isCastling = false;
// Check if move is castling
if (piece.getType() == PieceType.King && Math.abs(toX - piece.getX()) == 2) {
isCastling = true;
}
// Check if move is en passant
if (piece.getType() == PieceType.Pawn && toX != piece.getX() && getPieceAt(board, toX, toY) == null) {
isEnPassant = true;
}
Piece capturedPiece = null;
if (isEnPassant) {
capturedPiece = getPieceAt(board, toX, piece.getY());
} else {
capturedPiece = getPieceAt(board, toX, toY);
}
Move move = new Move(piece.getX(), piece.getY(), toX, toY,
new Piece(piece.getX(), piece.getY(), piece.getType(), piece.isWhite()),
capturedPiece, isEnPassant, isCastling);
allMoves.add(move);
ArrayList<Move> possibleMoves = new ArrayList<>();
boolean whiteTurn = board.isTurnWhite();
for (Piece p : board.getPieces()) {
if (p.isWhite() == whiteTurn) {
ArrayList<int[]> moves = board.getValidMovesForPiece(p.getX(), p.getY());
for (int[] pos : moves) {
int toX = pos[0];
int toY = pos[1];
Piece movingPiece = new Piece(p.getX(), p.getY(), p.getType(), p.isWhite());
Piece captured = board.getPieceAt(toX, toY);
Move move = new Move(p.getX(), p.getY(), toX, toY, movingPiece, captured, false, false);
possibleMoves.add(move);
}
}
}
return allMoves;
}
/**
* Evaluates the board state from the perspective of the current player
*/
private int evaluateBoard(Board board, boolean isWhite) {
int score = 0;
boolean isEndgame = isEndGame(board);
// Material evaluation
ArrayList<Piece> pieces = board.getPieces();
for (Piece piece : pieces) {
int pieceValue = getPieceValue(piece.getType());
int positionValue = getPositionValue(piece, isEndgame);
if (piece.isWhite() == isWhite) {
score += pieceValue + positionValue;
} else {
score -= pieceValue + positionValue;
// Si aucune possibilit<EFBFBD> => null
if (possibleMoves.isEmpty()) return null;
// Cherche un coup qui capture une pi<EFBFBD>ce
for (Move m : possibleMoves) {
if (m.getCapturedPiece() != null) {
return m;
}
}
// Bonus for checks and checkmate
if (board.isInCheck(!isWhite)) {
score += 50; // Bonus for putting opponent in check
}
if (board.isCheckmate()) {
if (board.isTurnWhite() != isWhite) {
score += 10000; // Bonus for checkmate
} else {
score -= 10000; // Penalty for being checkmated
}
}
return score;
// Sinon, retourne un coup al<EFBFBD>atoire
return possibleMoves.get(rand.nextInt(possibleMoves.size()));
}
/**
* Determines if the game is in an endgame state
*/
private boolean isEndGame(Board board) {
int queens = 0;
int minors = 0;
for (Piece piece : board.getPieces()) {
if (piece.getType() == PieceType.Queen) {
queens++;
} else if (piece.getType() == PieceType.Rook ||
piece.getType() == PieceType.Bishop ||
piece.getType() == PieceType.Knight) {
minors++;
}
}
// Consider it endgame if no queens or few minor pieces
return queens == 0 || (queens == 1 && minors <= 2);
}
/**
* Returns the basic 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;
}
}
/**
* Returns the positional value of a piece based on its location
*/
private int getPositionValue(Piece piece, boolean isEndgame) {
int x = piece.getX();
int y = piece.getY();
boolean isWhite = piece.isWhite();
// Flip coordinates for black pieces to use the same tables
if (!isWhite) {
y = 7 - y;
}
switch (piece.getType()) {
case Pawn: return PAWN_TABLE[y][x];
case Knight: return KNIGHT_TABLE[y][x];
case Bishop: return BISHOP_TABLE[y][x];
case Rook: return ROOK_TABLE[y][x];
case Queen: return QUEEN_TABLE[y][x];
case King:
if (isEndgame) {
return KING_TABLE_ENDGAME[y][x];
} else {
return KING_TABLE_MIDDLEGAME[y][x];
}
default: return 0;
}
}
/**
* Gets valid moves for a piece (had to reimplement as board methods are private)
*/
private ArrayList<int[]> getValidMoves(Board board, Piece piece) {
// This method would need to mimic board.getValidMoves logic
// Since we don't have direct access to Board's private methods,
// we'll simulate by analyzing the board setup
// For this implementation, I'll use a simplified approach
// that leverages board's existing functionality by simulating user touches
ArrayList<int[]> validMoves = new ArrayList<>();
int x = piece.getX();
int y = piece.getY();
// Make a copy of the board to avoid side effects
Board tempBoard = new Board(board);
// Select the piece
tempBoard.userTouch(x, y);
// Check which positions are highlighted (valid moves)
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (tempBoard.isHighlighted(i, j)) {
validMoves.add(new int[]{i, j});
}
}
}
return validMoves;
}
/**
* Helper method to get a piece at a specific position
*/
private Piece getPieceAt(Board board, int x, int y) {
for (Piece piece : board.getPieces()) {
if (piece.getX() == x && piece.getY() == y) {
return piece;
}
}
return null;
}
}
}

View File

@ -23,7 +23,7 @@ public class Board {
private boolean blackRookQueensideMoved = false;
private ArrayList<Move> moveHistory = new ArrayList<>();
private Piece getPieceAt(int x, int y) {
public Piece getPieceAt(int x, int y) {
for (Piece piece : pieces) {
if (piece.getX() == x && piece.getY() == y) {
return piece;