new class
This commit is contained in:
parent
a1771230ce
commit
5b454d3a66
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 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<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 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;
|
||||
}
|
||||
}
|
||||
// ───────────────────────────────────────────────────────────────────────
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue