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; return true;
} }
public void userTouch(int x, int y) { public void userTouch(int x, int y) {
Piece clickedPiece = getPieceAt(x, y); Piece clickedPiece = getPieceAt(x, y);
@ -217,6 +214,8 @@ public class Board {
} }
} }
private boolean simulateOnly = false; private boolean simulateOnly = false;
public void setSimulateOnly(boolean simulateOnly) { 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.Arrays;
import java.util.List; import java.util.List;
/** Your main game thread, now with a ticking clock */
public class Game extends Thread { public class Game extends Thread {
private AutoPlayer aiPlayer; private AutoPlayer aiPlayer;
private Board board; private Board board;
private MyInterface mjf; private MyInterface mjf;
private int COL_NUM = 8; private ChessClock clock; // our new clock
private int LINE_NUM = 8; private int COL_NUM = 8, LINE_NUM = 8;
private int loopDelay = 250; private int loopDelay = 250; // tick every 250ms
private boolean[] activationAIFlags; private boolean[] activationAIFlags;
private boolean gameOver = false; // track when the game ends private boolean gameOver = false;
public Game(MyInterface mjfParam) { public Game(MyInterface mjfParam) {
mjf = mjfParam; this.mjf = mjfParam;
board = new Board(COL_NUM, LINE_NUM); this.board = new Board(COL_NUM, LINE_NUM);
activationAIFlags = new boolean[2]; this.clock = new ChessClock(5 * 60 * 1000, 2 * 1000); // 5 + 2 increment
aiPlayer = new AutoPlayer(); this.activationAIFlags = new boolean[2];
this.aiPlayer = new AutoPlayer();
} }
// Save & load // Save & load
@ -36,8 +38,14 @@ public class Game extends Thread {
public void loadFromFile(Path path) throws IOException { public void loadFromFile(Path path) throws IOException {
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8); List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
board = new Board(lines.toArray(new String[0])); 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.update(board.getTurnNumber(), board.isTurnWhite());
mjf.updateClocks(
ChessClock.format(clock.getWhiteTimeMs()),
ChessClock.format(clock.getBlackTimeMs())
);
checkGameOver(); checkGameOver();
} }
// //
@ -48,9 +56,17 @@ public class Game extends Thread {
@Override @Override
public void run() { public void run() {
while (!gameOver) { while (!gameOver) {
aiPlayerTurn(); // 1) Always tick the UI with current turn & clock
mjf.update(board.getTurnNumber(), board.isTurnWhite()); 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(); checkGameOver();
// 3) If its the AIs turn, let it move
aiPlayerTurn();
// 4) Sleep until next tick
try { Thread.sleep(loopDelay); } try { Thread.sleep(loopDelay); }
catch (InterruptedException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); }
} }
@ -61,10 +77,11 @@ public class Game extends Thread {
} }
private void aiPlayerTurn() { private void aiPlayerTurn() {
if (isAITurn()) { if (!gameOver && isAITurn()) {
Move bestMove = aiPlayer.getBestMoveUsingNegamax(board, 2); Move bestMove = aiPlayer.getBestMoveUsingNegamax(board, 2);
if (bestMove != null) { if (bestMove != null) {
board.playMove(bestMove); 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"); System.out.println("Click out of bounds");
return; return;
} }
if (!isAITurn()) { if (gameOver || isAITurn()) return;
board.userTouch(x, y);
mjf.update(board.getTurnNumber(), board.isTurnWhite()); // 1) remember how many turns have passed
checkGameOver(); 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) { public void setPiece(boolean isWhite, PieceType type, int x, int y) {
board.setPiece(isWhite, type, x, y); board.setPiece(isWhite, type, x, y);
} }
@ -104,42 +141,47 @@ public class Game extends Thread {
checkGameOver(); checkGameOver();
} }
public Iterable<Piece> getPieces() { public Iterable<Piece> getPieces() { return board.getPieces(); }
return board.getPieces();
}
public boolean isSelected(int x, int y) { public boolean isSelected(int x, int y) { return board.isSelected(x, y); }
return board.isSelected(x, y); public boolean isHighlighted(int x, int y) { return board.isHighlighted(x, y); }
}
public boolean isHighlighted(int x, int y) {
return board.isHighlighted(x, y);
}
public void undoLastMove() { public void undoLastMove() {
board.undoLastMove(); board.undoLastMove();
mjf.update(board.getTurnNumber(), board.isTurnWhite()); // we wont rewind the clock; you could if you like
gameOver = false; // allow play to continue after undo gameOver = false;
} }
public void toggleAI(boolean isWhite) { public void toggleAI(boolean isWhite) {
activationAIFlags[isWhite ? 1 : 0] = !activationAIFlags[isWhite ? 1 : 0]; activationAIFlags[isWhite ? 1 : 0] = !activationAIFlags[isWhite ? 1 : 0];
} }
// WIN/LOSE/DRAW logic // WIN/LOSE/DRAW + TIMELOSS logic
private void checkGameOver() { 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(); GameResult res = board.getGameResult();
if (res != GameResult.ONGOING) { if (res != GameResult.ONGOING) {
String msg; String msg = switch(res) {
switch (res) { case WHITE_WINS -> "White wins!";
case WHITE_WINS: msg = "White wins!"; break; case BLACK_WINS -> "Black wins!";
case BLACK_WINS: msg = "Black wins!"; break; case DRAW -> "Draw!";
case DRAW: msg = "Draw!"; break; default -> "";
default: msg = ""; break; };
} clock.pause();
mjf.setStepBanner(msg); mjf.setStepBanner(msg);
gameOver = true; gameOver = true;
} }
} }
// //
} }

View File

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