From 36e923650ef9d7857539c88a7df4a735cff30251 Mon Sep 17 00:00:00 2001 From: Yash Shah Date: Thu, 22 May 2025 08:35:40 +0200 Subject: [PATCH] comments --- src/backend/AutoPlayer.java | 10 +--- src/backend/Board.java | 103 ++++++++++++++++++++++-------------- src/backend/ChessClock.java | 54 +++++++++---------- 3 files changed, 89 insertions(+), 78 deletions(-) diff --git a/src/backend/AutoPlayer.java b/src/backend/AutoPlayer.java index 5510750..a5f39bf 100644 --- a/src/backend/AutoPlayer.java +++ b/src/backend/AutoPlayer.java @@ -4,13 +4,7 @@ import java.util.ArrayList; import java.util.Random; public class AutoPlayer { - - - /** - * returns the best Move to try on provided board for active player - * @param board - * @return - */ + //Creates an Empty list to hold all the legal moves for the current player public Move computeBestMove(Board board) {ArrayList possibleMoves = new ArrayList<>(); boolean isWhiteTurn = board.isTurnWhite(); @@ -26,7 +20,7 @@ public class AutoPlayer { } } - // If there are no moves available, return null + // If there are no moves available, return null- Handles checkmate or stalemate if (possibleMoves.isEmpty()) { return null; } diff --git a/src/backend/Board.java b/src/backend/Board.java index cea4227..5471bd1 100644 --- a/src/backend/Board.java +++ b/src/backend/Board.java @@ -5,24 +5,30 @@ import java.util.ArrayList; import java.util.List; public class Board { + //class fields - All core game state lives here private int width; private int height; private ArrayList pieces; private int turnNumber = 0; private boolean turnWhite = true; + //for UI State private int[] selected = null; //for user touch private ArrayList highlighted = new ArrayList<>(); + //for Undo and Special rules private ArrayList moveHistory = new ArrayList<>(); private int[] enPassantTarget = null; + //Constructors - Initialize the empty board of the specific size 8*8 public Board(int colNum, int lineNum) { this.width = colNum; this.height = lineNum; this.pieces = new ArrayList<>(); this.enPassantTarget = null; } - + //Deep Copy - clones the entire board to simulate without mutating the original + //Allows safe simulation without side effects on the source board public Board(Board other) { + //copy primitives and simple fields this.width = other.width; this.height = other.height; this.turnNumber = other.turnNumber; @@ -33,22 +39,26 @@ public class Board { : null; + //Deep copy each piece this.pieces = new ArrayList<>(); for (Piece p : other.pieces) { this.pieces.add(new Piece(p)); // uses Piece copy constructor (next step) } + //Copy UI Highlights this.highlighted = new ArrayList<>(); for (int[] pos : other.highlighted) { this.highlighted.add(new int[]{pos[0], pos[1]}); } - + //shallow copy move history this.moveHistory = new ArrayList<>(other.moveHistory); } + //Simple Getters + //returns board with column public int getWidth() { return width; } @@ -61,10 +71,13 @@ public class Board { return turnNumber; } + //return true if its white turn to move public boolean isTurnWhite() { return turnWhite; } + //places a new piece of a given colour & type at (x,y) + //used by populateBoard public void setPiece(boolean isWhite, PieceType type, int x, int y) { Piece piece = new Piece(isWhite, type, x, y); pieces.add(piece); @@ -73,11 +86,12 @@ public class Board { public void populateBoard() { cleanBoard(); // clear any existing pieces - // White pieces + // White pieces- Places white pawns at rank 1 for (int i = 0; i < 8; i++) { setPiece(true, PieceType.Pawn, i, 1); } - setPiece(true, PieceType.Rook, 0, 0); + //places white back-rank pieces + setPiece(true, PieceType.Rook, 1, 0); setPiece(true, PieceType.Knight, 1, 0); setPiece(true, PieceType.Bishop, 2, 0); setPiece(true, PieceType.Queen, 3, 0); @@ -86,7 +100,7 @@ public class Board { setPiece(true, PieceType.Knight, 6, 0); setPiece(true, PieceType.Rook, 7, 0); - // Black pieces + // Black pieces - positions for (int i = 0; i < 8; i++) { setPiece(false, PieceType.Pawn, i, 6); } @@ -100,11 +114,17 @@ public class Board { setPiece(false, PieceType.Rook, 7, 7); } + //empties the board state + //used before populating or loading new positions + //resets all pieces and reset en passant state public void cleanBoard() { pieces.clear(); enPassantTarget = null; } + //renders the board as ASCII art (eg. "..p......") + //one rank per line and uses Uppercase for BLACK and lowercase for White + //could have used char[][] for memory efficiency public String toString() { String[][] grid = new String[height][width]; @@ -137,10 +157,13 @@ public class Board { } + //Lookup by coordinates and helpful for move generation and UI + //return all active pieces on the board public ArrayList getPieces() { return pieces; } - + //finds the king side - necessary for check/checkmate + //returns the king piece for the given colour , or null if missing public Piece getKing(boolean isWhite) { for (Piece p : pieces) { if (p.isWhite() == isWhite && p.getType() == PieceType.King) { @@ -149,7 +172,7 @@ public class Board { } return null; } - + //Scans every opponent move to see if it lands on your king public boolean isInCheck(boolean whitePlayer) { Piece king = getKing(whitePlayer); if (king == null) return false; @@ -170,10 +193,11 @@ public class Board { return false; } - + //Declares Checkmate if in check and no legal moves left public boolean isCheckmate(boolean whitePlayer) { if (!isInCheck(whitePlayer)) return false; + //if any legal moves exists - not checkmate for (Piece p : pieces) { if (p.isWhite() == whitePlayer) { ArrayList legalMoves = getLegalMoves(p); @@ -184,6 +208,8 @@ public class Board { return true; } + //Implements a two-tap GUI: First selects a Piece + //then highlights the move and second tap either moves , reselect or deselects public void userTouch(int x, int y) { Piece clickedPiece = getPieceAt(x, y); @@ -195,6 +221,7 @@ public class Board { } } else { // Second click: check if move is legal + //it also checks if target is highlighted,then play move if (isHighlighted(x, y)) { Piece piece = getPieceAt(selected[0], selected[1]); Piece captured = getPieceAt(x, y); @@ -225,6 +252,7 @@ public class Board { public boolean isSimulateOnly() { return simulateOnly; } + //is used to validate castling and pinned-piece moves private boolean isSquareUnderAttack(boolean byWhite, int x, int y) { for (Piece p : pieces) { if (p.isWhite() != byWhite) continue; @@ -237,6 +265,7 @@ public class Board { } + //generates a pseudo-legal moves by piece type and then filters out those that leave your king in check public ArrayList getLegalMoves(Piece piece) { return getLegalMoves(piece, true); // default: check for king safety } @@ -248,6 +277,8 @@ public class Board { switch (piece.getType()) { case Pawn: + //single & double advance + //captures(including en passant) int forwardY = y + dir; if (isInBounds(x, forwardY) && getPieceAt(x, forwardY) == null) { @@ -300,6 +331,7 @@ public class Board { break; case King: + //one square in every direction + castling for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { if (dx == 0 && dy == 0) continue; @@ -312,8 +344,7 @@ public class Board { } } } - - // Castling logic + // Castling logic if (!piece.hasMoved() && (!validateCheck || !isInCheck(piece.isWhite()))) { int row = piece.isWhite() ? 0 : 7; @@ -388,7 +419,8 @@ public class Board { return legalMoves; } - + //walks in direction (dx,dy) adding empty squares and a single capture + //Avoids repeating rook/bishop/queen code private void addLinearMoves(ArrayList moves, Piece piece, int dx, int dy) { int x = piece.getX(), y = piece.getY(); while (true) { @@ -406,6 +438,8 @@ public class Board { } } + //helpers for bounds checks and UI flags + //return true of (x,y) is a valid board square private boolean isInBounds(int x, int y) { return x >= 0 && y >= 0 && x < width && y < height; } @@ -417,27 +451,22 @@ public class Board { /* saving-loading feature :*/ - - // in Board.java - - /** - * @return each line representing one piece, plus a header: - * Line 0: width,height,turnNumber,turnWhite,enPassantX,enPassantY - * Lines 1–N: serialized Pieces - */ + // @return each line representing one piece, plus a header: + // Line 0: width,height,turnNumber,turnWhite,enPassantX,enPassantY + // Lines 1–N: serialized Pieces public String[] toFileRep() { List lines = new ArrayList<>(); int epX = enPassantTarget == null ? -1 : enPassantTarget[0]; int epY = enPassantTarget == null ? -1 : enPassantTarget[1]; - // header + // header:width,height,turnNumber,turnWhite,enPassantX,enpassantY lines.add(width + "," + height + "," + turnNumber + "," + turnWhite + "," + epX + "," + epY); - // pieces + // pieces - each piece's own serialization for (Piece p : pieces) { lines.add(p.toFileRep()); } @@ -445,12 +474,9 @@ public class Board { return lines.toArray(new String[0]); } - - // in Board.java - public Board(String[] array) { this.pieces = new ArrayList<>(); - // parse header + // parse header fields String[] hdr = array[0].split(","); this.width = Integer.parseInt(hdr[0]); this.height = Integer.parseInt(hdr[1]); @@ -466,13 +492,13 @@ public class Board { this.pieces.add(p); } - // clear other state + // clear other state - resets UI and History this.selected = null; this.highlighted = new ArrayList<>(); this.moveHistory = new ArrayList<>(); } - + //return the Piece at (x,y) or null if none present public Piece getPieceAt(int x, int y) { for (Piece piece : pieces) { if (piece.getX() == x && piece.getY() == y) { @@ -482,17 +508,14 @@ public class Board { return null; } - - /* The following methods require more work ! */ - + //returns true if (x,y) is one of the highlighted legal moves public boolean isHighlighted(int x, int y) { for (int[] pos : highlighted) { if (pos[0] == x && pos[1] == y) return true; } - //TODO return false; } - +//reverses the very last playMove including rook reposition for castling and recovers captured pawns public void undoLastMove() { if (moveHistory.isEmpty()) return; @@ -539,7 +562,7 @@ public void undoLastMove() { selected = null; highlighted.clear(); } - + //applies every rule: castling,en passant,captures,promotions,history and turn switching public void playMove(Move move) { Piece piece = move.getPieceMoved(); int fromX = move.getFromX(), fromY = move.getFromY(); @@ -614,7 +637,8 @@ public void playMove(Move move) { highlighted.clear(); } - public int evaluateBoard() { + //Simple static evaluation for negamax + public int evaluateBoard() { int score = 0; for (Piece piece : pieces) { int value = 0; @@ -637,9 +661,8 @@ public void playMove(Move move) { BLACK_WINS // white is checkmated } - /** - * @return the current game result: ONGOING, DRAW, WHITE_WINS or BLACK_WINS. - */ + + //@return the current game result: ONGOING, DRAW, WHITE_WINS or BLACK_WINS. public GameResult getGameResult() { // if White is checkmated → Black wins if (isCheckmate(true)) return GameResult.BLACK_WINS; @@ -650,10 +673,8 @@ public void playMove(Move move) { return GameResult.ONGOING; } - /** - * @param whitePlayer whose turn we're checking for stalemate - * @return true if whitePlayer is not in check and has no legal moves - */ + //@param whitePlayer whose turn we're checking for stalemate + //@return true if whitePlayer is not in check and has no legal moves private boolean isStalemate(boolean whitePlayer) { // must not be in check if (isInCheck(whitePlayer)) return false; diff --git a/src/backend/ChessClock.java b/src/backend/ChessClock.java index e975c25..081dc61 100644 --- a/src/backend/ChessClock.java +++ b/src/backend/ChessClock.java @@ -1,19 +1,16 @@ package backend; -/** - * A simple two‐player chess clock with an optional increment. - * Getters do NOT mutate state; only switchPlayer() and pause() do. - */ + +//A simple two‐player chess clock with an optional increment. +//Getters do NOT mutate state; only switchPlayer() and pause() do. public class ChessClock { private long whiteTimeMs, blackTimeMs; // stored remaining times private final long incrementMs; // per‐move bonus private boolean whiteToMove; // whose clock is running private long lastTimestamp; // when current turn started - - /** - * @param initialTimeMs starting time per player (ms) - * @param incrementMs per‐move increment (ms) - */ + + //@param initialTimeMs starting time per player (ms) +//@param incrementMs per‐move increment (ms) public ChessClock(long initialTimeMs, long incrementMs) { this.whiteTimeMs = initialTimeMs; this.blackTimeMs = initialTimeMs; @@ -22,10 +19,9 @@ public class ChessClock { this.lastTimestamp = System.currentTimeMillis(); } - /** - * Called only when a move is made to flip the clock. - * Subtracts the elapsed time, adds the increment, and switches sides. - */ + + //Called only when a move is made to flip the clock. + //Subtracts the elapsed time, adds the increment, and switches sides. public void switchPlayer() { long now = System.currentTimeMillis(); long elapsed = now - lastTimestamp; @@ -40,10 +36,10 @@ public class ChessClock { lastTimestamp = now; } - /** - * Pause the clock (e.g. at game end). - * Subtracts elapsed from whichever side was running. - */ + + //Pause the clock (e.g. at game end). + // Subtracts elapsed from whichever side was running. + public void pause() { long now = System.currentTimeMillis(); long elapsed = now - lastTimestamp; @@ -55,9 +51,9 @@ public class ChessClock { lastTimestamp = now; } - /** - * @return White’s remaining time (ms), computed without mutating state - */ + + //@return White’s remaining time (ms), computed without mutating state + public long getWhiteTimeMs() { long now = System.currentTimeMillis(); if (whiteToMove) { @@ -68,9 +64,9 @@ public class ChessClock { } } - /** - * @return Black’s remaining time (ms), computed without mutating state - */ + + //@return Black’s remaining time (ms), computed without mutating state + public long getBlackTimeMs() { long now = System.currentTimeMillis(); if (!whiteToMove) { @@ -81,16 +77,16 @@ public class ChessClock { } } - /** - * @return true if either player has run out of time - */ + + //@return true if either player has run out of time + public boolean isFlagged() { return getWhiteTimeMs() <= 0 || getBlackTimeMs() <= 0; } - /** - * Format mm:ss for display - */ + + //Format mm:ss for display + public static String format(long timeMs) { long totalSec = Math.max(timeMs / 1000, 0); long minutes = totalSec / 60;