new class
This commit is contained in:
parent
a1771230ce
commit
5b454d3a66
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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.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 time‐loss or checkmate/stalemate happened
|
||||||
checkGameOver();
|
checkGameOver();
|
||||||
|
// 3) If it’s the AI’s 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 game‐end
|
||||||
|
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,39 +141,44 @@ 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 won’t 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 + TIME‐LOSS logic ────────────────────────────────────
|
||||||
private void checkGameOver() {
|
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();
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
@ -266,4 +275,11 @@ public class MyInterface extends JFrame {
|
||||||
this.setStepBanner("Turn : X");
|
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