new class

This commit is contained in:
Yash Shah 2025-05-22 07:12:03 +02:00
parent a1771230ce
commit 5b454d3a66
4 changed files with 196 additions and 39 deletions

View File

@ -184,9 +184,6 @@ public class Board {
return true;
}
public void userTouch(int x, int y) {
Piece clickedPiece = getPieceAt(x, y);
@ -217,6 +214,8 @@ public class Board {
}
}
private boolean simulateOnly = false;
public void setSimulateOnly(boolean simulateOnly) {

100
src/backend/ChessClock.java Normal file
View File

@ -0,0 +1,100 @@
package backend;
/**
* A simple twoplayer 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; // permove bonus
private boolean whiteToMove; // whose clock is running
private long lastTimestamp; // when current turn started
/**
* @param initialTimeMs starting time per player (ms)
* @param incrementMs permove increment (ms)
*/
public ChessClock(long initialTimeMs, long incrementMs) {
this.whiteTimeMs = initialTimeMs;
this.blackTimeMs = initialTimeMs;
this.incrementMs = incrementMs;
this.whiteToMove = true;
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.
*/
public void switchPlayer() {
long now = System.currentTimeMillis();
long elapsed = now - lastTimestamp;
if (whiteToMove) {
whiteTimeMs -= elapsed;
whiteTimeMs += incrementMs;
} else {
blackTimeMs -= elapsed;
blackTimeMs += incrementMs;
}
whiteToMove = !whiteToMove;
lastTimestamp = now;
}
/**
* 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;
if (whiteToMove) {
whiteTimeMs -= elapsed;
} else {
blackTimeMs -= elapsed;
}
lastTimestamp = now;
}
/**
* @return Whites remaining time (ms), computed without mutating state
*/
public long getWhiteTimeMs() {
long now = System.currentTimeMillis();
if (whiteToMove) {
long elapsed = now - lastTimestamp;
return Math.max(whiteTimeMs - elapsed, 0);
} else {
return Math.max(whiteTimeMs, 0);
}
}
/**
* @return Blacks remaining time (ms), computed without mutating state
*/
public long getBlackTimeMs() {
long now = System.currentTimeMillis();
if (!whiteToMove) {
long elapsed = now - lastTimestamp;
return Math.max(blackTimeMs - elapsed, 0);
} else {
return Math.max(blackTimeMs, 0);
}
}
/**
* @return true if either player has run out of time
*/
public boolean isFlagged() {
return getWhiteTimeMs() <= 0 || getBlackTimeMs() <= 0;
}
/**
* Format mm:ss for display
*/
public static String format(long timeMs) {
long totalSec = Math.max(timeMs / 1000, 0);
long minutes = totalSec / 60;
long seconds = totalSec % 60;
return String.format("%d:%02d", minutes, seconds);
}
}

View File

@ -10,21 +10,23 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/** Your main game thread, now with a ticking clock */
public class Game extends Thread {
private AutoPlayer aiPlayer;
private Board board;
private MyInterface mjf;
private int COL_NUM = 8;
private int LINE_NUM = 8;
private int loopDelay = 250;
private ChessClock clock; // our new clock
private int COL_NUM = 8, LINE_NUM = 8;
private int loopDelay = 250; // tick every 250ms
private boolean[] activationAIFlags;
private boolean gameOver = false; // track when the game ends
private boolean gameOver = false;
public Game(MyInterface mjfParam) {
mjf = mjfParam;
board = new Board(COL_NUM, LINE_NUM);
activationAIFlags = new boolean[2];
aiPlayer = new AutoPlayer();
this.mjf = mjfParam;
this.board = new Board(COL_NUM, LINE_NUM);
this.clock = new ChessClock(5 * 60 * 1000, 2 * 1000); // 5 + 2 increment
this.activationAIFlags = new boolean[2];
this.aiPlayer = new AutoPlayer();
}
// Save & load
@ -36,8 +38,14 @@ public class Game extends Thread {
public void loadFromFile(Path path) throws IOException {
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
board = new Board(lines.toArray(new String[0]));
// refresh UI for the new board state:
// reset clock to full time or load a saved timestamp if you extend it
clock = new ChessClock(5 * 60 * 1000, 2 * 1000);
// immediate UI refresh
mjf.update(board.getTurnNumber(), board.isTurnWhite());
mjf.updateClocks(
ChessClock.format(clock.getWhiteTimeMs()),
ChessClock.format(clock.getBlackTimeMs())
);
checkGameOver();
}
//
@ -48,9 +56,17 @@ public class Game extends Thread {
@Override
public void run() {
while (!gameOver) {
aiPlayerTurn();
// 1) Always tick the UI with current turn & clock
mjf.update(board.getTurnNumber(), board.isTurnWhite());
mjf.updateClocks(
ChessClock.format(clock.getWhiteTimeMs()),
ChessClock.format(clock.getBlackTimeMs())
);
// 2) Check if timeloss or checkmate/stalemate happened
checkGameOver();
// 3) If its the AIs turn, let it move
aiPlayerTurn();
// 4) Sleep until next tick
try { Thread.sleep(loopDelay); }
catch (InterruptedException e) { e.printStackTrace(); }
}
@ -61,10 +77,11 @@ public class Game extends Thread {
}
private void aiPlayerTurn() {
if (isAITurn()) {
if (!gameOver && isAITurn()) {
Move bestMove = aiPlayer.getBestMoveUsingNegamax(board, 2);
if (bestMove != null) {
board.playMove(bestMove);
clock.switchPlayer(); // flip clock on AI move
}
}
}
@ -74,13 +91,33 @@ public class Game extends Thread {
System.out.println("Click out of bounds");
return;
}
if (!isAITurn()) {
board.userTouch(x, y);
mjf.update(board.getTurnNumber(), board.isTurnWhite());
checkGameOver();
if (gameOver || isAITurn()) return;
// 1) remember how many turns have passed
int beforeTurns = board.getTurnNumber();
// 2) attempt the click (select or move)
board.userTouch(x, y);
// 3) if turnNumber increased, a move was made switch clock
if (board.getTurnNumber() > beforeTurns) {
clock.switchPlayer();
}
// 4) update UI & clocks
mjf.update(board.getTurnNumber(), board.isTurnWhite());
mjf.updateClocks(
ChessClock.format(clock.getWhiteTimeMs()),
ChessClock.format(clock.getBlackTimeMs())
);
// 5) check for gameend
checkGameOver();
}
// Other existing methods
public void setPiece(boolean isWhite, PieceType type, int x, int y) {
board.setPiece(isWhite, type, x, y);
}
@ -104,42 +141,47 @@ public class Game extends Thread {
checkGameOver();
}
public Iterable<Piece> getPieces() {
return board.getPieces();
}
public Iterable<Piece> getPieces() { return board.getPieces(); }
public boolean isSelected(int x, int y) {
return board.isSelected(x, y);
}
public boolean isHighlighted(int x, int y) {
return board.isHighlighted(x, y);
}
public boolean isSelected(int x, int y) { return board.isSelected(x, y); }
public boolean isHighlighted(int x, int y) { return board.isHighlighted(x, y); }
public void undoLastMove() {
board.undoLastMove();
mjf.update(board.getTurnNumber(), board.isTurnWhite());
gameOver = false; // allow play to continue after undo
// we wont rewind the clock; you could if you like
gameOver = false;
}
public void toggleAI(boolean isWhite) {
activationAIFlags[isWhite ? 1 : 0] = !activationAIFlags[isWhite ? 1 : 0];
}
// WIN/LOSE/DRAW logic
// WIN/LOSE/DRAW + TIMELOSS logic
private void checkGameOver() {
// 1) Timeloss check
if (clock.isFlagged()) {
clock.pause();
if (clock.getWhiteTimeMs() <= 0) {
mjf.setStepBanner("Black wins on time!");
} else {
mjf.setStepBanner("White wins on time!");
}
gameOver = true;
return;
}
// 2) Checkmate/stalemate
GameResult res = board.getGameResult();
if (res != GameResult.ONGOING) {
String msg;
switch (res) {
case WHITE_WINS: msg = "White wins!"; break;
case BLACK_WINS: msg = "Black wins!"; break;
case DRAW: msg = "Draw!"; break;
default: msg = ""; break;
}
String msg = switch(res) {
case WHITE_WINS -> "White wins!";
case BLACK_WINS -> "Black wins!";
case DRAW -> "Draw!";
default -> "";
};
clock.pause();
mjf.setStepBanner(msg);
gameOver = true;
}
}
//
}
}

View File

@ -41,6 +41,9 @@ public class MyInterface extends JFrame {
private Game game;
private JLabel actionLabel;
private JCheckBox chckbxBlackAI;
private JLabel whiteClockLabel;
private JLabel blackClockLabel;
private JCheckBox chckbxWhiteAI;
/**
@ -136,6 +139,12 @@ public class MyInterface extends JFrame {
panelDraw = new JPanelChessBoard(this);
contentPane.add(panelDraw, BorderLayout.CENTER);
whiteClockLabel = new JLabel("White: 5:00");
panelTop.add(whiteClockLabel);
blackClockLabel = new JLabel("Black: 5:00");
panelTop.add(blackClockLabel);
}
public void setStepBanner(String s) {
@ -265,5 +274,12 @@ public class MyInterface extends JFrame {
public void eraseLabels() {
this.setStepBanner("Turn : X");
}
public void updateClocks(String whiteTime, String blackTime) {
whiteClockLabel.setText("White: " + whiteTime);
blackClockLabel.setText("Black: " + blackTime);
repaint();
}
}