From 5f02aeed3efe3897b8166017adc9914167ef7269 Mon Sep 17 00:00:00 2001 From: cleme Date: Fri, 23 May 2025 21:03:25 +0200 Subject: [PATCH] ai trial --- OOP_1A2_Project/bin/.gitignore | 1 - OOP_1A2_Project/bin/backend/AutoPlayer.class | Bin 417 -> 4554 bytes OOP_1A2_Project/bin/backend/Board.class | Bin 12153 -> 10704 bytes OOP_1A2_Project/bin/backend/Game.class | Bin 3684 -> 3686 bytes OOP_1A2_Project/bin/backend/Move.class | Bin 10871 -> 8939 bytes OOP_1A2_Project/bin/backend/Piece.class | Bin 908 -> 1078 bytes OOP_1A2_Project/src/backend/AutoPlayer.java | 108 ++++++++- OOP_1A2_Project/src/backend/Board.java | 108 +-------- OOP_1A2_Project/src/backend/Game.java | 5 +- OOP_1A2_Project/src/backend/Move.java | 236 ++++++------------- OOP_1A2_Project/src/backend/Piece.java | 7 + 11 files changed, 190 insertions(+), 275 deletions(-) diff --git a/OOP_1A2_Project/bin/.gitignore b/OOP_1A2_Project/bin/.gitignore index 5c7ebe3..6d0c385 100644 --- a/OOP_1A2_Project/bin/.gitignore +++ b/OOP_1A2_Project/bin/.gitignore @@ -1,2 +1 @@ /backend/ -/windowInterface/ diff --git a/OOP_1A2_Project/bin/backend/AutoPlayer.class b/OOP_1A2_Project/bin/backend/AutoPlayer.class index fe9ba973672ad83cc2efd5a786dc2c1d6ee9b5f6..ecb0d55228ede6e3bc0fa649e1bef94e87b3876f 100644 GIT binary patch literal 4554 zcmbtXZFm&b6@Di>yR*AYNQeYjAf;G@BtRk<1113i;eCgHI^n&;V>d(S=h zoO{muo^vPf|L3i%09x@E10@ND?soyzx z&>r$Mgt}Vy?7y$0yLX!gEvlik-E~r)<#>HoJZ<}El=fDv>gkX6wr|_t+uGLIu_|kQ zRd>uDvU`svYz-y*xHqsR=ES@f4ZiA{J`H-iJ8XyHLz#(rFg28Q#vFS`dTh{6_F98+ zZmZ~Yhpc#?m5j+eE7ZNwSW3gf&bd)E)5wrJmPmVco1OBy+`}}oqB^hH=32?&=9>Im z6q>`(u-L>BM3{jQ+iQ)-JM9rGE{pWDa_)AOrnDD}*LTKJUh_OGLzRK$8s^VjG4Tnk z&|t(;9b*aas0w_qft4B-6fzxkJbT1W`krJi#nLJhw_&x0V9c|VmggolEUK>QELe_a z<@8&PS_3uA)7(`PYq5?=cI}C!PLL=2l?uG|`0} zM93lFW7fDNYL=(y-kPW+y4%Dq1?H%g+M#ml9~3qTZD%Bm91O>c_)}g4ouUhiYLMjY z8FG_kXHdqOv(L{PQb#9vV~W5b&mK#txVbeI8%xJ6&!%I4Dq~{N%qn5QWh>>Avs`bP z>7a#2E$#8x3%aa?+9ElzOblX(2eS{e#OX=HEwhutoP=Rx#K5O>q*ui1vb<3fqll3K zDxH3USHsfx58DZERF#5CKAXv2LC`VbBEhgo1=TKCaEo(XRTb8nOj<`(-oi*?l>utp zQlNW&*+9C8y2_SBbHqdl#8t1Z*~c?`c+A9S@L8Tj$ftT!CH>_P%(pCdg=2Wqz!R+1 zIg2Kq60#a@ayaH#aRZ;zu%uAY%)-!U$M_IKj=4@4$1x#X{=A0T>I@<(Sv_7d=8QBK zNqR?;>${HJMBq~UomkAD`fP%iLc2~ zbwYEry6r#;IE3Fr0@&!#JzmjK5WO`#F*`PZLX7M>ca_X>RIlFdSWAv z<)wwk8)kE)xMc}`bI2!nmPzrSHt`}}B6>U|ua(%a(4W3;FPO}nEFXWi3CyB58`{en zDsym&l~vttDkdm|a0cg0`~U$4Jub5!n)s2L9hEcu*u<;ZUagKGvY(jvsmLmLpQLsk zaU{J_r+w5OIwXzybrTox2AOURvluIjq0QW$%{6ir z^eYp;R-)}rY-H3E(R(I-DcJdjSjpJb7Y3HXj{s+e zd~YCF{(uC`vB>!8z-K9E^?Yl5`)e;^{_9G$JgX$C3pf^4Dxd`cd@?7e+BgeYcFC$lW_&> z2O|C{G{|GKJeuXv5-~2KwJ8`eBEc!#R~s>OI@op+hHnzOK85XGW2iDzbr#{uP)o$$ zq>K5T8ct20isRH?`xV#*j@HgXy5Mh{~T;2HGdL8f&f_w{k^qlF{1w}+nVs58KkpFX14%bCHI zTI{1NNMEIRh&w`fn6@Ri8EcQw+I~t-!2d5SFtBWzAs7ZU1NEHowVrmWfoWdLp&4>6 zaYgB^hcC0FP1DxLsOecX>Q&s-4UfZXJ`42eEYK6nPa#~pwrUCoUah#bQCGOUa9i77Cb+1qrTlkYI5Ae5|zdHVEv%igeZ_+2Rf_1zsIElsO zp-EJf4^N`>3XTtG+C@D5PGi8=XegT(aN-;bTFhU-v*%DF!{<(-RNh}L54?nhN^%Ni zN}``d;AumX@z?z?VWk=bU&a!(N=ZhI5>cxRR2l~wgH^Kl-0~L@tmKITl|k)4Kea{ospflz2S%b4lSQ|LyXLN^p~O6ct)MF<~#{te&4w+V^E*76-DI6yzg z*d+sG#dS82Qyc|V=|6$8X?CeVt~7JBxw{}T;3+Qod_G-|YX8PMO)D>1Jhd2tK3`yXCHSuD@fUas>G6|SkQ}&(?{!HhE+*DQF|iaZ zRWBkTot=mDvCp?gq5cn115#vpqmB*y)Z3;y~Q+VYh zNyGPRr(mv&gf8QJ3Hrr^>iJW7D|8K8$Tha#24>_MYps>7^BOu>MZ5Uk&D=l2cczug zS4r-JLt3z8Wj#u_8TeqDK#@_KQd=%;zMZv1rS=n Ang9R* delta 236 zcmX@5ypWme)W2Q(7#J8F8AK*>8OX8OWaed-*fBCNYiNcsGO#%3r=)T*Ff(xSFt9SP zO6&BV9?L^2?B c@`81WL1Y$Yi@(x$W}iEB{*bKe`yXla_S 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 V!GE10H`9tA=c>c<30|g@{ttumKxzO0 literal 12153 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 AMgRZ+ delta 1201 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 diff --git a/OOP_1A2_Project/bin/backend/Piece.class b/OOP_1A2_Project/bin/backend/Piece.class index 2b1301e0e0ea0f47095c9ddc7c119faa4c0eb0cf..b5ca328254994132135ef20c1f608a3171347d38 100644 GIT binary patch delta 189 zcmXAiO%B0e6h+V1e|pQ1k^lV_?gU^@D*ff$?3=9m83>q7`f*B`QGCDJ|ZQjfHgppkhD9ps54gl{=3KswX diff --git a/OOP_1A2_Project/src/backend/AutoPlayer.java b/OOP_1A2_Project/src/backend/AutoPlayer.java index a988a22..3beca93 100644 --- a/OOP_1A2_Project/src/backend/AutoPlayer.java +++ b/OOP_1A2_Project/src/backend/AutoPlayer.java @@ -1,17 +1,99 @@ package backend; +import java.util.ArrayList; +import java.util.List; + 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 static final int MAX_DEPTH = 2; + + public Move computeBestMove(Board board) { + List legalMoves = getAllLegalMoves(board); + if (legalMoves.isEmpty()) return null; + + int bestScore = Integer.MIN_VALUE; + Move bestMove = null; + + for (Move move : legalMoves) { + Board tempBoard = new Board(board); + Move simulated = new Move(move.getFromX(), move.getFromY(), move.getToX(), move.getToY(), tempBoard); + tempBoard.playMove(simulated); + + int score = -negamax(tempBoard, MAX_DEPTH - 1, !board.isTurnWhite()); + if (score > bestScore) { + bestScore = score; + bestMove = move; + } + } + + return bestMove; + } + + private int negamax(Board board, int depth, boolean isWhite) { + if (depth == 0) { + return evaluate(board, isWhite); + } + + int max = Integer.MIN_VALUE; + for (Move move : getAllLegalMoves(board)) { + Board tempBoard = new Board(board); + Move simulated = new Move(move.getFromX(), move.getFromY(), move.getToX(), move.getToY(), tempBoard); + tempBoard.playMove(simulated); + int score = -negamax(tempBoard, depth - 1, !isWhite); + max = Math.max(max, score); + } + return max; + } + + private int evaluate(Board board, boolean isWhite) { + int score = 0; + + for (Piece p : board.getPieces()) { + int value = switch (p.getType()) { + case Pawn -> 1; + case Knight, Bishop -> 3; + case Rook -> 5; + case Queen -> 9; + case King -> 100; + }; + + int developmentBonus = 0; + + // Encourage developing minor pieces + if (p.getType() != PieceType.Pawn && p.getType() != PieceType.King) { + if ((p.isWhite() && p.getY() < 6) || (!p.isWhite() && p.getY() > 1)) { + developmentBonus += 1; + } + } + + // Encourage center control + if (p.getX() >= 2 && p.getX() <= 5 && p.getY() >= 2 && p.getY() <= 5) { + developmentBonus += 1; + } + + int pieceScore = value + developmentBonus; + score += (p.isWhite() == isWhite ? pieceScore : -pieceScore); + } + + return score; + } + + private List getAllLegalMoves(Board board) { + List legalMoves = new ArrayList<>(); + boolean isWhite = board.isTurnWhite(); + + for (Piece piece : board.getPieces()) { + if (piece.isWhite() == isWhite) { + List validMoves = new Move(0, 0, 0, 0, board).getValidMoves(piece, board); + for (Move.Position pos : validMoves) { + Move move = new Move(piece.getX(), piece.getY(), pos.x, pos.y, board); + if (move.isValid() && !move.putsOwnKingInCheck()) { + legalMoves.add(move); + } + } + } + } + + return legalMoves; + } } diff --git a/OOP_1A2_Project/src/backend/Board.java b/OOP_1A2_Project/src/backend/Board.java index 328735f..c64fdf3 100644 --- a/OOP_1A2_Project/src/backend/Board.java +++ b/OOP_1A2_Project/src/backend/Board.java @@ -20,11 +20,6 @@ public class Board { // Add Sound instance private Sound sound; - // En passant tracking - private Integer enPassantX = null; // File where en passant capture is possible - private Integer enPassantY = null; // Rank where the pawn that can be captured is located - private int enPassantTurn = -1; // Turn number when en passant became possible - public Board(int colNum, int lineNum) { this.width = colNum; this.height = lineNum; @@ -47,6 +42,15 @@ public class Board { public int getTurnNumber() { return turnNumber; } + public void playMove(Move move) { + if (move != null && move.isValid() && !move.putsOwnKingInCheck()) { + move.execute(); + System.out.println("AI plays: " + move.getFromX() + "," + move.getFromY() + " -> " + move.getToX() + "," + move.getToY()); + } else { + System.out.println("Invalid AI move."); + } + } + public boolean isTurnWhite() { return isWhiteTurn; @@ -118,7 +122,6 @@ public class Board { public void cleanBoard() { pieces.clear(); - clearEnPassant(); } public String toString() { @@ -234,13 +237,7 @@ public class Board { fileRep[y] = line; } - // Include en passant information in the file representation - String enPassantInfo = ""; - if (enPassantX != null && enPassantY != null) { - enPassantInfo = "," + enPassantX + "," + enPassantY + "," + enPassantTurn; - } - - fileRep[height] = (isWhiteTurn ? "W" : "B") + "," + turnNumber + enPassantInfo; + fileRep[height] = (isWhiteTurn ? "W" : "B") + "," + turnNumber; return fileRep; } @@ -294,23 +291,9 @@ public class Board { } else { this.turnNumber = 0; } - - // Parse en passant information - if (turnData.length >= 5) { - try { - this.enPassantX = Integer.parseInt(turnData[2].trim()); - this.enPassantY = Integer.parseInt(turnData[3].trim()); - this.enPassantTurn = Integer.parseInt(turnData[4].trim()); - } catch (NumberFormatException e) { - clearEnPassant(); - } - } else { - clearEnPassant(); - } } else { this.turnNumber = 0; this.isWhiteTurn = true; - clearEnPassant(); } } @@ -343,11 +326,6 @@ public class Board { this.highlightedPositions = new ArrayList<>(); this.highlightedPositions.addAll(board.highlightedPositions); - // Copy en passant state - this.enPassantX = board.enPassantX; - this.enPassantY = board.enPassantY; - this.enPassantTurn = board.enPassantTurn; - // Initialize board history for copy constructor this.boardHistory = new ArrayList<>(); @@ -378,17 +356,6 @@ public class Board { highlightedPositions.addAll(validMoves); } - public void playMove(Move move) { - if (move.isValid() && !move.putsOwnKingInCheck()) { - // SAVE STATE BEFORE MOVE - saveCurrentState(); - - move.execute(); - - // Play the move sound using instance method - sound.playMoveSound(); - } - } // UNDO METHODS USING ARRAYLIST private void saveCurrentState() { @@ -403,15 +370,12 @@ public class Board { // Remove it from history boardHistory.remove(boardHistory.size() - 1); + // Use existing methods to restore state this.width = previousBoard.width; this.height = previousBoard.height; this.turnNumber = previousBoard.turnNumber; this.isWhiteTurn = previousBoard.isWhiteTurn; - this.enPassantX = previousBoard.enPassantX; - this.enPassantY = previousBoard.enPassantY; - this.enPassantTurn = previousBoard.enPassantTurn; - // Use existing getPieces() method to restore pieces this.pieces.clear(); for (Piece p : previousBoard.getPieces()) { @@ -429,58 +393,10 @@ public class Board { return !boardHistory.isEmpty(); } + // Add cleanup method to properly dispose of sound resources public void cleanup() { if (sound != null) { sound.cleanup(); } } - - public void setEnPassant(int x, int y) { - this.enPassantX = x; - this.enPassantY = y; - this.enPassantTurn = this.turnNumber; - } - - - public void clearEnPassant() { - this.enPassantX = null; - this.enPassantY = null; - this.enPassantTurn = -1; - } - - public boolean canCaptureEnPassant(int x, int y) { - // En passant is only valid for one turn after the double pawn move - if (enPassantX == null || enPassantY == null) { - return false; - } - - // Check if this is the immediate next turn - if (turnNumber != enPassantTurn + 1) { - return false; - } - - // Check if the position matches - return enPassantX == x && enPassantY == y; - } - - public void handlePawnDoubleMove(int fromX, int fromY, int toX, int toY) { - Piece pawn = getPieceAt(toX, toY); - if (pawn != null && pawn.getType() == PieceType.Pawn) { - if (Math.abs(toY - fromY) == 2) { - setEnPassant(toX, toY); - } else { - clearEnPassant(); - } - } else { - clearEnPassant(); - } - } - - - public Enpassant.Position getEnPassantTarget() { - if (enPassantX != null && enPassantY != null && canCaptureEnPassant(enPassantX, enPassantY)) { - return new Enpassant.Position(enPassantX, enPassantY); - } - return null; - } } \ No newline at end of file diff --git a/OOP_1A2_Project/src/backend/Game.java b/OOP_1A2_Project/src/backend/Game.java index 4c64f70..ad38d0e 100644 --- a/OOP_1A2_Project/src/backend/Game.java +++ b/OOP_1A2_Project/src/backend/Game.java @@ -49,7 +49,9 @@ public class Game extends Thread { private void aiPlayerTurn() { if(isAITurn()) { - board.playMove(aiPlayer.computeBestMove(new Board(board))); + Move move = aiPlayer.computeBestMove(board); // Use the real board + board.playMove(move); + } } @@ -106,5 +108,6 @@ public class Game extends Thread { public void toggleAI(boolean isWhite) { this.activationAIFlags[isWhite?1:0] = !this.activationAIFlags[isWhite?1:0]; } + } diff --git a/OOP_1A2_Project/src/backend/Move.java b/OOP_1A2_Project/src/backend/Move.java index ada3231..7af41bb 100644 --- a/OOP_1A2_Project/src/backend/Move.java +++ b/OOP_1A2_Project/src/backend/Move.java @@ -12,14 +12,6 @@ public class Move { private Piece capturedPiece; private Board board; - // Castling-related fields - private CastlingHandler castlingHandler; - private CastlingHandler.CastlingMove castlingMove; - private boolean isCastling; - - // En passant-related fields - private Enpassant enpassantHandler; - private boolean isEnPassant; public Move(int fromX, int fromY, int toX, int toY, Board board) { this.fromX = fromX; @@ -29,15 +21,6 @@ public class Move { this.board = board; this.movingPiece = board.getPieceAt(fromX, fromY); this.capturedPiece = board.getPieceAt(toX, toY); - - // Initialize castling handler and check if this is a castling move - this.castlingHandler = new CastlingHandler(board); - this.castlingMove = castlingHandler.getCastlingMove(fromX, fromY, toX, toY); - this.isCastling = (castlingMove != null); - - // Initialize en passant handler and check if this is an en passant move - this.enpassantHandler = new Enpassant(board); - this.isEnPassant = enpassantHandler.isEnPassantValid(fromX, fromY, toX, toY); } public boolean isValid() { @@ -46,46 +29,20 @@ public class Move { return false; } - // Handle castling validation - if (isCastling) { - return castlingHandler.isCastlingValid(castlingMove); - } - - // Handle en passant validation - if (isEnPassant) { - return enpassantHandler.isEnPassantValid(fromX, fromY, toX, toY); - } - // Check if the move is in the list of valid moves for this piece List validMoves = getValidDestinations(movingPiece, board); - return containsPosition(validMoves, new Position(toX, toY)); + return validMoves.contains(new Position(toX, toY)); } public void execute() { - if (isCastling) { - // Execute castling move - castlingHandler.executeCastling(castlingMove); - } else if (isEnPassant) { - // Execute en passant capture - enpassantHandler.executeEnPassant(fromX, fromY, toX, toY); - } else { - // Remove any piece at the destination (normal capture) - if (capturedPiece != null) { - board.removePiece(toX, toY); - } - - // Move the piece (remove from original position, add to new position) - board.removePiece(fromX, fromY); - board.setPiece(movingPiece.isWhite(), movingPiece.getType(), toX, toY); + // Remove any piece at the destination (normal capture) + if (capturedPiece != null) { + board.removePiece(toX, toY); } - // Handle en passant state updates for pawn moves - if (movingPiece.getType() == PieceType.Pawn && !isEnPassant) { - board.handlePawnDoubleMove(fromX, fromY, toX, toY); - } else { - // Clear en passant for non-pawn moves - board.clearEnPassant(); - } + // Move the piece (remove from original position, add to new position) + board.removePiece(fromX, fromY); + board.setPiece(movingPiece.isWhite(), movingPiece.getType(), toX, toY); // Advance turn board.advanceTurn(); @@ -99,29 +56,15 @@ public class Move { Piece tempPiece = tempBoard.getPieceAt(fromX, fromY); if (tempPiece == null) return true; // Safety check - if (isCastling) { - // For castling, we need to check the castling move specifically - CastlingHandler tempHandler = new CastlingHandler(tempBoard); - CastlingHandler.CastlingMove tempCastling = tempHandler.getCastlingMove(fromX, fromY, toX, toY); - if (tempCastling != null) { - tempHandler.executeCastling(tempCastling); - } - } else if (isEnPassant) { - // For en passant, execute the en passant capture - Enpassant tempEnpassant = new Enpassant(tempBoard); - tempEnpassant.executeEnPassant(fromX, fromY, toX, toY); - } else { - // Remove any piece at destination (normal capture) - tempBoard.removePiece(toX, toY); - - // Move the piece - tempBoard.removePiece(fromX, fromY); - tempBoard.setPiece(tempPiece.isWhite(), tempPiece.getType(), toX, toY); - } + // Remove any piece at destination (normal capture) + tempBoard.removePiece(toX, toY); + + // Move the piece + tempBoard.removePiece(fromX, fromY); + tempBoard.setPiece(tempPiece.isWhite(), tempPiece.getType(), toX, toY); // Check if king is in check after move - CastlingHandler tempHandler = new CastlingHandler(tempBoard); - return tempHandler.isKingInCheck(tempPiece.isWhite()); + return isKingInCheck(tempBoard, tempPiece.isWhite()); } public boolean putsOpponentInCheck() { @@ -132,29 +75,15 @@ public class Move { Piece tempPiece = tempBoard.getPieceAt(fromX, fromY); if (tempPiece == null) return false; // Safety check - if (isCastling) { - // For castling, execute the castling move - CastlingHandler tempHandler = new CastlingHandler(tempBoard); - CastlingHandler.CastlingMove tempCastling = tempHandler.getCastlingMove(fromX, fromY, toX, toY); - if (tempCastling != null) { - tempHandler.executeCastling(tempCastling); - } - } else if (isEnPassant) { - // For en passant, execute the en passant capture - Enpassant tempEnpassant = new Enpassant(tempBoard); - tempEnpassant.executeEnPassant(fromX, fromY, toX, toY); - } else { - // Remove any piece at destination (normal capture) - tempBoard.removePiece(toX, toY); - - // Move the piece - tempBoard.removePiece(fromX, fromY); - tempBoard.setPiece(tempPiece.isWhite(), tempPiece.getType(), toX, toY); - } + // Remove any piece at destination (normal capture) + tempBoard.removePiece(toX, toY); + + // Move the piece + tempBoard.removePiece(fromX, fromY); + tempBoard.setPiece(tempPiece.isWhite(), tempPiece.getType(), toX, toY); // Check if opponent's king is in check after move - CastlingHandler tempHandler = new CastlingHandler(tempBoard); - return tempHandler.isKingInCheck(!tempPiece.isWhite()); + return isKingInCheck(tempBoard, !tempPiece.isWhite()); } public boolean putsOpponentInCheckmate() { @@ -170,25 +99,12 @@ public class Move { Piece tempPiece = tempBoard.getPieceAt(fromX, fromY); if (tempPiece == null) return false; // Safety check - if (isCastling) { - // For castling, execute the castling move - CastlingHandler tempHandler = new CastlingHandler(tempBoard); - CastlingHandler.CastlingMove tempCastling = tempHandler.getCastlingMove(fromX, fromY, toX, toY); - if (tempCastling != null) { - tempHandler.executeCastling(tempCastling); - } - } else if (isEnPassant) { - // For en passant, execute the en passant capture - Enpassant tempEnpassant = new Enpassant(tempBoard); - tempEnpassant.executeEnPassant(fromX, fromY, toX, toY); - } else { - // Remove any piece at destination (normal capture) - tempBoard.removePiece(toX, toY); - - // Move the piece - tempBoard.removePiece(fromX, fromY); - tempBoard.setPiece(tempPiece.isWhite(), tempPiece.getType(), toX, toY); - } + // Remove any piece at destination (normal capture) + tempBoard.removePiece(toX, toY); + + // Move the piece + tempBoard.removePiece(fromX, fromY); + tempBoard.setPiece(tempPiece.isWhite(), tempPiece.getType(), toX, toY); boolean opponentColor = !tempPiece.isWhite(); @@ -218,23 +134,23 @@ public class Move { switch (piece.getType()) { case Pawn: addPawnMoves(moves, piece, board); - break; + return moves; case Rook: addRookMoves(moves, piece, board); - break; + return moves; case Knight: addKnightMoves(moves, piece, board); - break; + return moves; case Bishop: addBishopMoves(moves, piece, board); - break; + return moves; case Queen: addRookMoves(moves, piece, board); addBishopMoves(moves, piece, board); - break; + return moves; case King: addKingMoves(moves, piece, board); - break; + return moves; } return moves; @@ -257,8 +173,33 @@ public class Move { } public boolean isKingInCheck(Board board, boolean isWhiteKing) { - CastlingHandler handler = new CastlingHandler(board); - return handler.isKingInCheck(isWhiteKing); + // Find the king's position + Piece king = null; + for (Piece p : board.getPieces()) { + if (p.getType() == PieceType.King && p.isWhite() == isWhiteKing) { + king = p; + return checkIfKingUnderAttack(board, isWhiteKing, king); + } + } + + return false; + } + + private boolean checkIfKingUnderAttack(Board board, boolean isWhiteKing, Piece king) { + // Check if any opponent piece can attack the king + for (Piece p : board.getPieces()) { + if (p.isWhite() == isWhiteKing) continue; // Skip pieces of same color + + // Get raw moves without check validation + List attackMoves = getValidDestinations(p, board); + + // If any piece can move to king's position, king is in check + if (attackMoves.contains(new Position(king.getX(), king.getY()))) { + return true; + } + } + + return false; } private void addPawnMoves(List validMoves, Piece piece, Board board) { @@ -266,7 +207,7 @@ public class Move { int y = piece.getY(); boolean isWhite = piece.isWhite(); - int direction = isWhite ? -1 : 1; // White pawns move up (decreasing y), black pawns move down (increasing y) + int direction = isWhite ? -1 : 1; // White pawns move up, black pawns move down // Forward movement int newY = y + direction; @@ -285,7 +226,7 @@ public class Move { } } - // Diagonal captures (regular captures) + // Diagonal captures for (int dx = -1; dx <= 1; dx += 2) { int newX = x + dx; if (newX >= 0 && newX < board.getWidth()) { @@ -296,29 +237,6 @@ public class Move { } } } - - // En passant captures - // Check if there's an opponent pawn adjacent to this pawn that can be captured via en passant - for (int dx = -1; dx <= 1; dx += 2) { // Check left and right - int adjacentX = x + dx; - if (adjacentX >= 0 && adjacentX < board.getWidth()) { - // Check if there's an opponent pawn at the same rank - Piece adjacentPiece = board.getPieceAt(adjacentX, y); - if (adjacentPiece != null && - adjacentPiece.getType() == PieceType.Pawn && - adjacentPiece.isWhite() != isWhite) { - - // Check if this pawn can be captured via en passant - if (board.canCaptureEnPassant(adjacentX, y)) { - // The capture square is diagonally forward from the capturing pawn - int captureY = y + direction; - if (captureY >= 0 && captureY < board.getHeight()) { - validMoves.add(new Position(adjacentX, captureY)); - } - } - } - } - } } private void addRookMoves(List validMoves, Piece piece, Board board) { @@ -428,8 +346,9 @@ public class Move { private void addKingMoves(List validMoves, Piece piece, Board board) { int x = piece.getX(); int y = piece.getY(); + boolean isWhite = piece.isWhite(); - // All adjacent squares (normal king moves) + // All adjacent squares for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { if (dx == 0 && dy == 0) continue; // Skip the current position @@ -440,30 +359,13 @@ public class Move { if (newX >= 0 && newX < board.getWidth() && newY >= 0 && newY < board.getHeight()) { Piece targetPiece = board.getPieceAt(newX, newY); - if (targetPiece == null || targetPiece.isWhite() != piece.isWhite()) { + if (targetPiece == null || targetPiece.isWhite() != isWhite) { // Empty square or can capture opponent's piece validMoves.add(new Position(newX, newY)); } } } } - - // Add castling moves - CastlingHandler handler = new CastlingHandler(board); - List castlingPositions = handler.getCastlingPositions(piece); - for (CastlingHandler.Position castlingPos : castlingPositions) { - validMoves.add(new Position(castlingPos.x, castlingPos.y)); - } - } - - // Helper method to check if a list contains a position - private boolean containsPosition(List positions, Position target) { - for (Position pos : positions) { - if (pos.x == target.x && pos.y == target.y) { - return true; - } - } - return false; } public class Position { @@ -475,6 +377,7 @@ public class Move { this.y = y; } + @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; @@ -482,4 +385,9 @@ public class Move { return x == position.x && y == position.y; } } -} \ No newline at end of file + public int getFromX() { return fromX; } + public int getFromY() { return fromY; } + public int getToX() { return toX; } + public int getToY() { return toY; } + +} diff --git a/OOP_1A2_Project/src/backend/Piece.java b/OOP_1A2_Project/src/backend/Piece.java index e224343..d08b469 100644 --- a/OOP_1A2_Project/src/backend/Piece.java +++ b/OOP_1A2_Project/src/backend/Piece.java @@ -32,4 +32,11 @@ public class Piece { this.x = x; this.y = y; } + public Piece(boolean isWhite, PieceType type, int x, int y) { + this.isWhite = isWhite; + this.type = type; + this.x = x; + this.y = y; + } + }