comments
This commit is contained in:
parent
ccabbde52d
commit
36e923650e
|
|
@ -4,13 +4,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class AutoPlayer {
|
public class AutoPlayer {
|
||||||
|
//Creates an Empty list to hold all the legal moves for the current player
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the best Move to try on provided board for active player
|
|
||||||
* @param board
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public Move computeBestMove(Board board) {ArrayList<Move> possibleMoves = new ArrayList<>();
|
public Move computeBestMove(Board board) {ArrayList<Move> possibleMoves = new ArrayList<>();
|
||||||
boolean isWhiteTurn = board.isTurnWhite();
|
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()) {
|
if (possibleMoves.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,24 +5,30 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Board {
|
public class Board {
|
||||||
|
//class fields - All core game state lives here
|
||||||
private int width;
|
private int width;
|
||||||
private int height;
|
private int height;
|
||||||
private ArrayList<Piece> pieces;
|
private ArrayList<Piece> pieces;
|
||||||
private int turnNumber = 0;
|
private int turnNumber = 0;
|
||||||
private boolean turnWhite = true;
|
private boolean turnWhite = true;
|
||||||
|
//for UI State
|
||||||
private int[] selected = null; //for user touch
|
private int[] selected = null; //for user touch
|
||||||
private ArrayList<int[]> highlighted = new ArrayList<>();
|
private ArrayList<int[]> highlighted = new ArrayList<>();
|
||||||
|
//for Undo and Special rules
|
||||||
private ArrayList<Move> moveHistory = new ArrayList<>();
|
private ArrayList<Move> moveHistory = new ArrayList<>();
|
||||||
private int[] enPassantTarget = null;
|
private int[] enPassantTarget = null;
|
||||||
|
|
||||||
|
//Constructors - Initialize the empty board of the specific size 8*8
|
||||||
public Board(int colNum, int lineNum) {
|
public Board(int colNum, int lineNum) {
|
||||||
this.width = colNum;
|
this.width = colNum;
|
||||||
this.height = lineNum;
|
this.height = lineNum;
|
||||||
this.pieces = new ArrayList<>();
|
this.pieces = new ArrayList<>();
|
||||||
this.enPassantTarget = null;
|
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) {
|
public Board(Board other) {
|
||||||
|
//copy primitives and simple fields
|
||||||
this.width = other.width;
|
this.width = other.width;
|
||||||
this.height = other.height;
|
this.height = other.height;
|
||||||
this.turnNumber = other.turnNumber;
|
this.turnNumber = other.turnNumber;
|
||||||
|
|
@ -33,22 +39,26 @@ public class Board {
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
|
||||||
|
//Deep copy each piece
|
||||||
this.pieces = new ArrayList<>();
|
this.pieces = new ArrayList<>();
|
||||||
for (Piece p : other.pieces) {
|
for (Piece p : other.pieces) {
|
||||||
this.pieces.add(new Piece(p)); // uses Piece copy constructor (next step)
|
this.pieces.add(new Piece(p)); // uses Piece copy constructor (next step)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Copy UI Highlights
|
||||||
this.highlighted = new ArrayList<>();
|
this.highlighted = new ArrayList<>();
|
||||||
for (int[] pos : other.highlighted) {
|
for (int[] pos : other.highlighted) {
|
||||||
this.highlighted.add(new int[]{pos[0], pos[1]});
|
this.highlighted.add(new int[]{pos[0], pos[1]});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
//shallow copy move history
|
||||||
this.moveHistory = new ArrayList<>(other.moveHistory);
|
this.moveHistory = new ArrayList<>(other.moveHistory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Simple Getters
|
||||||
|
//returns board with column
|
||||||
public int getWidth() {
|
public int getWidth() {
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
@ -61,10 +71,13 @@ public class Board {
|
||||||
return turnNumber;
|
return turnNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//return true if its white turn to move
|
||||||
public boolean isTurnWhite() {
|
public boolean isTurnWhite() {
|
||||||
return turnWhite;
|
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) {
|
public void setPiece(boolean isWhite, PieceType type, int x, int y) {
|
||||||
Piece piece = new Piece(isWhite, type, x, y);
|
Piece piece = new Piece(isWhite, type, x, y);
|
||||||
pieces.add(piece);
|
pieces.add(piece);
|
||||||
|
|
@ -73,11 +86,12 @@ public class Board {
|
||||||
public void populateBoard() {
|
public void populateBoard() {
|
||||||
cleanBoard(); // clear any existing pieces
|
cleanBoard(); // clear any existing pieces
|
||||||
|
|
||||||
// White pieces
|
// White pieces- Places white pawns at rank 1
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
setPiece(true, PieceType.Pawn, i, 1);
|
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.Knight, 1, 0);
|
||||||
setPiece(true, PieceType.Bishop, 2, 0);
|
setPiece(true, PieceType.Bishop, 2, 0);
|
||||||
setPiece(true, PieceType.Queen, 3, 0);
|
setPiece(true, PieceType.Queen, 3, 0);
|
||||||
|
|
@ -86,7 +100,7 @@ public class Board {
|
||||||
setPiece(true, PieceType.Knight, 6, 0);
|
setPiece(true, PieceType.Knight, 6, 0);
|
||||||
setPiece(true, PieceType.Rook, 7, 0);
|
setPiece(true, PieceType.Rook, 7, 0);
|
||||||
|
|
||||||
// Black pieces
|
// Black pieces - positions
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
setPiece(false, PieceType.Pawn, i, 6);
|
setPiece(false, PieceType.Pawn, i, 6);
|
||||||
}
|
}
|
||||||
|
|
@ -100,11 +114,17 @@ public class Board {
|
||||||
setPiece(false, PieceType.Rook, 7, 7);
|
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() {
|
public void cleanBoard() {
|
||||||
pieces.clear();
|
pieces.clear();
|
||||||
enPassantTarget = null;
|
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() {
|
public String toString() {
|
||||||
String[][] grid = new String[height][width];
|
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<Piece> getPieces() {
|
public ArrayList<Piece> getPieces() {
|
||||||
return pieces;
|
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) {
|
public Piece getKing(boolean isWhite) {
|
||||||
for (Piece p : pieces) {
|
for (Piece p : pieces) {
|
||||||
if (p.isWhite() == isWhite && p.getType() == PieceType.King) {
|
if (p.isWhite() == isWhite && p.getType() == PieceType.King) {
|
||||||
|
|
@ -149,7 +172,7 @@ public class Board {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
//Scans every opponent move to see if it lands on your king
|
||||||
public boolean isInCheck(boolean whitePlayer) {
|
public boolean isInCheck(boolean whitePlayer) {
|
||||||
Piece king = getKing(whitePlayer);
|
Piece king = getKing(whitePlayer);
|
||||||
if (king == null) return false;
|
if (king == null) return false;
|
||||||
|
|
@ -170,10 +193,11 @@ public class Board {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Declares Checkmate if in check and no legal moves left
|
||||||
public boolean isCheckmate(boolean whitePlayer) {
|
public boolean isCheckmate(boolean whitePlayer) {
|
||||||
if (!isInCheck(whitePlayer)) return false;
|
if (!isInCheck(whitePlayer)) return false;
|
||||||
|
|
||||||
|
//if any legal moves exists - not checkmate
|
||||||
for (Piece p : pieces) {
|
for (Piece p : pieces) {
|
||||||
if (p.isWhite() == whitePlayer) {
|
if (p.isWhite() == whitePlayer) {
|
||||||
ArrayList<int[]> legalMoves = getLegalMoves(p);
|
ArrayList<int[]> legalMoves = getLegalMoves(p);
|
||||||
|
|
@ -184,6 +208,8 @@ public class Board {
|
||||||
return true;
|
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) {
|
public void userTouch(int x, int y) {
|
||||||
Piece clickedPiece = getPieceAt(x, y);
|
Piece clickedPiece = getPieceAt(x, y);
|
||||||
|
|
||||||
|
|
@ -195,6 +221,7 @@ public class Board {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Second click: check if move is legal
|
// Second click: check if move is legal
|
||||||
|
//it also checks if target is highlighted,then play move
|
||||||
if (isHighlighted(x, y)) {
|
if (isHighlighted(x, y)) {
|
||||||
Piece piece = getPieceAt(selected[0], selected[1]);
|
Piece piece = getPieceAt(selected[0], selected[1]);
|
||||||
Piece captured = getPieceAt(x, y);
|
Piece captured = getPieceAt(x, y);
|
||||||
|
|
@ -225,6 +252,7 @@ public class Board {
|
||||||
public boolean isSimulateOnly() {
|
public boolean isSimulateOnly() {
|
||||||
return simulateOnly;
|
return simulateOnly;
|
||||||
}
|
}
|
||||||
|
//is used to validate castling and pinned-piece moves
|
||||||
private boolean isSquareUnderAttack(boolean byWhite, int x, int y) {
|
private boolean isSquareUnderAttack(boolean byWhite, int x, int y) {
|
||||||
for (Piece p : pieces) {
|
for (Piece p : pieces) {
|
||||||
if (p.isWhite() != byWhite) continue;
|
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<int[]> getLegalMoves(Piece piece) {
|
public ArrayList<int[]> getLegalMoves(Piece piece) {
|
||||||
return getLegalMoves(piece, true); // default: check for king safety
|
return getLegalMoves(piece, true); // default: check for king safety
|
||||||
}
|
}
|
||||||
|
|
@ -248,6 +277,8 @@ public class Board {
|
||||||
|
|
||||||
switch (piece.getType()) {
|
switch (piece.getType()) {
|
||||||
case Pawn:
|
case Pawn:
|
||||||
|
//single & double advance
|
||||||
|
//captures(including en passant)
|
||||||
int forwardY = y + dir;
|
int forwardY = y + dir;
|
||||||
|
|
||||||
if (isInBounds(x, forwardY) && getPieceAt(x, forwardY) == null) {
|
if (isInBounds(x, forwardY) && getPieceAt(x, forwardY) == null) {
|
||||||
|
|
@ -300,6 +331,7 @@ public class Board {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case King:
|
case King:
|
||||||
|
//one square in every direction + castling
|
||||||
for (int dx = -1; dx <= 1; dx++) {
|
for (int dx = -1; dx <= 1; dx++) {
|
||||||
for (int dy = -1; dy <= 1; dy++) {
|
for (int dy = -1; dy <= 1; dy++) {
|
||||||
if (dx == 0 && dy == 0) continue;
|
if (dx == 0 && dy == 0) continue;
|
||||||
|
|
@ -312,8 +344,7 @@ public class Board {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Castling logic
|
|
||||||
// Castling logic
|
// Castling logic
|
||||||
if (!piece.hasMoved() && (!validateCheck || !isInCheck(piece.isWhite()))) {
|
if (!piece.hasMoved() && (!validateCheck || !isInCheck(piece.isWhite()))) {
|
||||||
int row = piece.isWhite() ? 0 : 7;
|
int row = piece.isWhite() ? 0 : 7;
|
||||||
|
|
@ -388,7 +419,8 @@ public class Board {
|
||||||
return legalMoves;
|
return legalMoves;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//walks in direction (dx,dy) adding empty squares and a single capture
|
||||||
|
//Avoids repeating rook/bishop/queen code
|
||||||
private void addLinearMoves(ArrayList<int[]> moves, Piece piece, int dx, int dy) {
|
private void addLinearMoves(ArrayList<int[]> moves, Piece piece, int dx, int dy) {
|
||||||
int x = piece.getX(), y = piece.getY();
|
int x = piece.getX(), y = piece.getY();
|
||||||
while (true) {
|
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) {
|
private boolean isInBounds(int x, int y) {
|
||||||
return x >= 0 && y >= 0 && x < width && y < height;
|
return x >= 0 && y >= 0 && x < width && y < height;
|
||||||
}
|
}
|
||||||
|
|
@ -417,27 +451,22 @@ public class Board {
|
||||||
|
|
||||||
|
|
||||||
/* saving-loading feature :*/
|
/* saving-loading feature :*/
|
||||||
|
// @return each line representing one piece, plus a header:
|
||||||
// in Board.java
|
// 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() {
|
public String[] toFileRep() {
|
||||||
List<String> lines = new ArrayList<>();
|
List<String> lines = new ArrayList<>();
|
||||||
|
|
||||||
int epX = enPassantTarget == null ? -1 : enPassantTarget[0];
|
int epX = enPassantTarget == null ? -1 : enPassantTarget[0];
|
||||||
int epY = enPassantTarget == null ? -1 : enPassantTarget[1];
|
int epY = enPassantTarget == null ? -1 : enPassantTarget[1];
|
||||||
// header
|
// header:width,height,turnNumber,turnWhite,enPassantX,enpassantY
|
||||||
lines.add(width + "," +
|
lines.add(width + "," +
|
||||||
height + "," +
|
height + "," +
|
||||||
turnNumber + "," +
|
turnNumber + "," +
|
||||||
turnWhite + "," +
|
turnWhite + "," +
|
||||||
epX + "," + epY);
|
epX + "," + epY);
|
||||||
|
|
||||||
// pieces
|
// pieces - each piece's own serialization
|
||||||
for (Piece p : pieces) {
|
for (Piece p : pieces) {
|
||||||
lines.add(p.toFileRep());
|
lines.add(p.toFileRep());
|
||||||
}
|
}
|
||||||
|
|
@ -445,12 +474,9 @@ public class Board {
|
||||||
return lines.toArray(new String[0]);
|
return lines.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// in Board.java
|
|
||||||
|
|
||||||
public Board(String[] array) {
|
public Board(String[] array) {
|
||||||
this.pieces = new ArrayList<>();
|
this.pieces = new ArrayList<>();
|
||||||
// parse header
|
// parse header fields
|
||||||
String[] hdr = array[0].split(",");
|
String[] hdr = array[0].split(",");
|
||||||
this.width = Integer.parseInt(hdr[0]);
|
this.width = Integer.parseInt(hdr[0]);
|
||||||
this.height = Integer.parseInt(hdr[1]);
|
this.height = Integer.parseInt(hdr[1]);
|
||||||
|
|
@ -466,13 +492,13 @@ public class Board {
|
||||||
this.pieces.add(p);
|
this.pieces.add(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear other state
|
// clear other state - resets UI and History
|
||||||
this.selected = null;
|
this.selected = null;
|
||||||
this.highlighted = new ArrayList<>();
|
this.highlighted = new ArrayList<>();
|
||||||
this.moveHistory = new ArrayList<>();
|
this.moveHistory = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//return the Piece at (x,y) or null if none present
|
||||||
public 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) {
|
||||||
|
|
@ -482,17 +508,14 @@ public class Board {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//returns true if (x,y) is one of the highlighted legal moves
|
||||||
/* The following methods require more work ! */
|
|
||||||
|
|
||||||
public boolean isHighlighted(int x, int y) {
|
public boolean isHighlighted(int x, int y) {
|
||||||
for (int[] pos : highlighted) {
|
for (int[] pos : highlighted) {
|
||||||
if (pos[0] == x && pos[1] == y) return true;
|
if (pos[0] == x && pos[1] == y) return true;
|
||||||
}
|
}
|
||||||
//TODO
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
//reverses the very last playMove including rook reposition for castling and recovers captured pawns
|
||||||
public void undoLastMove() {
|
public void undoLastMove() {
|
||||||
|
|
||||||
if (moveHistory.isEmpty()) return;
|
if (moveHistory.isEmpty()) return;
|
||||||
|
|
@ -539,7 +562,7 @@ public void undoLastMove() {
|
||||||
selected = null;
|
selected = null;
|
||||||
highlighted.clear();
|
highlighted.clear();
|
||||||
}
|
}
|
||||||
|
//applies every rule: castling,en passant,captures,promotions,history and turn switching
|
||||||
public void playMove(Move move) {
|
public void playMove(Move move) {
|
||||||
Piece piece = move.getPieceMoved();
|
Piece piece = move.getPieceMoved();
|
||||||
int fromX = move.getFromX(), fromY = move.getFromY();
|
int fromX = move.getFromX(), fromY = move.getFromY();
|
||||||
|
|
@ -614,7 +637,8 @@ public void playMove(Move move) {
|
||||||
highlighted.clear();
|
highlighted.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int evaluateBoard() {
|
//Simple static evaluation for negamax
|
||||||
|
public int evaluateBoard() {
|
||||||
int score = 0;
|
int score = 0;
|
||||||
for (Piece piece : pieces) {
|
for (Piece piece : pieces) {
|
||||||
int value = 0;
|
int value = 0;
|
||||||
|
|
@ -637,9 +661,8 @@ public void playMove(Move move) {
|
||||||
BLACK_WINS // white is checkmated
|
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() {
|
public GameResult getGameResult() {
|
||||||
// if White is checkmated → Black wins
|
// if White is checkmated → Black wins
|
||||||
if (isCheckmate(true)) return GameResult.BLACK_WINS;
|
if (isCheckmate(true)) return GameResult.BLACK_WINS;
|
||||||
|
|
@ -650,10 +673,8 @@ public void playMove(Move move) {
|
||||||
return GameResult.ONGOING;
|
return GameResult.ONGOING;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
//@param whitePlayer whose turn we're checking for stalemate
|
||||||
* @param whitePlayer whose turn we're checking for stalemate
|
//@return true if whitePlayer is not in check and has no legal moves
|
||||||
* @return true if whitePlayer is not in check and has no legal moves
|
|
||||||
*/
|
|
||||||
private boolean isStalemate(boolean whitePlayer) {
|
private boolean isStalemate(boolean whitePlayer) {
|
||||||
// must not be in check
|
// must not be in check
|
||||||
if (isInCheck(whitePlayer)) return false;
|
if (isInCheck(whitePlayer)) return false;
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,16 @@
|
||||||
package backend;
|
package backend;
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple two‐player chess clock with an optional increment.
|
//A simple two‐player chess clock with an optional increment.
|
||||||
* Getters do NOT mutate state; only switchPlayer() and pause() do.
|
//Getters do NOT mutate state; only switchPlayer() and pause() do.
|
||||||
*/
|
|
||||||
public class ChessClock {
|
public class ChessClock {
|
||||||
private long whiteTimeMs, blackTimeMs; // stored remaining times
|
private long whiteTimeMs, blackTimeMs; // stored remaining times
|
||||||
private final long incrementMs; // per‐move bonus
|
private final long incrementMs; // per‐move bonus
|
||||||
private boolean whiteToMove; // whose clock is running
|
private boolean whiteToMove; // whose clock is running
|
||||||
private long lastTimestamp; // when current turn started
|
private long lastTimestamp; // when current turn started
|
||||||
|
|
||||||
/**
|
//@param initialTimeMs starting time per player (ms)
|
||||||
* @param initialTimeMs starting time per player (ms)
|
//@param incrementMs per‐move increment (ms)
|
||||||
* @param incrementMs per‐move increment (ms)
|
|
||||||
*/
|
|
||||||
public ChessClock(long initialTimeMs, long incrementMs) {
|
public ChessClock(long initialTimeMs, long incrementMs) {
|
||||||
this.whiteTimeMs = initialTimeMs;
|
this.whiteTimeMs = initialTimeMs;
|
||||||
this.blackTimeMs = initialTimeMs;
|
this.blackTimeMs = initialTimeMs;
|
||||||
|
|
@ -22,10 +19,9 @@ public class ChessClock {
|
||||||
this.lastTimestamp = System.currentTimeMillis();
|
this.lastTimestamp = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called only when a move is made to flip the clock.
|
//Called only when a move is made to flip the clock.
|
||||||
* Subtracts the elapsed time, adds the increment, and switches sides.
|
//Subtracts the elapsed time, adds the increment, and switches sides.
|
||||||
*/
|
|
||||||
public void switchPlayer() {
|
public void switchPlayer() {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
long elapsed = now - lastTimestamp;
|
long elapsed = now - lastTimestamp;
|
||||||
|
|
@ -40,10 +36,10 @@ public class ChessClock {
|
||||||
lastTimestamp = now;
|
lastTimestamp = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Pause the clock (e.g. at game end).
|
//Pause the clock (e.g. at game end).
|
||||||
* Subtracts elapsed from whichever side was running.
|
// Subtracts elapsed from whichever side was running.
|
||||||
*/
|
|
||||||
public void pause() {
|
public void pause() {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
long elapsed = now - lastTimestamp;
|
long elapsed = now - lastTimestamp;
|
||||||
|
|
@ -55,9 +51,9 @@ public class ChessClock {
|
||||||
lastTimestamp = now;
|
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() {
|
public long getWhiteTimeMs() {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
if (whiteToMove) {
|
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() {
|
public long getBlackTimeMs() {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
if (!whiteToMove) {
|
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() {
|
public boolean isFlagged() {
|
||||||
return getWhiteTimeMs() <= 0 || getBlackTimeMs() <= 0;
|
return getWhiteTimeMs() <= 0 || getBlackTimeMs() <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Format mm:ss for display
|
//Format mm:ss for display
|
||||||
*/
|
|
||||||
public static String format(long timeMs) {
|
public static String format(long timeMs) {
|
||||||
long totalSec = Math.max(timeMs / 1000, 0);
|
long totalSec = Math.max(timeMs / 1000, 0);
|
||||||
long minutes = totalSec / 60;
|
long minutes = totalSec / 60;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue