final deposit
This commit is contained in:
commit
91da7823ad
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21">
|
||||
<attributes>
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>Project_2</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=21
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
|
||||
org.eclipse.jdt.core.compiler.release=enabled
|
||||
org.eclipse.jdt.core.compiler.source=21
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,9 @@
|
|||
BR,BN,BB,BQ,BK,BB,BN,BR
|
||||
BP,BP,BP,BP,BP,BP,BP,BP
|
||||
, , , , , , ,
|
||||
, , , , , , ,
|
||||
, , , , , , ,
|
||||
, , , , , , ,
|
||||
WP,WP,WP,WP,WP,WP,WP,WP
|
||||
WR,WN,WB,WQ,WK,WB,WN,WR
|
||||
W
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 954 B |
Binary file not shown.
|
|
@ -0,0 +1,19 @@
|
|||
import backend.Board;
|
||||
import backend.Move;
|
||||
import backend.Piece;
|
||||
import backend.PieceType;
|
||||
import windowInterface.MyInterface;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
// testing :
|
||||
Board testBoard = new Board(8, 8);
|
||||
testBoard.populateBoard();
|
||||
System.out.println(testBoard.toString());
|
||||
|
||||
// launches graphical interface :
|
||||
MyInterface mjf = new MyInterface();
|
||||
mjf.setVisible(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package backend;
|
||||
|
||||
public class AutoPlayer {
|
||||
|
||||
public Move computeBestMove(Board board) {
|
||||
// This will be implemented in Part 4
|
||||
// For now, just return null or a placeholder move
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,621 @@
|
|||
package backend;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class Board {
|
||||
private int width;
|
||||
private int height;
|
||||
private Piece[][] pieces; // 2D array to represent the chess board
|
||||
private int turnNumber;
|
||||
private boolean isWhiteTurn;
|
||||
private int selectedX;
|
||||
private int selectedY;
|
||||
private boolean hasSelection;
|
||||
private Set<Position> validMoves; // Set of valid positions for the selected piece
|
||||
private MoveHistory moveHistory; // Added move history
|
||||
|
||||
/**
|
||||
* Position class to store x,y coordinates and enable using them in HashSet
|
||||
*/
|
||||
private static class Position {
|
||||
private final int x;
|
||||
private final int y;
|
||||
|
||||
public Position(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
Position position = (Position) obj;
|
||||
return x == position.x && y == position.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * x + y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for creating a new chess board
|
||||
* @param width Width of the board
|
||||
* @param height Height of the board
|
||||
*/
|
||||
public Board(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.pieces = new Piece[width][height];
|
||||
this.turnNumber = 0;
|
||||
this.isWhiteTurn = true; // White goes first in chess
|
||||
this.hasSelection = false;
|
||||
this.selectedX = -1;
|
||||
this.selectedY = -1;
|
||||
this.validMoves = new HashSet<>();
|
||||
this.moveHistory = new MoveHistory(); // Initialize move history
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Width of the chess board
|
||||
*/
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Height of the chess board
|
||||
*/
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current turn number
|
||||
*/
|
||||
public int getTurnNumber() {
|
||||
return turnNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if it's white's turn, false if it's black's turn
|
||||
*/
|
||||
public boolean isTurnWhite() {
|
||||
return isWhiteTurn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Places a piece of specified type and color at the given position
|
||||
* @param isWhite True for white piece, false for black piece
|
||||
* @param type Type of piece (Pawn, Rook, etc.)
|
||||
* @param x X-coordinate
|
||||
* @param y Y-coordinate
|
||||
*/
|
||||
public void setPiece(boolean isWhite, PieceType type, int x, int y) {
|
||||
if (x >= 0 && x < width && y >= 0 && y < height) {
|
||||
pieces[x][y] = new Piece(x, y, type, isWhite);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the board with the standard chess starting position
|
||||
*/
|
||||
public void populateBoard() {
|
||||
// Place pawns
|
||||
for (int x = 0; x < width; x++) {
|
||||
setPiece(true, PieceType.Pawn, x, 6); // White pawns
|
||||
setPiece(false, PieceType.Pawn, x, 1); // Black pawns
|
||||
}
|
||||
|
||||
// Place Rooks
|
||||
setPiece(true, PieceType.Rook, 0, 7);
|
||||
setPiece(true, PieceType.Rook, 7, 7);
|
||||
setPiece(false, PieceType.Rook, 0, 0);
|
||||
setPiece(false, PieceType.Rook, 7, 0);
|
||||
|
||||
// Place Knights
|
||||
setPiece(true, PieceType.Knight, 1, 7);
|
||||
setPiece(true, PieceType.Knight, 6, 7);
|
||||
setPiece(false, PieceType.Knight, 1, 0);
|
||||
setPiece(false, PieceType.Knight, 6, 0);
|
||||
|
||||
// Place Bishops
|
||||
setPiece(true, PieceType.Bishop, 2, 7);
|
||||
setPiece(true, PieceType.Bishop, 5, 7);
|
||||
setPiece(false, PieceType.Bishop, 2, 0);
|
||||
setPiece(false, PieceType.Bishop, 5, 0);
|
||||
|
||||
// Place Queens
|
||||
setPiece(true, PieceType.Queen, 3, 7);
|
||||
setPiece(false, PieceType.Queen, 3, 0);
|
||||
|
||||
// Place Kings
|
||||
setPiece(true, PieceType.King, 4, 7);
|
||||
setPiece(false, PieceType.King, 4, 0);
|
||||
|
||||
// Reset turn information
|
||||
turnNumber = 0;
|
||||
isWhiteTurn = true;
|
||||
|
||||
// Clear selection and valid moves
|
||||
clearSelection();
|
||||
moveHistory.clear(); // Clear move history when starting a new game
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all pieces from the board
|
||||
*/
|
||||
public void cleanBoard() {
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
pieces[x][y] = null;
|
||||
}
|
||||
}
|
||||
turnNumber = 0;
|
||||
isWhiteTurn = true;
|
||||
clearSelection();
|
||||
moveHistory.clear(); // Clear move history when cleaning the board
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String representation of the board
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Turn: ").append(turnNumber).append(", ").append(isWhiteTurn ? "White" : "Black").append("\n");
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
sb.append(8 - y).append(" ");
|
||||
for (int x = 0; x < width; x++) {
|
||||
if (pieces[x][y] == null) {
|
||||
sb.append(((x + y) % 2 == 0) ? "." : " ");
|
||||
} else {
|
||||
Piece p = pieces[x][y];
|
||||
char c = p.getType().getSummary().charAt(0);
|
||||
sb.append(p.isWhite() ? Character.toUpperCase(c) : Character.toLowerCase(c));
|
||||
}
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.append(" a b c d e f g h");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List of all pieces currently on the board
|
||||
*/
|
||||
public ArrayList<Piece> getPieces() {
|
||||
ArrayList<Piece> piecesList = new ArrayList<>();
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
if (pieces[x][y] != null) {
|
||||
piecesList.add(pieces[x][y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return piecesList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles user clicks on the board
|
||||
* @param x X-coordinate
|
||||
* @param y Y-coordinate
|
||||
*/
|
||||
public void userTouch(int x, int y) {
|
||||
if (x < 0 || x >= width || y < 0 || y >= height) {
|
||||
return; // Out of bounds
|
||||
}
|
||||
|
||||
if (!hasSelection) {
|
||||
// If no piece is selected yet, select the piece at the clicked position
|
||||
if (pieces[x][y] != null && pieces[x][y].isWhite() == isWhiteTurn) {
|
||||
// Select the piece and calculate valid moves
|
||||
selectedX = x;
|
||||
selectedY = y;
|
||||
hasSelection = true;
|
||||
calculateValidMoves();
|
||||
}
|
||||
} else {
|
||||
// If a piece is already selected
|
||||
if (selectedX == x && selectedY == y) {
|
||||
// If the same position is clicked again, unselect it
|
||||
clearSelection();
|
||||
} else if (isHighlighted(x, y)) {
|
||||
// Move the selected piece to the new position if it's a valid move
|
||||
Piece selectedPiece = pieces[selectedX][selectedY];
|
||||
Piece capturedPiece = pieces[x][y]; // Store captured piece (null if no capture)
|
||||
|
||||
// Create a Move object to record this move
|
||||
Move move = new Move(selectedX, selectedY, x, y, selectedPiece, capturedPiece);
|
||||
|
||||
// Move the piece (captures any piece at destination)
|
||||
pieces[x][y] = selectedPiece;
|
||||
pieces[selectedX][selectedY] = null;
|
||||
|
||||
// Update the piece's position
|
||||
selectedPiece.setPosition(x, y);
|
||||
|
||||
// Record the move in history
|
||||
moveHistory.recordMove(move);
|
||||
|
||||
// Update turn information
|
||||
turnNumber++;
|
||||
isWhiteTurn = !isWhiteTurn;
|
||||
|
||||
// Reset selection and valid moves
|
||||
clearSelection();
|
||||
} else if (pieces[x][y] != null && pieces[x][y].isWhite() == isWhiteTurn) {
|
||||
// If clicked on another piece of the same color, select it instead
|
||||
selectedX = x;
|
||||
selectedY = y;
|
||||
hasSelection = true;
|
||||
calculateValidMoves();
|
||||
} else {
|
||||
// Clicked on an invalid position, unselect
|
||||
clearSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears selection and valid moves
|
||||
*/
|
||||
private void clearSelection() {
|
||||
hasSelection = false;
|
||||
selectedX = -1;
|
||||
selectedY = -1;
|
||||
validMoves.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates valid moves for the currently selected piece
|
||||
*/
|
||||
private void calculateValidMoves() {
|
||||
validMoves.clear();
|
||||
|
||||
if (!hasSelection) return;
|
||||
|
||||
Piece piece = pieces[selectedX][selectedY];
|
||||
if (piece == null) return;
|
||||
|
||||
switch (piece.getType()) {
|
||||
case Pawn:
|
||||
calculatePawnMoves(piece);
|
||||
break;
|
||||
case Rook:
|
||||
calculateRookMoves(piece);
|
||||
break;
|
||||
case Knight:
|
||||
calculateKnightMoves(piece);
|
||||
break;
|
||||
case Bishop:
|
||||
calculateBishopMoves(piece);
|
||||
break;
|
||||
case Queen:
|
||||
calculateQueenMoves(piece);
|
||||
break;
|
||||
case King:
|
||||
calculateKingMoves(piece);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate valid moves for a pawn
|
||||
* @param piece The pawn piece
|
||||
*/
|
||||
private void calculatePawnMoves(Piece piece) {
|
||||
int x = piece.getX();
|
||||
int y = piece.getY();
|
||||
boolean isWhite = piece.isWhite();
|
||||
|
||||
// Direction is -1 for white (moving up) and 1 for black (moving down)
|
||||
int direction = isWhite ? -1 : 1;
|
||||
int startingRow = isWhite ? 6 : 1;
|
||||
|
||||
// Move forward one square
|
||||
if (isInsideBoard(x, y + direction) && pieces[x][y + direction] == null) {
|
||||
validMoves.add(new Position(x, y + direction));
|
||||
|
||||
// Move forward two squares from starting position
|
||||
if (y == startingRow && isInsideBoard(x, y + 2 * direction) && pieces[x][y + 2 * direction] == null) {
|
||||
validMoves.add(new Position(x, y + 2 * direction));
|
||||
}
|
||||
}
|
||||
|
||||
// Capture diagonally
|
||||
for (int dx = -1; dx <= 1; dx += 2) {
|
||||
int newX = x + dx;
|
||||
int newY = y + direction;
|
||||
|
||||
if (isInsideBoard(newX, newY) && pieces[newX][newY] != null && pieces[newX][newY].isWhite() != isWhite) {
|
||||
validMoves.add(new Position(newX, newY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate valid moves for a rook
|
||||
* @param piece The rook piece
|
||||
*/
|
||||
private void calculateRookMoves(Piece piece) {
|
||||
int x = piece.getX();
|
||||
int y = piece.getY();
|
||||
boolean isWhite = piece.isWhite();
|
||||
|
||||
// Four directions: horizontal and vertical
|
||||
int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
|
||||
|
||||
for (int[] dir : directions) {
|
||||
int dx = dir[0];
|
||||
int dy = dir[1];
|
||||
|
||||
for (int step = 1; step < Math.max(width, height); step++) {
|
||||
int newX = x + dx * step;
|
||||
int newY = y + dy * step;
|
||||
|
||||
if (!isInsideBoard(newX, newY)) break; // Out of bounds
|
||||
|
||||
if (pieces[newX][newY] == null) {
|
||||
// Empty square, can move here
|
||||
validMoves.add(new Position(newX, newY));
|
||||
} else {
|
||||
// Square has a piece
|
||||
if (pieces[newX][newY].isWhite() != isWhite) {
|
||||
// Can capture opponent's piece
|
||||
validMoves.add(new Position(newX, newY));
|
||||
}
|
||||
break; // Can't move past a piece
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate valid moves for a knight
|
||||
* @param piece The knight piece
|
||||
*/
|
||||
private void calculateKnightMoves(Piece piece) {
|
||||
int x = piece.getX();
|
||||
int y = piece.getY();
|
||||
boolean isWhite = piece.isWhite();
|
||||
|
||||
// All possible knight moves
|
||||
int[][] moves = {
|
||||
{-2, -1}, {-2, 1}, {-1, -2}, {-1, 2},
|
||||
{1, -2}, {1, 2}, {2, -1}, {2, 1}
|
||||
};
|
||||
|
||||
for (int[] move : moves) {
|
||||
int newX = x + move[0];
|
||||
int newY = y + move[1];
|
||||
|
||||
if (isInsideBoard(newX, newY) && (pieces[newX][newY] == null || pieces[newX][newY].isWhite() != isWhite)) {
|
||||
validMoves.add(new Position(newX, newY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate valid moves for a bishop
|
||||
* @param piece The bishop piece
|
||||
*/
|
||||
private void calculateBishopMoves(Piece piece) {
|
||||
int x = piece.getX();
|
||||
int y = piece.getY();
|
||||
boolean isWhite = piece.isWhite();
|
||||
|
||||
// Four diagonal directions
|
||||
int[][] directions = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
|
||||
|
||||
for (int[] dir : directions) {
|
||||
int dx = dir[0];
|
||||
int dy = dir[1];
|
||||
|
||||
for (int step = 1; step < Math.max(width, height); step++) {
|
||||
int newX = x + dx * step;
|
||||
int newY = y + dy * step;
|
||||
|
||||
if (!isInsideBoard(newX, newY)) break; // Out of bounds
|
||||
|
||||
if (pieces[newX][newY] == null) {
|
||||
// Empty square, can move here
|
||||
validMoves.add(new Position(newX, newY));
|
||||
} else {
|
||||
// Square has a piece
|
||||
if (pieces[newX][newY].isWhite() != isWhite) {
|
||||
// Can capture opponent's piece
|
||||
validMoves.add(new Position(newX, newY));
|
||||
}
|
||||
break; // Can't move past a piece
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate valid moves for a queen
|
||||
* @param piece The queen piece
|
||||
*/
|
||||
private void calculateQueenMoves(Piece piece) {
|
||||
// Queen combines rook and bishop movement
|
||||
calculateRookMoves(piece);
|
||||
calculateBishopMoves(piece);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate valid moves for a king
|
||||
* @param piece The king piece
|
||||
*/
|
||||
private void calculateKingMoves(Piece piece) {
|
||||
int x = piece.getX();
|
||||
int y = piece.getY();
|
||||
boolean isWhite = piece.isWhite();
|
||||
|
||||
// All 8 directions around the king
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
for (int dy = -1; dy <= 1; dy++) {
|
||||
if (dx == 0 && dy == 0) continue; // Skip the current position
|
||||
|
||||
int newX = x + dx;
|
||||
int newY = y + dy;
|
||||
|
||||
if (isInsideBoard(newX, newY) && (pieces[newX][newY] == null || pieces[newX][newY].isWhite() != isWhite)) {
|
||||
validMoves.add(new Position(newX, newY));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if coordinates are inside the board
|
||||
* @param x X-coordinate
|
||||
* @param y Y-coordinate
|
||||
* @return True if inside board, false otherwise
|
||||
*/
|
||||
private boolean isInsideBoard(int x, int y) {
|
||||
return x >= 0 && x < width && y >= 0 && y < height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a position is currently selected
|
||||
* @param x X-coordinate
|
||||
* @param y Y-coordinate
|
||||
* @return True if the position is selected, false otherwise
|
||||
*/
|
||||
public boolean isSelected(int x, int y) {
|
||||
return hasSelection && selectedX == x && selectedY == y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a position is highlighted as a valid move
|
||||
* @param x X-coordinate
|
||||
* @param y Y-coordinate
|
||||
* @return True if the position is highlighted, false otherwise
|
||||
*/
|
||||
public boolean isHighlighted(int x, int y) {
|
||||
return validMoves.contains(new Position(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Undoes the last move made
|
||||
*/
|
||||
public void undoLastMove() {
|
||||
if (!moveHistory.canUndo()) {
|
||||
return; // No moves to undo
|
||||
}
|
||||
|
||||
// Get the last move
|
||||
Move lastMove = moveHistory.getLastMove();
|
||||
|
||||
// Get the moving piece (which is now at the destination)
|
||||
Piece movingPiece = pieces[lastMove.getToX()][lastMove.getToY()];
|
||||
|
||||
if (movingPiece == null) {
|
||||
// Something went wrong, the piece isn't where it should be
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the piece back to its original position
|
||||
pieces[lastMove.getFromX()][lastMove.getFromY()] = movingPiece;
|
||||
movingPiece.setPosition(lastMove.getFromX(), lastMove.getFromY());
|
||||
|
||||
// If a piece was captured, put it back
|
||||
if (lastMove.getCapturedPiece() != null) {
|
||||
Piece capturedPiece = lastMove.getCapturedPiece();
|
||||
pieces[lastMove.getToX()][lastMove.getToY()] = capturedPiece;
|
||||
} else {
|
||||
// Otherwise, clear the destination square
|
||||
pieces[lastMove.getToX()][lastMove.getToY()] = null;
|
||||
}
|
||||
|
||||
// Revert turn information
|
||||
turnNumber--;
|
||||
isWhiteTurn = !isWhiteTurn;
|
||||
|
||||
// Clear any selection and highlighting
|
||||
clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for loading a board from a file
|
||||
* @param array Array of strings representing the board
|
||||
*/
|
||||
public Board(String[] array) {
|
||||
this(8, 8); // Default to standard 8x8 board
|
||||
// The rest will be implemented in part 3
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for copying a board
|
||||
* @param board Board to copy
|
||||
*/
|
||||
public Board(Board board) {
|
||||
this(board.getWidth(), board.getHeight());
|
||||
|
||||
this.turnNumber = board.getTurnNumber();
|
||||
this.isWhiteTurn = board.isTurnWhite();
|
||||
|
||||
// Copy pieces
|
||||
for (Piece piece : board.getPieces()) {
|
||||
this.setPiece(piece.isWhite(), piece.getType(), piece.getX(), piece.getY());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Placeholder for saving the board to a file
|
||||
* @return Array of strings representing the board
|
||||
*/
|
||||
public String[] toFileRep() {
|
||||
// This will be implemented in part 3
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a move on the board
|
||||
* @param move Move to play
|
||||
*/
|
||||
public void playMove(Move move) {
|
||||
if (move == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int fromX = move.getFromX();
|
||||
int fromY = move.getFromY();
|
||||
int toX = move.getToX();
|
||||
int toY = move.getToY();
|
||||
|
||||
// Make sure there's a piece at the starting position
|
||||
if (pieces[fromX][fromY] == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the piece that might be captured
|
||||
Piece capturedPiece = pieces[toX][toY];
|
||||
|
||||
// Update the move object with the actual pieces
|
||||
move = new Move(fromX, fromY, toX, toY, pieces[fromX][fromY], capturedPiece);
|
||||
|
||||
// Make the move
|
||||
pieces[toX][toY] = pieces[fromX][fromY];
|
||||
pieces[fromX][fromY] = null;
|
||||
|
||||
// Update the piece's position
|
||||
pieces[toX][toY].setPosition(toX, toY);
|
||||
|
||||
// Record the move
|
||||
moveHistory.recordMove(move);
|
||||
|
||||
// Update turn information
|
||||
turnNumber++;
|
||||
isWhiteTurn = !isWhiteTurn;
|
||||
|
||||
// Clear any selection
|
||||
clearSelection();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
package backend;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class ChessTimer {
|
||||
private long whiteTimeMillis; // Time remaining for white player in milliseconds
|
||||
private long blackTimeMillis; // Time remaining for black player in milliseconds
|
||||
private long lastUpdateTime; // Time when the timer was last updated
|
||||
private boolean isRunning; // Is the timer currently running?
|
||||
private boolean isWhiteTurn; // Is it white's turn?
|
||||
private Timer timer; // Java util timer to handle periodic updates
|
||||
private TimerUpdateListener listener; // Listener to notify UI when time changes
|
||||
|
||||
/**
|
||||
* Interface for notifying time updates
|
||||
*/
|
||||
public interface TimerUpdateListener {
|
||||
void onTimeUpdate(long whiteTimeMillis, long blackTimeMillis);
|
||||
void onTimeExpired(boolean isWhiteExpired); // Called when a player's time runs out
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with initial time in minutes
|
||||
* @param initialTimeMinutes Initial time for both players in minutes
|
||||
* @param listener Listener to receive time updates
|
||||
*/
|
||||
public ChessTimer(int initialTimeMinutes, TimerUpdateListener listener) {
|
||||
this.whiteTimeMillis = initialTimeMinutes * 60 * 1000;
|
||||
this.blackTimeMillis = initialTimeMinutes * 60 * 1000;
|
||||
this.isRunning = false;
|
||||
this.isWhiteTurn = true; // White starts in chess
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the timer
|
||||
*/
|
||||
public void start() {
|
||||
if (!isRunning) {
|
||||
isRunning = true;
|
||||
lastUpdateTime = System.currentTimeMillis();
|
||||
|
||||
// Schedule periodic updates (every 100ms)
|
||||
timer = new Timer(true);
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateTime();
|
||||
}
|
||||
}, 0, 100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the timer
|
||||
*/
|
||||
public void stop() {
|
||||
if (isRunning) {
|
||||
isRunning = false;
|
||||
updateTime(); // Update one last time
|
||||
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the timer to the initial values
|
||||
* @param initialTimeMinutes New initial time in minutes
|
||||
*/
|
||||
public void reset(int initialTimeMinutes) {
|
||||
stop();
|
||||
this.whiteTimeMillis = initialTimeMinutes * 60 * 1000;
|
||||
this.blackTimeMillis = initialTimeMinutes * 60 * 1000;
|
||||
this.isWhiteTurn = true;
|
||||
notifyTimeUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch the active player
|
||||
* @param isWhiteTurn True if it's now white's turn
|
||||
*/
|
||||
public void switchTurn(boolean isWhiteTurn) {
|
||||
if (isRunning) {
|
||||
updateTime(); // Update time for the player who just completed their move
|
||||
}
|
||||
this.isWhiteTurn = isWhiteTurn;
|
||||
notifyTimeUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the time based on elapsed time
|
||||
*/
|
||||
private void updateTime() {
|
||||
if (!isRunning) return;
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long elapsedTime = currentTime - lastUpdateTime;
|
||||
lastUpdateTime = currentTime;
|
||||
|
||||
// Deduct time from the active player
|
||||
if (isWhiteTurn) {
|
||||
whiteTimeMillis -= elapsedTime;
|
||||
if (whiteTimeMillis <= 0) {
|
||||
whiteTimeMillis = 0;
|
||||
stop();
|
||||
if (listener != null) {
|
||||
listener.onTimeExpired(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
blackTimeMillis -= elapsedTime;
|
||||
if (blackTimeMillis <= 0) {
|
||||
blackTimeMillis = 0;
|
||||
stop();
|
||||
if (listener != null) {
|
||||
listener.onTimeExpired(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyTimeUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the listener about time updates
|
||||
*/
|
||||
private void notifyTimeUpdate() {
|
||||
if (listener != null) {
|
||||
listener.onTimeUpdate(whiteTimeMillis, blackTimeMillis);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get white player's remaining time in milliseconds
|
||||
*/
|
||||
public long getWhiteTimeMillis() {
|
||||
return whiteTimeMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get black player's remaining time in milliseconds
|
||||
*/
|
||||
public long getBlackTimeMillis() {
|
||||
return blackTimeMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format milliseconds as "mm:ss"
|
||||
*/
|
||||
public static String formatTime(long timeMillis) {
|
||||
int seconds = (int) (timeMillis / 1000) % 60;
|
||||
int minutes = (int) (timeMillis / (60 * 1000));
|
||||
return String.format("%02d:%02d", minutes, seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the timer is currently running
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return isRunning;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
package backend;
|
||||
|
||||
import windowInterface.MyInterface;
|
||||
|
||||
public class Game extends Thread {
|
||||
|
||||
private AutoPlayer aiPlayer;
|
||||
private Board board;
|
||||
private ChessTimer chessTimer; // Add chess timer
|
||||
private MyInterface mjf;
|
||||
private int COL_NUM = 8;
|
||||
private int LINE_NUM = 8;
|
||||
private int loopDelay = 250;
|
||||
boolean[] activationAIFlags;
|
||||
private int initialTimeMinutes = 10; // Default to 10 minutes per player
|
||||
private boolean timerEnabled = false; // Flag to enable/disable timer
|
||||
|
||||
public Game(MyInterface mjfParam) {
|
||||
mjf = mjfParam;
|
||||
board = new Board(COL_NUM, LINE_NUM);
|
||||
loopDelay = 250;
|
||||
LINE_NUM = 8;
|
||||
COL_NUM = 8;
|
||||
activationAIFlags = new boolean[2];
|
||||
aiPlayer = new AutoPlayer();
|
||||
|
||||
// Initialize the timer with a listener
|
||||
chessTimer = new ChessTimer(initialTimeMinutes, new ChessTimer.TimerUpdateListener() {
|
||||
@Override
|
||||
public void onTimeUpdate(long whiteTimeMillis, long blackTimeMillis) {
|
||||
// Update UI with new time values
|
||||
mjf.updateTimers(whiteTimeMillis, blackTimeMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeExpired(boolean isWhiteExpired) {
|
||||
// Handle time expiration - game over
|
||||
mjf.timeExpired(isWhiteExpired);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return board.getWidth();
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return board.getHeight();
|
||||
}
|
||||
|
||||
// Added getBoard method to access the board
|
||||
public Board getBoard() {
|
||||
return this.board;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while(true) {
|
||||
aiPlayerTurn();
|
||||
mjf.update(board.getTurnNumber(), board.isTurnWhite());
|
||||
try {
|
||||
Thread.sleep(loopDelay);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAITurn() {
|
||||
return activationAIFlags[board.isTurnWhite()?1:0];
|
||||
}
|
||||
|
||||
private void aiPlayerTurn() {
|
||||
if(isAITurn()) {
|
||||
// Before AI makes a move
|
||||
boolean currentTurn = board.isTurnWhite();
|
||||
|
||||
board.playMove(aiPlayer.computeBestMove(new Board(board)));
|
||||
|
||||
// After AI makes a move, switch the timer
|
||||
if (timerEnabled && currentTurn != board.isTurnWhite()) {
|
||||
chessTimer.switchTurn(board.isTurnWhite());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clickCoords(int x, int y) {
|
||||
int width = this.getWidth();
|
||||
int height = this.getHeight();
|
||||
if(0>x || 0>y || x>width || y>height) {
|
||||
System.out.println("Click out of bounds");
|
||||
return;
|
||||
}
|
||||
if(!isAITurn()) {
|
||||
// Before user makes a move
|
||||
boolean currentTurn = board.isTurnWhite();
|
||||
|
||||
board.userTouch(x, y);
|
||||
|
||||
// After user makes a move, check if turn switched and update timer
|
||||
if (timerEnabled && currentTurn != board.isTurnWhite()) {
|
||||
chessTimer.switchTurn(board.isTurnWhite());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setPiece(boolean isWhite, PieceType type, int x, int y) {
|
||||
board.setPiece(isWhite, type, x, y);
|
||||
}
|
||||
|
||||
public String[] getFileRepresentation() {
|
||||
return board.toFileRep();
|
||||
}
|
||||
|
||||
public void setLoopDelay(int delay) {
|
||||
this.loopDelay = delay;
|
||||
}
|
||||
|
||||
public void setDefaultSetup() {
|
||||
board.cleanBoard();
|
||||
board.populateBoard();
|
||||
|
||||
// Reset the timer when starting a new game
|
||||
if (timerEnabled) {
|
||||
chessTimer.reset(initialTimeMinutes);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoard(String[] array) {
|
||||
board = new Board(array);
|
||||
|
||||
// Reset the timer when loading a new board
|
||||
if (timerEnabled) {
|
||||
chessTimer.reset(initialTimeMinutes);
|
||||
}
|
||||
}
|
||||
|
||||
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 void undoLastMove() {
|
||||
// Store current turn before undo
|
||||
boolean currentTurn = board.isTurnWhite();
|
||||
|
||||
board.undoLastMove();
|
||||
|
||||
// After undo, check if turn switched and update timer
|
||||
if (timerEnabled && currentTurn != board.isTurnWhite()) {
|
||||
chessTimer.switchTurn(board.isTurnWhite());
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleAI(boolean isWhite) {
|
||||
this.activationAIFlags[isWhite?1:0] = !this.activationAIFlags[isWhite?1:0];
|
||||
}
|
||||
|
||||
// Timer-related methods
|
||||
|
||||
/**
|
||||
* Start the chess timer
|
||||
*/
|
||||
public void startTimer() {
|
||||
if (timerEnabled) {
|
||||
chessTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the chess timer
|
||||
*/
|
||||
public void stopTimer() {
|
||||
if (timerEnabled) {
|
||||
chessTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the timer feature
|
||||
*/
|
||||
public void setTimerEnabled(boolean enabled) {
|
||||
this.timerEnabled = enabled;
|
||||
if (enabled) {
|
||||
chessTimer.reset(initialTimeMinutes);
|
||||
} else {
|
||||
chessTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if timer is enabled
|
||||
*/
|
||||
public boolean isTimerEnabled() {
|
||||
return timerEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the initial time in minutes for both players
|
||||
*/
|
||||
public void setInitialTime(int minutes) {
|
||||
this.initialTimeMinutes = minutes;
|
||||
if (timerEnabled) {
|
||||
chessTimer.reset(minutes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current chess timer
|
||||
*/
|
||||
public ChessTimer getChessTimer() {
|
||||
return chessTimer;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package backend;
|
||||
|
||||
public class Move {
|
||||
private int fromX;
|
||||
private int fromY;
|
||||
private int toX;
|
||||
private int toY;
|
||||
private Piece movingPiece;
|
||||
private Piece capturedPiece;
|
||||
|
||||
/**
|
||||
* Constructor for a move
|
||||
* @param fromX Starting X-coordinate
|
||||
* @param fromY Starting Y-coordinate
|
||||
* @param toX Destination X-coordinate
|
||||
* @param toY Destination Y-coordinate
|
||||
* @param movingPiece The piece being moved
|
||||
* @param capturedPiece The piece being captured (null if no capture)
|
||||
*/
|
||||
public Move(int fromX, int fromY, int toX, int toY, Piece movingPiece, Piece capturedPiece) {
|
||||
this.fromX = fromX;
|
||||
this.fromY = fromY;
|
||||
this.toX = toX;
|
||||
this.toY = toY;
|
||||
this.movingPiece = movingPiece;
|
||||
this.capturedPiece = capturedPiece;
|
||||
}
|
||||
|
||||
// Basic constructor for now - can be expanded later
|
||||
public Move() {
|
||||
this.fromX = -1;
|
||||
this.fromY = -1;
|
||||
this.toX = -1;
|
||||
this.toY = -1;
|
||||
this.movingPiece = null;
|
||||
this.capturedPiece = null;
|
||||
}
|
||||
|
||||
// Getters
|
||||
public int getFromX() { return fromX; }
|
||||
public int getFromY() { return fromY; }
|
||||
public int getToX() { return toX; }
|
||||
public int getToY() { return toY; }
|
||||
public Piece getMovingPiece() { return movingPiece; }
|
||||
public Piece getCapturedPiece() { return capturedPiece; }
|
||||
|
||||
// Setters if needed
|
||||
public void setFromX(int fromX) { this.fromX = fromX; }
|
||||
public void setFromY(int fromY) { this.fromY = fromY; }
|
||||
public void setToX(int toX) { this.toX = toX; }
|
||||
public void setToY(int toY) { this.toY = toY; }
|
||||
public void setMovingPiece(Piece movingPiece) { this.movingPiece = movingPiece; }
|
||||
public void setCapturedPiece(Piece capturedPiece) { this.capturedPiece = capturedPiece; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (movingPiece == null) {
|
||||
return "Empty move";
|
||||
}
|
||||
String from = (char)('a' + fromX) + "" + (8 - fromY);
|
||||
String to = (char)('a' + toX) + "" + (8 - toY);
|
||||
return movingPiece.getType().getSummary() + from + "-" + to;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package backend;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Class to maintain a history of moves made during the game
|
||||
* and enable undo functionality
|
||||
*/
|
||||
public class MoveHistory {
|
||||
private Stack<Move> moveStack;
|
||||
|
||||
/**
|
||||
* Constructor for MoveHistory
|
||||
*/
|
||||
public MoveHistory() {
|
||||
moveStack = new Stack<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Records a move in the history
|
||||
*
|
||||
* @param move The move to record
|
||||
*/
|
||||
public void recordMove(Move move) {
|
||||
moveStack.push(move);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last move made and removes it from history
|
||||
*
|
||||
* @return The last move made, or null if no moves have been made
|
||||
*/
|
||||
public Move getLastMove() {
|
||||
if (moveStack.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return moveStack.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are moves to undo
|
||||
*
|
||||
* @return True if there are moves in the history, false otherwise
|
||||
*/
|
||||
public boolean canUndo() {
|
||||
return !moveStack.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the move history
|
||||
*/
|
||||
public void clear() {
|
||||
moveStack.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package backend;
|
||||
|
||||
public class Piece {
|
||||
private int x;
|
||||
private int y;
|
||||
private PieceType type;
|
||||
private boolean isWhite;
|
||||
|
||||
public Piece(int x, int y, PieceType type, boolean isWhite) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.type = type;
|
||||
this.isWhite = isWhite;
|
||||
}
|
||||
|
||||
public PieceType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isWhite() {
|
||||
return isWhite;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setPosition(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package backend;
|
||||
|
||||
public enum PieceType {
|
||||
Pawn("Pawn", "p"),
|
||||
Rook("Rook", "r"),
|
||||
Knight("Knight", "n"),
|
||||
Bishop("Bishop", "b"),
|
||||
Queen("Queen", "q"),
|
||||
King("King", "k");
|
||||
|
||||
private final String name;
|
||||
private final String summary;
|
||||
|
||||
PieceType(String name, String summary) {
|
||||
this.name = name;
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return summary;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
package windowInterface;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Image;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import backend.Game;
|
||||
import backend.Piece;
|
||||
import backend.PieceType;
|
||||
|
||||
public class JPanelChessBoard extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private Game myGame;
|
||||
private MyInterface interfaceGlobal;
|
||||
private BufferedImage spriteSheet;
|
||||
private int PIECE_WIDTH = 16; //in spritesheet
|
||||
private int PIECE_HEIGHT = 16; //in spritesheet
|
||||
private int MARGIN = 6;
|
||||
|
||||
private boolean pieceSelectorMode;
|
||||
private boolean selectedPieceIsWhite;
|
||||
private PieceType selectedPieceType;
|
||||
private boolean pieceAdderMode;
|
||||
|
||||
public JPanelChessBoard(MyInterface itf) {
|
||||
super();
|
||||
myGame = null;
|
||||
interfaceGlobal = itf;
|
||||
selectedPieceIsWhite = true;
|
||||
selectedPieceType = PieceType.Pawn;
|
||||
pieceSelectorMode = false;
|
||||
try {
|
||||
spriteSheet = ImageIO.read(new File("pieces.png"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
pieceSelectorMode = false;
|
||||
pieceAdderMode = false;
|
||||
addMouseListener(new MouseAdapter() {
|
||||
public void mousePressed(MouseEvent me) {
|
||||
// System.out.println(me);
|
||||
if(pieceSelectorMode) {
|
||||
int x = Math.round(me.getX()/cellWidth());
|
||||
selectedPieceType = PieceType.values()[5-x];
|
||||
selectedPieceIsWhite = (me.getY() > cellHeight());
|
||||
pieceSelectorMode = false;
|
||||
} else {
|
||||
if(myGame == null) {
|
||||
interfaceGlobal.instantiateSimu();
|
||||
}
|
||||
int x = (me.getX()*myGame.getWidth())/getWidth();
|
||||
int y = (me.getY()*myGame.getHeight())/getHeight();
|
||||
if(pieceAdderMode) {
|
||||
//TODO
|
||||
myGame.setPiece(selectedPieceIsWhite,selectedPieceType, x, y);
|
||||
pieceAdderMode = false;
|
||||
} else {
|
||||
myGame.clickCoords(x,y);
|
||||
}
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void setGame(Game simu) {
|
||||
myGame = simu;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
this.setBackground(Color.black);
|
||||
if(pieceSelectorMode) {
|
||||
g.drawImage(
|
||||
spriteSheet,
|
||||
0,
|
||||
0,
|
||||
Math.round(5*cellWidth()),
|
||||
Math.round(2*cellHeight()),
|
||||
null
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (myGame != null) {
|
||||
// Draw Interface from state of simulator
|
||||
float cellWidth = cellWidth();
|
||||
float cellHeight = cellHeight();
|
||||
|
||||
g.setColor(Color.white);
|
||||
for(int x=0; x<myGame.getWidth();x++) {
|
||||
for (int y=0; y<myGame.getHeight(); y++) {
|
||||
boolean isSelect = myGame.isSelected(x,y);
|
||||
boolean isHighlight = myGame.isHighlighted(x,y);
|
||||
if(isSelect) {
|
||||
g.setColor(Color.blue);
|
||||
}
|
||||
if(isHighlight) {
|
||||
g.setColor(Color.yellow);
|
||||
}
|
||||
if((x+y)%2==1 || isSelect || isHighlight) {
|
||||
g.fillRect(
|
||||
Math.round(x*cellWidth),
|
||||
Math.round(y*cellHeight),
|
||||
Math.round(cellWidth),
|
||||
Math.round(cellHeight)
|
||||
);
|
||||
}
|
||||
if(isHighlight || isSelect) {
|
||||
g.setColor(Color.white);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
g.setColor(Color.gray);
|
||||
for(int x=0; x<myGame.getWidth();x++) {
|
||||
int graphX = Math.round(x*cellWidth);
|
||||
g.drawLine(graphX, 0, graphX, this.getHeight());
|
||||
}
|
||||
for (int y=0; y<myGame.getHeight(); y++) {
|
||||
int graphY = Math.round(y*cellHeight);
|
||||
g.drawLine(0, graphY, this.getWidth(), graphY);
|
||||
}
|
||||
|
||||
for (Piece piece : myGame.getPieces()) {
|
||||
drawPiece(g,piece);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawPiece(Graphics g, Piece piece) {
|
||||
g.drawImage(
|
||||
getChessPieceImageFromType(piece.getType(), piece.isWhite()),
|
||||
MARGIN+(xCoordFromGame(piece.getX())),
|
||||
MARGIN+(yCoordFromGame(piece.getY())),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private Image getChessPieceImageFromType(PieceType type, boolean isWhite) {
|
||||
int x = spriteSheetPositionOfPieceType(type)*PIECE_WIDTH;
|
||||
int y = PIECE_HEIGHT * (isWhite?1:0);
|
||||
Image subImage = spriteSheet.getSubimage(x, y, PIECE_WIDTH, PIECE_HEIGHT);
|
||||
return subImage.getScaledInstance(
|
||||
Math.round(cellWidth())-2*MARGIN,
|
||||
Math.round(cellHeight())-2*MARGIN, 0
|
||||
);
|
||||
}
|
||||
|
||||
private int spriteSheetPositionOfPieceType(PieceType type) {
|
||||
return 5-type.ordinal();
|
||||
}
|
||||
|
||||
private float cellWidth() {
|
||||
return (float) this.getWidth()/ (float)myGame.getWidth();
|
||||
}
|
||||
private float cellHeight() {
|
||||
return (float)this.getHeight()/ (float)myGame.getHeight();
|
||||
}
|
||||
private int xCoordFromGame(int x) {
|
||||
return Math.round(x*cellWidth());
|
||||
}
|
||||
private int yCoordFromGame(int y) {
|
||||
return Math.round(y*cellHeight());
|
||||
}
|
||||
|
||||
public void togglePieceSelector() {
|
||||
pieceSelectorMode = ! pieceSelectorMode;
|
||||
}
|
||||
|
||||
public void toggleAdderMode() {
|
||||
pieceAdderMode = ! pieceAdderMode;
|
||||
}
|
||||
|
||||
public boolean isPieceSelectorMode() {
|
||||
return pieceSelectorMode;
|
||||
}
|
||||
|
||||
|
||||
public boolean isPieceAdderMode() {
|
||||
return pieceAdderMode;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,387 @@
|
|||
package windowInterface;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import backend.ChessTimer;
|
||||
import backend.Game;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.awt.event.ActionEvent;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.AbstractListModel;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JCheckBox;
|
||||
|
||||
public class MyInterface extends JFrame {
|
||||
|
||||
private static final long serialVersionUID = -6840815447618468846L;
|
||||
private JPanel contentPane;
|
||||
private JLabel turnLabel;
|
||||
private JLabel borderLabel;
|
||||
private JLabel speedLabel;
|
||||
private JPanelChessBoard panelDraw;
|
||||
private Game game;
|
||||
private JLabel actionLabel;
|
||||
private JCheckBox chckbxBlackAI;
|
||||
private JCheckBox chckbxWhiteAI;
|
||||
|
||||
// Timer-related components
|
||||
private JLabel whiteTimerLabel;
|
||||
private JLabel blackTimerLabel;
|
||||
private JCheckBox chckbxEnableTimer;
|
||||
private JSpinner timeSpinner;
|
||||
|
||||
/**
|
||||
* Create the frame.
|
||||
*/
|
||||
public MyInterface() {
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setBounds(10, 10, 650, 650);
|
||||
contentPane = new JPanel();
|
||||
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
contentPane.setLayout(new BorderLayout(0, 0));
|
||||
setContentPane(contentPane);
|
||||
|
||||
JPanel panelTop = new JPanel();
|
||||
contentPane.add(panelTop, BorderLayout.NORTH);
|
||||
JPanel panelRight = new JPanel();
|
||||
contentPane.add(panelRight, BorderLayout.EAST);
|
||||
panelRight.setLayout(new GridLayout(7, 1, 0, 5)); // Increased number of rows for timer controls
|
||||
|
||||
// Timer panel at the bottom
|
||||
JPanel timerPanel = new JPanel();
|
||||
timerPanel.setLayout(new GridLayout(2, 2, 5, 5));
|
||||
contentPane.add(timerPanel, BorderLayout.SOUTH);
|
||||
|
||||
// White timer
|
||||
JPanel whiteTimerPanel = new JPanel();
|
||||
whiteTimerLabel = new JLabel("10:00");
|
||||
whiteTimerLabel.setFont(new Font("Monospaced", Font.BOLD, 20));
|
||||
whiteTimerLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
whiteTimerPanel.add(new JLabel("White: "));
|
||||
whiteTimerPanel.add(whiteTimerLabel);
|
||||
timerPanel.add(whiteTimerPanel);
|
||||
|
||||
// Black timer
|
||||
JPanel blackTimerPanel = new JPanel();
|
||||
blackTimerLabel = new JLabel("10:00");
|
||||
blackTimerLabel.setFont(new Font("Monospaced", Font.BOLD, 20));
|
||||
blackTimerLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
blackTimerPanel.add(new JLabel("Black: "));
|
||||
blackTimerPanel.add(blackTimerLabel);
|
||||
timerPanel.add(blackTimerPanel);
|
||||
|
||||
// Timer controls
|
||||
JPanel timerControls = new JPanel();
|
||||
timerControls.setLayout(new FlowLayout());
|
||||
|
||||
chckbxEnableTimer = new JCheckBox("Enable Timer");
|
||||
chckbxEnableTimer.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
toggleTimer();
|
||||
}
|
||||
});
|
||||
timerControls.add(chckbxEnableTimer);
|
||||
|
||||
timerControls.add(new JLabel("Minutes:"));
|
||||
timeSpinner = new JSpinner(new SpinnerNumberModel(10, 1, 60, 1));
|
||||
timeSpinner.addChangeListener(new ChangeListener() {
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
updateInitialTime();
|
||||
}
|
||||
});
|
||||
timerControls.add(timeSpinner);
|
||||
|
||||
timerPanel.add(timerControls);
|
||||
|
||||
actionLabel = new JLabel("Waiting For Start");
|
||||
panelTop.add(actionLabel);
|
||||
|
||||
JButton btnGo = new JButton("Start/Restart");
|
||||
btnGo.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
clicButtonStart();
|
||||
}
|
||||
});
|
||||
panelTop.add(btnGo);
|
||||
|
||||
turnLabel = new JLabel("Turn : X");
|
||||
panelTop.add(turnLabel);
|
||||
|
||||
JButton btnLoad = new JButton("Load File");
|
||||
btnLoad.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
clicLoadFileButton();
|
||||
}
|
||||
});
|
||||
panelRight.add(btnLoad);
|
||||
|
||||
JButton btnSave = new JButton("Save To File");
|
||||
btnSave.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
clicSaveToFileButton();
|
||||
}
|
||||
});
|
||||
panelRight.add(btnSave);
|
||||
|
||||
JButton btnAdder = new JButton("Add Piece");
|
||||
btnAdder.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
clickButtonAdder();
|
||||
}
|
||||
});
|
||||
panelRight.add(btnAdder);
|
||||
|
||||
JButton btnPieceSelector = new JButton("Piece Select");
|
||||
btnPieceSelector.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
clickButtonSelector();
|
||||
}
|
||||
});
|
||||
panelRight.add(btnPieceSelector);
|
||||
|
||||
JButton btnUndo = new JButton("Undo");
|
||||
btnUndo.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
clicUndoButton();
|
||||
}
|
||||
|
||||
});
|
||||
panelTop.add(btnUndo);
|
||||
|
||||
chckbxWhiteAI = new JCheckBox("WhiteAI");
|
||||
chckbxWhiteAI.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
clicAIToggle(true);
|
||||
}
|
||||
|
||||
});
|
||||
panelTop.add(chckbxWhiteAI);
|
||||
|
||||
chckbxBlackAI = new JCheckBox("BlackAI");
|
||||
chckbxBlackAI.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
clicAIToggle(false);
|
||||
}
|
||||
|
||||
});
|
||||
panelTop.add(chckbxBlackAI);
|
||||
|
||||
panelDraw = new JPanelChessBoard(this);
|
||||
contentPane.add(panelDraw, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
public void setStepBanner(String s) {
|
||||
turnLabel.setText(s);
|
||||
}
|
||||
|
||||
public void setBorderBanner(String s) {
|
||||
borderLabel.setText(s);
|
||||
}
|
||||
|
||||
public JPanelChessBoard getPanelDessin() {
|
||||
return panelDraw;
|
||||
}
|
||||
|
||||
public void instantiateSimu() {
|
||||
if(game==null) {
|
||||
game = new Game(this);
|
||||
panelDraw.setGame(game);
|
||||
game.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void clicButtonStart() {
|
||||
this.instantiateSimu();
|
||||
game.setDefaultSetup();
|
||||
|
||||
// Reset and start the timer if enabled
|
||||
if (game.isTimerEnabled()) {
|
||||
game.startTimer();
|
||||
}
|
||||
}
|
||||
|
||||
public void clickButtonAdder() {
|
||||
panelDraw.toggleAdderMode();
|
||||
}
|
||||
public void clickButtonSelector() {
|
||||
panelDraw.togglePieceSelector();
|
||||
}
|
||||
|
||||
private void clicUndoButton() {
|
||||
if(game == null) {
|
||||
System.out.println("error : can't undo while no game present");
|
||||
} else {
|
||||
game.undoLastMove();
|
||||
}
|
||||
}
|
||||
|
||||
public void clicAIToggle(boolean isWhite) {
|
||||
if(game == null) {
|
||||
System.out.println("error : can't activate AI while no game present");
|
||||
if(isWhite) {
|
||||
chckbxWhiteAI.setSelected(false);
|
||||
}else {
|
||||
chckbxBlackAI.setSelected(false);
|
||||
}
|
||||
} else {
|
||||
game.toggleAI(isWhite);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle timer on/off
|
||||
*/
|
||||
private void toggleTimer() {
|
||||
if (game == null) {
|
||||
System.out.println("error : can't toggle timer while no game present");
|
||||
chckbxEnableTimer.setSelected(false);
|
||||
} else {
|
||||
boolean isEnabled = chckbxEnableTimer.isSelected();
|
||||
game.setTimerEnabled(isEnabled);
|
||||
|
||||
if (isEnabled) {
|
||||
game.startTimer();
|
||||
} else {
|
||||
game.stopTimer();
|
||||
|
||||
// Reset the displayed time
|
||||
updateTimers(
|
||||
((Integer)timeSpinner.getValue()) * 60 * 1000,
|
||||
((Integer)timeSpinner.getValue()) * 60 * 1000
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update initial time when spinner changes
|
||||
*/
|
||||
private void updateInitialTime() {
|
||||
if (game != null) {
|
||||
int minutes = (Integer)timeSpinner.getValue();
|
||||
game.setInitialTime(minutes);
|
||||
|
||||
// Update timer labels
|
||||
long timeMillis = minutes * 60 * 1000;
|
||||
whiteTimerLabel.setText(ChessTimer.formatTime(timeMillis));
|
||||
blackTimerLabel.setText(ChessTimer.formatTime(timeMillis));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update timer display
|
||||
*/
|
||||
public void updateTimers(long whiteTimeMillis, long blackTimeMillis) {
|
||||
whiteTimerLabel.setText(ChessTimer.formatTime(whiteTimeMillis));
|
||||
blackTimerLabel.setText(ChessTimer.formatTime(blackTimeMillis));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle time expiration
|
||||
*/
|
||||
public void timeExpired(boolean isWhiteExpired) {
|
||||
String message = (isWhiteExpired ? "White" : "Black") + " player's time has expired. " +
|
||||
(!isWhiteExpired ? "White" : "Black") + " wins!";
|
||||
JOptionPane.showMessageDialog(this, message, "Game Over", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
|
||||
public void clicLoadFileButton() {
|
||||
Game loadedSim = new Game(this);
|
||||
String fileName=SelectFile();
|
||||
LinkedList<String> lines = new LinkedList<String>();
|
||||
if (fileName.length()>0) {
|
||||
try {
|
||||
BufferedReader fileContent = new BufferedReader(new FileReader(fileName));
|
||||
String line = fileContent.readLine();
|
||||
int colorID = 0;
|
||||
while (line != null) {
|
||||
lines.add(line);
|
||||
line = fileContent.readLine();
|
||||
}
|
||||
loadedSim.setBoard(Arrays.stream(lines.toArray()).map(Object::toString).toArray(String[]::new));
|
||||
fileContent.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
game = loadedSim;
|
||||
panelDraw.setGame(game);
|
||||
this.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public void clicSaveToFileButton() {
|
||||
String fileName=SelectFile();
|
||||
if (fileName.length()>0) {
|
||||
String[] content = game.getFileRepresentation();
|
||||
writeFile(fileName, content);
|
||||
}
|
||||
}
|
||||
|
||||
public String SelectFile() {
|
||||
String s;
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
chooser.setCurrentDirectory(new java.io.File("."));
|
||||
chooser.setDialogTitle("Choose a file");
|
||||
chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
|
||||
chooser.setAcceptAllFileFilterUsed(true);
|
||||
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
|
||||
s=chooser.getSelectedFile().toString();
|
||||
} else {
|
||||
System.out.println("No Selection ");
|
||||
s="";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public void writeFile(String fileName, String[] content) {
|
||||
FileWriter csvWriter;
|
||||
try {
|
||||
csvWriter = new FileWriter(fileName);
|
||||
for (String row : content) {
|
||||
csvWriter.append(row);
|
||||
csvWriter.append("\n");
|
||||
}
|
||||
csvWriter.flush();
|
||||
csvWriter.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void update(int turnCount, boolean turnIsWhite) {
|
||||
turnLabel.setText("Turn : "+turnCount+", "+ (turnIsWhite?"White":"Black"));
|
||||
actionLabel.setText(panelDraw.isPieceAdderMode()?"Adding Piece":
|
||||
(panelDraw.isPieceSelectorMode()?"Selecting Piece to Add":
|
||||
"Playing"));
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
public void eraseLabels() {
|
||||
this.setStepBanner("Turn : X");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue