Final Part (utilisateur is Louis Flichy)
This commit is contained in:
parent
9e86e6ee48
commit
0a3f54876a
|
|
@ -2,9 +2,9 @@ BR,BN,BB,BQ,BK,BB,BN,BR
|
||||||
BP,BP,BP,BP,BP,BP,BP,BP
|
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
|
WR,WN,WB,WQ,WK,WB,WN,WR
|
||||||
W
|
B
|
||||||
000000,-1,-1
|
000000,7,5
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import java.io.FileNotFoundException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
import backend.AutoPlayer;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
@ -16,7 +17,16 @@ public class Main {
|
||||||
Board testBoard = new Board(8, 8);
|
Board testBoard = new Board(8, 8);
|
||||||
testBoard.populateBoard();
|
testBoard.populateBoard();
|
||||||
System.out.println(testBoard.toString());
|
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
|
// Test saving board to file
|
||||||
testSavingBoard(testBoard, "default.board");
|
testSavingBoard(testBoard, "default.board");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,373 +4,37 @@ import java.util.ArrayList;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class AutoPlayer {
|
public class AutoPlayer {
|
||||||
private static final int DEFAULT_DEPTH = 3;
|
private Random rand = new Random();
|
||||||
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
|
|
||||||
*/
|
|
||||||
public Move computeBestMove(Board board) {
|
public Move computeBestMove(Board board) {
|
||||||
boolean isWhite = board.isTurnWhite();
|
ArrayList<Move> possibleMoves = new ArrayList<>();
|
||||||
ArrayList<Move> possibleMoves = generateAllMoves(board, isWhite);
|
boolean whiteTurn = board.isTurnWhite();
|
||||||
|
|
||||||
if (possibleMoves.isEmpty()) {
|
for (Piece p : board.getPieces()) {
|
||||||
return null; // No moves available, likely checkmate or stalemate
|
if (p.isWhite() == whiteTurn) {
|
||||||
}
|
ArrayList<int[]> moves = board.getValidMovesForPiece(p.getX(), p.getY());
|
||||||
|
for (int[] pos : moves) {
|
||||||
Move bestMove = null;
|
int toX = pos[0];
|
||||||
int bestScore = Integer.MIN_VALUE;
|
int toY = pos[1];
|
||||||
|
Piece movingPiece = new Piece(p.getX(), p.getY(), p.getType(), p.isWhite());
|
||||||
for (Move move : possibleMoves) {
|
Piece captured = board.getPieceAt(toX, toY);
|
||||||
Board tempBoard = new Board(board); // Create a copy of the board
|
Move move = new Move(p.getX(), p.getY(), toX, toY, movingPiece, captured, false, false);
|
||||||
tempBoard.playMove(move); // Apply the move to the copy
|
possibleMoves.add(move);
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return allMoves;
|
// Si aucune possibilit<EFBFBD> => null
|
||||||
}
|
if (possibleMoves.isEmpty()) return null;
|
||||||
|
|
||||||
/**
|
// Cherche un coup qui capture une pi<EFBFBD>ce
|
||||||
* Evaluates the board state from the perspective of the current player
|
for (Move m : possibleMoves) {
|
||||||
*/
|
if (m.getCapturedPiece() != null) {
|
||||||
private int evaluateBoard(Board board, boolean isWhite) {
|
return m;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bonus for checks and checkmate
|
// Sinon, retourne un coup al<EFBFBD>atoire
|
||||||
if (board.isInCheck(!isWhite)) {
|
return possibleMoves.get(rand.nextInt(possibleMoves.size()));
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -23,7 +23,7 @@ public class Board {
|
||||||
private boolean blackRookQueensideMoved = false;
|
private boolean blackRookQueensideMoved = false;
|
||||||
private ArrayList<Move> moveHistory = new ArrayList<>();
|
private ArrayList<Move> moveHistory = new ArrayList<>();
|
||||||
|
|
||||||
private Piece getPieceAt(int x, int y) {
|
public Piece getPieceAt(int x, int y) {
|
||||||
for (Piece piece : pieces) {
|
for (Piece piece : pieces) {
|
||||||
if (piece.getX() == x && piece.getY() == y) {
|
if (piece.getX() == x && piece.getY() == y) {
|
||||||
return piece;
|
return piece;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue