From 84605fdf6071554b613e11fae4df8cbb4b9c381e Mon Sep 17 00:00:00 2001 From: Romain Murphy Date: Wed, 23 Apr 2025 16:48:35 +0200 Subject: [PATCH] IAAAAAAAAAAAA --- OOP_2B1_Project/src/backend/AutoPlayer.java | 240 ++++++++++++++++++-- OOP_2B1_Project/src/backend/Board.java | 40 +++- OOP_2B1_Project/src/backend/Game.java | 2 +- OOP_2B1_Project/src/backend/Move.java | 42 ++-- 4 files changed, 290 insertions(+), 34 deletions(-) diff --git a/OOP_2B1_Project/src/backend/AutoPlayer.java b/OOP_2B1_Project/src/backend/AutoPlayer.java index 1b56073..4510338 100644 --- a/OOP_2B1_Project/src/backend/AutoPlayer.java +++ b/OOP_2B1_Project/src/backend/AutoPlayer.java @@ -1,19 +1,227 @@ package backend; +import java.util.ArrayList; + + public class AutoPlayer { - - - /** - * returns the best Move to try on provided board for active player - * @param board - * @return - */ - public Move computeBestMove(Board board) { - - - - return null; - } - - -} + + private int MAX_DEPTH = 3; + private int[][] PAWN_TABLE = { + { 0, 0, 0, 0, 0, 0, 0, 0 }, + { 5, 5, 5, -5, -5, 5, 5, 5 }, + { 1, 1, 2, 3, 3, 2, 1, 1 }, + { 0, 0, 0, 2, 2, 0, 0, 0 }, + { 1, 1, 1, 0, 0, 1, 1, 1 }, + { 2, 2, 2, -2, -2, 2, 2, 2 }, + { 5, 5, 5, 5, 5, 5, 5, 5 }, + { 0, 0, 0, 0, 0, 0, 0, 0 } + }; + private 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 int[][] BISHOP_TABLE = { + {-20,-10,-10,-10,-10,-10,-10,-20}, + {-10, 5, 0, 0, 0, 0, 5,-10}, + {-10, 10, 10, 10, 10, 10, 10,-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, 0, 0, 0, 0, 0, 0,-10}, + {-20,-10,-10,-10,-10,-10,-10,-20} + }; + private int[][] ROOK_TABLE = { + { 0, 0, 0, 5, 5, 0, 0, 0 }, + { -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 }, + { 5, 10, 10, 10, 10, 10, 10, 5 }, + { 0, 0, 0, 0, 0, 0, 0, 0 } + }; + private 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 int[][] KING_TABLE = { + {-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} + }; + public Move computeBestMove(ArrayList> board, boolean isWhiteTurn) { + Move bestMove = null; + int bestScore = isWhiteTurn ? Integer.MIN_VALUE : Integer.MAX_VALUE; + + ArrayList allMoves = getAllLegalMoves(board, isWhiteTurn); + + for (Move move : allMoves) { + ArrayList> simulatedBoard = simulateMove(board, move); + int score = minimax(simulatedBoard, MAX_DEPTH - 1, !isWhiteTurn); + + if (isWhiteTurn && score > bestScore) { + bestScore = score; + bestMove = move; + } else if (!isWhiteTurn && score < bestScore) { + bestScore = score; + bestMove = move; + } + } + + return bestMove; + } + + private int minimax(ArrayList> board, int depth, boolean isWhiteTurn) { + if (depth == 0) { + return evaluateBoard(board); + } + + ArrayList allMoves = getAllLegalMoves(board, isWhiteTurn); + if (allMoves.isEmpty()) return evaluateBoard(board); // checkmate or stalemate + + int bestScore = isWhiteTurn ? Integer.MIN_VALUE : Integer.MAX_VALUE; + + for (Move move : allMoves) { + ArrayList> simulatedBoard = simulateMove(board, move); + int score = minimax(simulatedBoard, depth - 1, !isWhiteTurn); + + if (isWhiteTurn) { + bestScore = Math.max(bestScore, score); + } else { + bestScore = Math.min(bestScore, score); + } + } + + return bestScore; + } + + private int evaluateBoard(ArrayList> board) { + int score = 0; + + for (int y = 0; y < 8; y++) { + for (int x = 0; x < 8; x++) { + Piece p = board.get(y).get(x); + if (p != null) { + int value = getPieceValue(p.getType()); + int positionalBonus = getPositionalBonus(p, x, y); + + if (p.isWhite()) { + score += value + positionalBonus; + } else { + score -= value + positionalBonus; // subtract opponent's bonus too + } + } + } + } + + return score; + } + + private int getPieceValue(PieceType type) { + switch (type) { + case Pawn: + return 10; + case Knight: + return 30; + case Bishop: + return 30; + case Rook: + return 50; + case Queen: + return 90; + case King: + return 900; + default: + return 0; + } + } + + private ArrayList> simulateMove(ArrayList> board, Move move) { + ArrayList> newBoard = deepCopyBoard(board); + + Piece piece = newBoard.get(move.fromY).get(move.fromX); + newBoard.get(move.fromY).set(move.fromX, null); + piece.x = move.toX; + piece.y = move.toY; + newBoard.get(move.toY).set(move.toX, piece); + + return newBoard; + } + + private ArrayList> deepCopyBoard(ArrayList> original) { + ArrayList> copy = new ArrayList<>(); + for (ArrayList row : original) { + ArrayList newRow = new ArrayList<>(); + for (Piece p : row) { + if (p != null) { + newRow.add(PieceCreation.createPiece(p.getX(), p.getY(), p.getType(), p.isWhite())); + } else { + newRow.add(null); + } + } + copy.add(newRow); + } + return copy; + } + + private ArrayList getAllLegalMoves(ArrayList> board, boolean isWhiteTurn) { + ArrayList legalMoves = new ArrayList<>(); + + for (ArrayList row : board) { + for (Piece p : row) { + if (p != null && p.isWhite() == isWhiteTurn) { + ArrayList> moves = p.getPossibleMoves(board); + for (int y = 0; y < 8; y++) { + for (int x = 0; x < 8; x++) { + if (moves.get(y).get(x)) { + legalMoves.add(new Move(p.getX(), p.getY(), x, y)); + } + } + } + } + } + } + + return legalMoves; + } + + private int getPositionalBonus(Piece piece, int x, int y) { + if (!piece.isWhite()) { + y = 7 - y; // mirror for black + } + + 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: + return KING_TABLE[y][x]; + default: + return 0; + } + } +} \ No newline at end of file diff --git a/OOP_2B1_Project/src/backend/Board.java b/OOP_2B1_Project/src/backend/Board.java index 994c9c6..65b3c59 100644 --- a/OOP_2B1_Project/src/backend/Board.java +++ b/OOP_2B1_Project/src/backend/Board.java @@ -240,8 +240,46 @@ public class Board { } public void playMove(Move move) { - //TODO + int xm = move.getFromX(); + int ym = move.getFromY(); + int x = move.getToX(); + int y = move.getToY(); + boardHistory.add(new BoardHistory(board,turnNumber,turnColor)); + Piece pieceToMove = this.board.get(ym).get(xm); + boolean isCastlingMove = pieceToMove.getType() == PieceType.King && Math.abs(x - xm) == 2; + //check for castling mode: + if (isCastlingMove) { + + //kingside + if (x > xm) { + Piece rook = board.get(ym).get(7); + board.get(ym).set(5, rook); + board.get(ym).set(7, null); + if (rook != null) { + rook.x = 5; + rook.setMoved(true); + } + } + if (x < xm) { + Piece rook = board.get(ym).get(0); + board.get(ym).set(3, rook); + board.get(ym).set(0, null); + if (rook != null) { + rook.x = 3; + rook.setMoved(true); + } + } + } + + this.setPiece(x,y,pieceToMove.getType(),pieceToMove.isWhite()); + Piece movedPiece = this.getPiece(x, y); + if (movedPiece != null) { + movedPiece.setMoved(true); + } + board.get(ym).set(xm,null); + this.turnColor = !this.turnColor; + this.turnNumber +=1; } public void setBoard(ArrayList> board) { diff --git a/OOP_2B1_Project/src/backend/Game.java b/OOP_2B1_Project/src/backend/Game.java index e241263..432ee0a 100644 --- a/OOP_2B1_Project/src/backend/Game.java +++ b/OOP_2B1_Project/src/backend/Game.java @@ -49,7 +49,7 @@ public class Game extends Thread { private void aiPlayerTurn() { if(isAITurn()) { - board.playMove(aiPlayer.computeBestMove(new Board(board))); + board.playMove(aiPlayer.computeBestMove(board.getBoard(),board.isTurnWhite())); } } diff --git a/OOP_2B1_Project/src/backend/Move.java b/OOP_2B1_Project/src/backend/Move.java index 9252396..708627c 100644 --- a/OOP_2B1_Project/src/backend/Move.java +++ b/OOP_2B1_Project/src/backend/Move.java @@ -1,24 +1,34 @@ package backend; -import java.util.ArrayList; public class Move { - private int x; - private int y; - - - public Move(int x, int y) { - this.x=x; - this.y=y; + public int fromX, fromY, toX, toY; + public Move(int fromX, int fromY, int toX, int toY) { + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + + public int getFromX() { + return fromX; } - - - - - - - + public int getFromY() { + return fromY; + } -} + public int getToX() { + return toX; + } + + public int getToY() { + return toY; + } + + @Override + public String toString() { + return "(" + fromX + "," + fromY + ") → (" + toX + "," + toY + ")"; + } +} \ No newline at end of file