From 5b454d3a6656d1ab759afc91b20beeb6d95aee71 Mon Sep 17 00:00:00 2001 From: Yash Shah Date: Thu, 22 May 2025 07:12:03 +0200 Subject: [PATCH] new class --- src/backend/Board.java | 5 +- src/backend/ChessClock.java | 100 +++++++++++++++++++++++ src/backend/Game.java | 114 ++++++++++++++++++--------- src/windowInterface/MyInterface.java | 16 ++++ 4 files changed, 196 insertions(+), 39 deletions(-) create mode 100644 src/backend/ChessClock.java diff --git a/src/backend/Board.java b/src/backend/Board.java index f458d5a..cea4227 100644 --- a/src/backend/Board.java +++ b/src/backend/Board.java @@ -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) { diff --git a/src/backend/ChessClock.java b/src/backend/ChessClock.java new file mode 100644 index 0000000..e975c25 --- /dev/null +++ b/src/backend/ChessClock.java @@ -0,0 +1,100 @@ +package backend; + +/** + * 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) + */ + 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 White’s 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 Black’s 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); + } +} diff --git a/src/backend/Game.java b/src/backend/Game.java index 6e889a2..93b90b7 100644 --- a/src/backend/Game.java +++ b/src/backend/Game.java @@ -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 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 time‐loss or checkmate/stalemate happened checkGameOver(); + // 3) If it’s the AI’s 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 game‐end + 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 getPieces() { - return board.getPieces(); - } + public Iterable 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 won’t 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 + TIME‐LOSS logic ──────────────────────────────────── private void checkGameOver() { + // 1) Time‐loss 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; } } // ─────────────────────────────────────────────────────────────────────── -} +} \ No newline at end of file diff --git a/src/windowInterface/MyInterface.java b/src/windowInterface/MyInterface.java index b7e2549..83f1637 100644 --- a/src/windowInterface/MyInterface.java +++ b/src/windowInterface/MyInterface.java @@ -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(); + } + }