new class
This commit is contained in:
parent
5e4ffefdbf
commit
d75c05d4b5
|
|
@ -3,46 +3,51 @@ package backend;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a chessboard (arbitrary dimensions),
|
||||||
|
* handles piece placement, move execution (including
|
||||||
|
* castling & en passant), legal-move generation, and check detection.
|
||||||
|
*/
|
||||||
public class Board {
|
public class Board {
|
||||||
private int width, height;
|
private final int width, height;
|
||||||
private ArrayList<Piece> pieces;
|
private final ArrayList<Piece> pieces = new ArrayList<>();
|
||||||
private int turnNumber;
|
private final Stack<Move> moveHistory = new Stack<>();
|
||||||
private boolean isWhiteTurn;
|
|
||||||
private Integer selectedX, selectedY;
|
private int turnNumber = 0;
|
||||||
|
private boolean isWhiteTurn = true;
|
||||||
|
|
||||||
|
// For GUI selection/highlighting
|
||||||
|
private Integer selectedX = null, selectedY = null;
|
||||||
private ArrayList<int[]> highlightedSquares = new ArrayList<>();
|
private ArrayList<int[]> highlightedSquares = new ArrayList<>();
|
||||||
private Stack<Move> moveHistory = new Stack<>();
|
|
||||||
|
|
||||||
// Castling rights
|
// Castling rights
|
||||||
private boolean canCastleWK = true, canCastleWQ = true;
|
private boolean canCastleWK = true, canCastleWQ = true;
|
||||||
private boolean canCastleBK = true, canCastleBQ = true;
|
private boolean canCastleBK = true, canCastleBQ = true;
|
||||||
|
|
||||||
/** Empty board */
|
/** Empty board of given dimensions. */
|
||||||
public Board(int colNum, int lineNum) {
|
public Board(int width, int height) {
|
||||||
this.width = colNum;
|
this.width = width;
|
||||||
this.height = lineNum;
|
this.height = height;
|
||||||
this.turnNumber = 0;
|
|
||||||
this.isWhiteTurn = true;
|
|
||||||
this.pieces = new ArrayList<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load from file */
|
/** Load from file lines. Last line is "W" or "B" for side to move. */
|
||||||
public Board(String[] array) {
|
public Board(String[] rows) {
|
||||||
this(array[0].split(",").length, array.length - 1);
|
this(rows[0].split(",").length, rows.length - 1);
|
||||||
this.isWhiteTurn = array[height].equals("W");
|
this.isWhiteTurn = rows[height].equals("W");
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
String[] row = array[y].split(",");
|
String[] cols = rows[y].split(",");
|
||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
String t = row[x];
|
String cell = cols[x];
|
||||||
if (!t.equals("..")) {
|
if (!cell.equals("..")) {
|
||||||
boolean w = t.charAt(0) == 'W';
|
boolean w = cell.charAt(0) == 'W';
|
||||||
PieceType tp = PieceType.fromSummary(t.charAt(1));
|
PieceType type = PieceType.fromSummary(cell.charAt(1));
|
||||||
pieces.add(new Piece(x, y, w, tp));
|
pieces.add(new Piece(x, y, w, type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Deep copy */
|
/** Deep copy (excludes moveHistory). */
|
||||||
public Board(Board other) {
|
public Board(Board other) {
|
||||||
this(other.width, other.height);
|
this(other.width, other.height);
|
||||||
this.turnNumber = other.turnNumber;
|
this.turnNumber = other.turnNumber;
|
||||||
|
|
@ -51,37 +56,55 @@ public class Board {
|
||||||
this.canCastleWQ = other.canCastleWQ;
|
this.canCastleWQ = other.canCastleWQ;
|
||||||
this.canCastleBK = other.canCastleBK;
|
this.canCastleBK = other.canCastleBK;
|
||||||
this.canCastleBQ = other.canCastleBQ;
|
this.canCastleBQ = other.canCastleBQ;
|
||||||
this.pieces = new ArrayList<>();
|
for (Piece p : other.pieces) {
|
||||||
for (Piece p : other.pieces)
|
pieces.add(new Piece(p.getX(), p.getY(), p.isWhite(), p.getType()));
|
||||||
this.pieces.add(new Piece(p.getX(), p.getY(), p.isWhite(), p.getType()));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accessors
|
// ─── ACCESSORS ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
public int getWidth() { return width; }
|
public int getWidth() { return width; }
|
||||||
public int getHeight() { return height; }
|
public int getHeight() { return height; }
|
||||||
public int getTurnNumber() { return turnNumber; }
|
public int getTurnNumber() { return turnNumber; }
|
||||||
public boolean isTurnWhite() { return isWhiteTurn; }
|
public boolean isTurnWhite() { return isWhiteTurn; }
|
||||||
|
public Iterable<Piece> getPieces() { return new ArrayList<>(pieces); }
|
||||||
|
/** Place a piece of the given color & type at (x,y) (used by the “Add Piece” button). */
|
||||||
|
public void setPiece(boolean white, PieceType type, int x, int y) {
|
||||||
|
// remove whatever was on that square (if any) and drop in your new piece
|
||||||
|
removePieceAt(x, y);
|
||||||
|
pieces.add(new Piece(x, y, white, type));
|
||||||
|
}
|
||||||
|
|
||||||
/** Standard setup */
|
/** Last move pushed onto history, or null if none. */
|
||||||
|
public Move getLastMove() {
|
||||||
|
return moveHistory.isEmpty() ? null : moveHistory.peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── BOARD SETUP & I/O ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Place the standard 8×8 chess starting array. */
|
||||||
public void populateBoard() {
|
public void populateBoard() {
|
||||||
PieceType[] back = {
|
PieceType[] back = {
|
||||||
PieceType.Rook, PieceType.Knight, PieceType.Bishop, PieceType.Queen,
|
PieceType.Rook, PieceType.Knight, PieceType.Bishop, PieceType.Queen,
|
||||||
PieceType.King, PieceType.Bishop, PieceType.Knight, PieceType.Rook
|
PieceType.King, PieceType.Bishop, PieceType.Knight, PieceType.Rook
|
||||||
};
|
};
|
||||||
// black
|
// Black
|
||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
pieces.add(new Piece(x, 1, false, PieceType.Pawn));
|
pieces.add(new Piece(x, 1, false, PieceType.Pawn));
|
||||||
pieces.add(new Piece(x, 0, false, back[x]));
|
pieces.add(new Piece(x, 0, false, back[x]));
|
||||||
}
|
}
|
||||||
// white
|
// White
|
||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
pieces.add(new Piece(x, 6, true, PieceType.Pawn));
|
pieces.add(new Piece(x, 6, true, PieceType.Pawn));
|
||||||
pieces.add(new Piece(x, 7, true, back[x]));
|
pieces.add(new Piece(x, 7, true, back[x]));
|
||||||
}
|
}
|
||||||
canCastleWK = canCastleWQ = canCastleBK = canCastleBQ = true;
|
canCastleWK = canCastleWQ = canCastleBK = canCastleBQ = true;
|
||||||
|
moveHistory.clear();
|
||||||
|
turnNumber = 0;
|
||||||
|
isWhiteTurn = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clear everything */
|
/** Remove all pieces, reset rights & turn. */
|
||||||
public void cleanBoard() {
|
public void cleanBoard() {
|
||||||
pieces.clear();
|
pieces.clear();
|
||||||
moveHistory.clear();
|
moveHistory.clear();
|
||||||
|
|
@ -91,63 +114,61 @@ public class Board {
|
||||||
canCastleWK = canCastleWQ = canCastleBK = canCastleBQ = true;
|
canCastleWK = canCastleWQ = canCastleBK = canCastleBQ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String toString() {
|
/** Saveable representation: one CSV row per rank, then "W" or "B". */
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (int y = 0; y < height; y++) {
|
|
||||||
for (int x = 0; x < width; x++) {
|
|
||||||
Piece p = getPieceAt(x,y);
|
|
||||||
sb.append(p==null?"..":(p.isWhite()?"W":"B")+p.getType().getSummary());
|
|
||||||
if (x<width-1) sb.append(",");
|
|
||||||
}
|
|
||||||
sb.append("\n");
|
|
||||||
}
|
|
||||||
sb.append(isWhiteTurn?"W":"B").append("\n");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] toFileRep() {
|
public String[] toFileRep() {
|
||||||
String[] out = new String[height + 1];
|
String[] out = new String[height + 1];
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
StringBuilder row = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
Piece p = getPieceAt(x, y);
|
Piece p = getPieceAt(x, y);
|
||||||
row.append(p==null?"..":(p.isWhite()?"W":"B")+p.getType().getSummary());
|
sb.append(p == null
|
||||||
if (x<width-1) row.append(",");
|
? ".."
|
||||||
|
: (p.isWhite() ? "W" : "B") + p.getType().getSummary());
|
||||||
|
if (x < width - 1) sb.append(",");
|
||||||
}
|
}
|
||||||
out[y] = row.toString();
|
out[y] = sb.toString();
|
||||||
}
|
}
|
||||||
out[height] = isWhiteTurn ? "W" : "B";
|
out[height] = isWhiteTurn ? "W" : "B";
|
||||||
return out;
|
return out;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Piece> getPieces() { return new ArrayList<>(pieces); }
|
// ─── GUI SELECTION HELPERS ─────────────────────────────────────────────
|
||||||
public void setPiece(boolean w, PieceType t, int x, int y) {
|
|
||||||
removePieceAt(x,y); pieces.add(new Piece(x,y,w,t));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** User click → select/move */
|
/**
|
||||||
|
* Called by GUI on mouse click at (x,y).
|
||||||
|
* First click selects a piece, second click attempts move.
|
||||||
|
*/
|
||||||
public void userTouch(int x, int y) {
|
public void userTouch(int x, int y) {
|
||||||
Piece clicked = getPieceAt(x, y);
|
Piece clicked = getPieceAt(x, y);
|
||||||
|
// select
|
||||||
if (selectedX == null) {
|
if (selectedX == null) {
|
||||||
if (clicked != null && clicked.isWhite() == isWhiteTurn) {
|
if (clicked != null && clicked.isWhite() == isWhiteTurn) {
|
||||||
selectedX=x; selectedY=y;
|
selectedX = x;
|
||||||
|
selectedY = y;
|
||||||
highlightedSquares = computeLegalMoves(clicked);
|
highlightedSquares = computeLegalMoves(clicked);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// deselect
|
||||||
if (selectedX == x && selectedY == y) {
|
if (selectedX == x && selectedY == y) {
|
||||||
selectedX = selectedY = null;
|
selectedX = selectedY = null;
|
||||||
highlightedSquares.clear();
|
highlightedSquares.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// attempt move
|
||||||
for (int[] mv : highlightedSquares) {
|
for (int[] mv : highlightedSquares) {
|
||||||
if (mv[0] == x && mv[1] == y) {
|
if (mv[0] == x && mv[1] == y) {
|
||||||
Piece sel = getPieceAt(selectedX, selectedY);
|
Piece sel = getPieceAt(selectedX, selectedY);
|
||||||
Piece cap = getPieceAt(x, y);
|
Piece cap = getPieceAt(x, y);
|
||||||
// en passant
|
|
||||||
if (cap==null && sel.getType()==PieceType.Pawn && x!=selectedX) {
|
// en passant capture
|
||||||
|
if (cap == null
|
||||||
|
&& sel.getType() == PieceType.Pawn
|
||||||
|
&& x != selectedX)
|
||||||
|
{
|
||||||
int dir = sel.isWhite() ? -1 : 1;
|
int dir = sel.isWhite() ? -1 : 1;
|
||||||
Move last = moveHistory.isEmpty()?null:moveHistory.peek();
|
Move last = getLastMove();
|
||||||
Piece behind = getPieceAt(x, y - dir);
|
Piece behind = getPieceAt(x, y - dir);
|
||||||
if (behind != null
|
if (behind != null
|
||||||
&& behind.getType() == PieceType.Pawn
|
&& behind.getType() == PieceType.Pawn
|
||||||
|
|
@ -155,10 +176,12 @@ public class Board {
|
||||||
&& last.getType() == PieceType.Pawn
|
&& last.getType() == PieceType.Pawn
|
||||||
&& Math.abs(last.getFromY() - last.getToY()) == 2
|
&& Math.abs(last.getFromY() - last.getToY()) == 2
|
||||||
&& last.getToX() == behind.getX()
|
&& last.getToX() == behind.getX()
|
||||||
&& last.getToY()==behind.getY()) {
|
&& last.getToY() == behind.getY())
|
||||||
|
{
|
||||||
cap = behind;
|
cap = behind;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Move m = new Move(
|
Move m = new Move(
|
||||||
selectedX, selectedY,
|
selectedX, selectedY,
|
||||||
x, y,
|
x, y,
|
||||||
|
|
@ -176,33 +199,55 @@ public class Board {
|
||||||
public boolean isSelected(int x, int y) {
|
public boolean isSelected(int x, int y) {
|
||||||
return selectedX != null && selectedX == x && selectedY == y;
|
return selectedX != null && selectedX == x && selectedY == y;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isHighlighted(int x, int y) {
|
public boolean isHighlighted(int x, int y) {
|
||||||
for (int[] mv: highlightedSquares)
|
for (int[] mv : highlightedSquares) {
|
||||||
if (mv[0] == x && mv[1] == y) return true;
|
if (mv[0] == x && mv[1] == y) return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Execute a move (incl. castle & en passant) */
|
// ─── MOVE EXECUTION ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a move:
|
||||||
|
* - normal move
|
||||||
|
* - capture / en passant
|
||||||
|
* - castle rook reposition
|
||||||
|
* - update castling rights
|
||||||
|
* - push history, toggle turn
|
||||||
|
*/
|
||||||
public void playMove(Move m) {
|
public void playMove(Move m) {
|
||||||
if (m == null) return;
|
if (m == null) return;
|
||||||
// castling rook
|
|
||||||
if (m.getType()==PieceType.King && Math.abs(m.getToX()-m.getFromX())==2) {
|
// 1) handle castling rook move
|
||||||
int y = m.isWhite()?7:0;
|
if (m.getType() == PieceType.King
|
||||||
|
&& Math.abs(m.getToX() - m.getFromX()) == 2)
|
||||||
|
{
|
||||||
|
int yRank = m.isWhite() ? 7 : 0;
|
||||||
if (m.getToX() - m.getFromX() == 2) {
|
if (m.getToX() - m.getFromX() == 2) {
|
||||||
removePieceAt(7,y); pieces.add(new Piece(5,y,m.isWhite(),PieceType.Rook));
|
// king-side
|
||||||
|
removePieceAt(7, yRank);
|
||||||
|
pieces.add(new Piece(5, yRank, m.isWhite(), PieceType.Rook));
|
||||||
} else {
|
} else {
|
||||||
removePieceAt(0,y); pieces.add(new Piece(3,y,m.isWhite(),PieceType.Rook));
|
// queen-side
|
||||||
|
removePieceAt(0, yRank);
|
||||||
|
pieces.add(new Piece(3, yRank, m.isWhite(), PieceType.Rook));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// normal or en passant cap
|
|
||||||
|
// 2) capture / en passant
|
||||||
if (m.getCapturedPiece() != null) {
|
if (m.getCapturedPiece() != null) {
|
||||||
removePieceAt(
|
removePieceAt(
|
||||||
m.getCapturedPiece().getX(),
|
m.getCapturedPiece().getX(),
|
||||||
m.getCapturedPiece().getY()
|
m.getCapturedPiece().getY()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// update opponent castling rights if rook was captured
|
|
||||||
if (m.getCapturedPiece()!=null && m.getCapturedPiece().getType()==PieceType.Rook) {
|
// 3) update opponent castling rights if rook was captured
|
||||||
|
if (m.getCapturedPiece() != null
|
||||||
|
&& m.getCapturedPiece().getType() == PieceType.Rook)
|
||||||
|
{
|
||||||
Piece cap = m.getCapturedPiece();
|
Piece cap = m.getCapturedPiece();
|
||||||
if (cap.isWhite()) {
|
if (cap.isWhite()) {
|
||||||
if (cap.getX() == 0 && cap.getY() == 7) canCastleWQ = false;
|
if (cap.getX() == 0 && cap.getY() == 7) canCastleWQ = false;
|
||||||
|
|
@ -212,11 +257,15 @@ public class Board {
|
||||||
if (cap.getX() == 7 && cap.getY() == 0) canCastleBK = false;
|
if (cap.getX() == 7 && cap.getY() == 0) canCastleBK = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// move the piece
|
|
||||||
removePieceAt(m.getFromX(),m.getFromY());
|
|
||||||
pieces.add(new Piece(m.getToX(),m.getToY(),m.isWhite(),m.getType()));
|
|
||||||
|
|
||||||
// moving piece castling rights
|
// 4) move the piece
|
||||||
|
removePieceAt(m.getFromX(), m.getFromY());
|
||||||
|
pieces.add(new Piece(
|
||||||
|
m.getToX(), m.getToY(),
|
||||||
|
m.isWhite(), m.getType()
|
||||||
|
));
|
||||||
|
|
||||||
|
// 5) moving-piece loses castling rights if king or rook moved
|
||||||
if (m.getType() == PieceType.King) {
|
if (m.getType() == PieceType.King) {
|
||||||
if (m.isWhite()) canCastleWK = canCastleWQ = false;
|
if (m.isWhite()) canCastleWK = canCastleWQ = false;
|
||||||
else canCastleBK = canCastleBQ = false;
|
else canCastleBK = canCastleBQ = false;
|
||||||
|
|
@ -231,45 +280,61 @@ public class Board {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 6) push history & toggle
|
||||||
moveHistory.push(m);
|
moveHistory.push(m);
|
||||||
turnNumber++;
|
turnNumber++;
|
||||||
isWhiteTurn = !isWhiteTurn;
|
isWhiteTurn = !isWhiteTurn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Undo last */
|
/** Undo last—restores piece positions, not full castling rights. */
|
||||||
public void undoLastMove() {
|
public void undoLastMove() {
|
||||||
if (moveHistory.isEmpty()) return;
|
if (moveHistory.isEmpty()) return;
|
||||||
Move m = moveHistory.pop();
|
Move m = moveHistory.pop();
|
||||||
// undo castling
|
|
||||||
if (m.getType()==PieceType.King && Math.abs(m.getToX()-m.getFromX())==2) {
|
// undo castling rook reposition
|
||||||
int y = m.isWhite()?7:0;
|
if (m.getType() == PieceType.King
|
||||||
|
&& Math.abs(m.getToX() - m.getFromX()) == 2)
|
||||||
|
{
|
||||||
|
int yRank = m.isWhite() ? 7 : 0;
|
||||||
if (m.getToX() - m.getFromX() == 2) {
|
if (m.getToX() - m.getFromX() == 2) {
|
||||||
removePieceAt(5,y); pieces.add(new Piece(7,y,m.isWhite(),PieceType.Rook));
|
removePieceAt(5, yRank);
|
||||||
|
pieces.add(new Piece(7, yRank, m.isWhite(), PieceType.Rook));
|
||||||
} else {
|
} else {
|
||||||
removePieceAt(3,y); pieces.add(new Piece(0,y,m.isWhite(),PieceType.Rook));
|
removePieceAt(3, yRank);
|
||||||
|
pieces.add(new Piece(0, yRank, m.isWhite(), PieceType.Rook));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// move the king/piece back
|
|
||||||
|
// move back
|
||||||
removePieceAt(m.getToX(), m.getToY());
|
removePieceAt(m.getToX(), m.getToY());
|
||||||
pieces.add(new Piece(m.getFromX(),m.getFromY(),m.isWhite(),m.getType()));
|
pieces.add(new Piece(
|
||||||
|
m.getFromX(), m.getFromY(),
|
||||||
|
m.isWhite(), m.getType()
|
||||||
|
));
|
||||||
|
|
||||||
// restore capture
|
// restore capture
|
||||||
if (m.getCapturedPiece() != null) {
|
if (m.getCapturedPiece() != null) {
|
||||||
Piece c = m.getCapturedPiece();
|
Piece c = m.getCapturedPiece();
|
||||||
pieces.add(new Piece(c.getX(),c.getY(),c.isWhite(),c.getType()));
|
pieces.add(new Piece(
|
||||||
}
|
c.getX(), c.getY(),
|
||||||
turnNumber--;
|
c.isWhite(), c.getType()
|
||||||
isWhiteTurn = !isWhiteTurn;
|
));
|
||||||
// note: castling rights not fully restored here
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Pseudo-legal moves for side to move */
|
turnNumber--;
|
||||||
|
isWhiteTurn = !isWhiteTurn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── MOVE-GENERATION & CHECK DETECTION ─────────────────────────────────
|
||||||
|
|
||||||
|
/** Returns pseudo-legal moves (ignores self-check). */
|
||||||
public ArrayList<Move> generateLegalMoves() {
|
public ArrayList<Move> generateLegalMoves() {
|
||||||
ArrayList<Move> all = new ArrayList<>();
|
ArrayList<Move> out = new ArrayList<>();
|
||||||
for (Piece p : pieces) {
|
for (Piece p : pieces) {
|
||||||
if (p.isWhite() == isWhiteTurn) {
|
if (p.isWhite() == isWhiteTurn) {
|
||||||
for (int[] d : computeLegalMoves(p)) {
|
for (int[] d : computeLegalMoves(p)) {
|
||||||
Piece cap = getPieceAt(d[0], d[1]);
|
Piece cap = getPieceAt(d[0], d[1]);
|
||||||
all.add(new Move(
|
out.add(new Move(
|
||||||
p.getX(), p.getY(),
|
p.getX(), p.getY(),
|
||||||
d[0], d[1],
|
d[0], d[1],
|
||||||
p.isWhite(), p.getType(),
|
p.isWhite(), p.getType(),
|
||||||
|
|
@ -278,22 +343,10 @@ public class Board {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return all;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Is that color’s king under attack? */
|
/** Filtered to remove moves that leave king in check. */
|
||||||
public boolean isInCheck(boolean color) {
|
|
||||||
int kx=-1, ky=-1;
|
|
||||||
for (Piece p:pieces) {
|
|
||||||
if (p.getType()==PieceType.King && p.isWhite()==color) {
|
|
||||||
kx=p.getX(); ky=p.getY(); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (kx<0) throw new IllegalStateException("No king for " + color);
|
|
||||||
return isSquareAttacked(kx,ky,!color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** True legal moves for `color` (filter out self-check) */
|
|
||||||
public ArrayList<Move> generateLegalMoves(boolean color) {
|
public ArrayList<Move> generateLegalMoves(boolean color) {
|
||||||
Board copy = new Board(this);
|
Board copy = new Board(this);
|
||||||
copy.isWhiteTurn = color;
|
copy.isWhiteTurn = color;
|
||||||
|
|
@ -301,22 +354,193 @@ public class Board {
|
||||||
ArrayList<Move> legal = new ArrayList<>();
|
ArrayList<Move> legal = new ArrayList<>();
|
||||||
for (Move m : pseudo) {
|
for (Move m : pseudo) {
|
||||||
copy.playMove(m);
|
copy.playMove(m);
|
||||||
if (!copy.isInCheck(color)) legal.add(m);
|
if (!copy.isInCheck(color)) {
|
||||||
|
legal.add(m);
|
||||||
|
}
|
||||||
copy.undoLastMove();
|
copy.undoLastMove();
|
||||||
}
|
}
|
||||||
return legal;
|
return legal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Square attacked by given side? */
|
/** True if that color’s king is attacked. */
|
||||||
|
public boolean isInCheck(boolean color) {
|
||||||
|
int kx = -1, ky = -1;
|
||||||
|
for (Piece p : pieces) {
|
||||||
|
if (p.getType() == PieceType.King && p.isWhite() == color) {
|
||||||
|
kx = p.getX(); ky = p.getY();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (kx < 0) throw new IllegalStateException("No king for " + (color ? "white" : "black"));
|
||||||
|
return isSquareAttacked(kx, ky, !color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns true if (x,y) is under attack by side “byWhite”. */
|
||||||
public boolean isSquareAttacked(int x, int y, boolean byWhite) {
|
public boolean isSquareAttacked(int x, int y, boolean byWhite) {
|
||||||
for (Piece p:pieces)
|
for (Piece p : pieces) {
|
||||||
if (p.isWhite()==byWhite && attacksSquare(p,x,y))
|
if (p.isWhite() == byWhite && attacksSquare(p, x, y)) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Private helpers ────────────────────────────────────────────
|
// ─── PRIVATE HELPER METHODS ──────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Pseudo-moves for a single piece (ignores check). */
|
||||||
|
private ArrayList<int[]> computeLegalMoves(Piece p) {
|
||||||
|
ArrayList<int[]> out = new ArrayList<>();
|
||||||
|
int x = p.getX(), y = p.getY();
|
||||||
|
boolean w = p.isWhite();
|
||||||
|
switch (p.getType()) {
|
||||||
|
case Pawn:
|
||||||
|
int dir = w ? -1 : 1, startRow = w ? 6 : 1;
|
||||||
|
// forward
|
||||||
|
if (in(x, y + dir) && getPieceAt(x, y + dir) == null) {
|
||||||
|
out.add(new int[]{x, y + dir});
|
||||||
|
if (y == startRow && getPieceAt(x, y + 2 * dir) == null) {
|
||||||
|
out.add(new int[]{x, y + 2 * dir});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// captures
|
||||||
|
for (int dx : new int[]{-1, 1}) {
|
||||||
|
int nx = x + dx, ny = y + dir;
|
||||||
|
if (in(nx, ny)) {
|
||||||
|
Piece t = getPieceAt(nx, ny);
|
||||||
|
if (t != null && t.isWhite() != w) {
|
||||||
|
out.add(new int[]{nx, ny});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// en passant capture possibility
|
||||||
|
if (!moveHistory.isEmpty()) {
|
||||||
|
Move last = moveHistory.peek();
|
||||||
|
if (last.getType() == PieceType.Pawn
|
||||||
|
&& Math.abs(last.getFromY() - last.getToY()) == 2
|
||||||
|
&& last.getToY() == y
|
||||||
|
&& Math.abs(last.getToX() - x) == 1)
|
||||||
|
{
|
||||||
|
int ex = last.getToX(), ey = y + dir;
|
||||||
|
if (in(ex, ey) && getPieceAt(ex, ey) == null) {
|
||||||
|
out.add(new int[]{ex, ey});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Knight:
|
||||||
|
int[][] km = {{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1}};
|
||||||
|
for (int[] d : km) {
|
||||||
|
int nx = x + d[0], ny = y + d[1];
|
||||||
|
if (in(nx, ny)) {
|
||||||
|
Piece t = getPieceAt(nx, ny);
|
||||||
|
if (t == null || t.isWhite() != w) {
|
||||||
|
out.add(new int[]{nx, ny});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Bishop:
|
||||||
|
slide(out, x, y, w, new int[][]{{1,1},{1,-1},{-1,1},{-1,-1}});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Rook:
|
||||||
|
slide(out, x, y, w, new int[][]{{1,0},{-1,0},{0,1},{0,-1}});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Queen:
|
||||||
|
slide(out, x, y, w, new int[][]{
|
||||||
|
{1,0},{-1,0},{0,1},{0,-1},
|
||||||
|
{1,1},{1,-1},{-1,1},{-1,-1}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case King:
|
||||||
|
for (int dx = -1; dx <= 1; dx++) {
|
||||||
|
for (int dy = -1; dy <= 1; dy++) {
|
||||||
|
if (dx == 0 && dy == 0) continue;
|
||||||
|
int nx = x + dx, ny = y + dy;
|
||||||
|
if (in(nx, ny)) {
|
||||||
|
Piece t = getPieceAt(nx, ny);
|
||||||
|
if (t == null || t.isWhite() != w) {
|
||||||
|
out.add(new int[]{nx, ny});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// castling
|
||||||
|
boolean opp = !w;
|
||||||
|
if (!isSquareAttacked(x, y, opp)) {
|
||||||
|
// white back rank
|
||||||
|
if (w && x == 4 && y == 7) {
|
||||||
|
if (canCastleWK
|
||||||
|
&& getPieceAt(5,7) == null
|
||||||
|
&& getPieceAt(6,7) == null
|
||||||
|
&& !isSquareAttacked(5,7,opp)
|
||||||
|
&& !isSquareAttacked(6,7,opp))
|
||||||
|
{
|
||||||
|
out.add(new int[]{6,7});
|
||||||
|
}
|
||||||
|
if (canCastleWQ
|
||||||
|
&& getPieceAt(3,7) == null
|
||||||
|
&& getPieceAt(2,7) == null
|
||||||
|
&& getPieceAt(1,7) == null
|
||||||
|
&& !isSquareAttacked(3,7,opp)
|
||||||
|
&& !isSquareAttacked(2,7,opp))
|
||||||
|
{
|
||||||
|
out.add(new int[]{2,7});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// black back rank
|
||||||
|
if (!w && x == 4 && y == 0) {
|
||||||
|
if (canCastleBK
|
||||||
|
&& getPieceAt(5,0) == null
|
||||||
|
&& getPieceAt(6,0) == null
|
||||||
|
&& !isSquareAttacked(5,0,opp)
|
||||||
|
&& !isSquareAttacked(6,0,opp))
|
||||||
|
{
|
||||||
|
out.add(new int[]{6,0});
|
||||||
|
}
|
||||||
|
if (canCastleBQ
|
||||||
|
&& getPieceAt(3,0) == null
|
||||||
|
&& getPieceAt(2,0) == null
|
||||||
|
&& getPieceAt(1,0) == null
|
||||||
|
&& !isSquareAttacked(3,0,opp)
|
||||||
|
&& !isSquareAttacked(2,0,opp))
|
||||||
|
{
|
||||||
|
out.add(new int[]{2,0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Helper for sliding pieces (bishop, rook, queen). */
|
||||||
|
private void slide(ArrayList<int[]> out,
|
||||||
|
int x, int y, boolean w,
|
||||||
|
int[][] directions)
|
||||||
|
{
|
||||||
|
for (int[] d : directions) {
|
||||||
|
for (int i = 1; i < 8; i++) {
|
||||||
|
int nx = x + d[0]*i, ny = y + d[1]*i;
|
||||||
|
if (!in(nx, ny)) break;
|
||||||
|
Piece t = getPieceAt(nx, ny);
|
||||||
|
if (t == null) {
|
||||||
|
out.add(new int[]{nx, ny});
|
||||||
|
} else {
|
||||||
|
if (t.isWhite() != w) {
|
||||||
|
out.add(new int[]{nx, ny});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Attack pattern ignoring blocks (for knight & king & pawn). */
|
||||||
private boolean attacksSquare(Piece p, int tx, int ty) {
|
private boolean attacksSquare(Piece p, int tx, int ty) {
|
||||||
int x = p.getX(), y = p.getY();
|
int x = p.getX(), y = p.getY();
|
||||||
boolean w = p.isWhite();
|
boolean w = p.isWhite();
|
||||||
|
|
@ -345,18 +569,24 @@ public class Board {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Checks that no piece blocks the straight-line path. */
|
||||||
private boolean clearPath(int x1, int y1, int x2, int y2) {
|
private boolean clearPath(int x1, int y1, int x2, int y2) {
|
||||||
int sx=Integer.signum(x2-x1), sy=Integer.signum(y2-y1);
|
int stepX = Integer.signum(x2 - x1);
|
||||||
int cx=x1+sx, cy=y1+sy;
|
int stepY = Integer.signum(y2 - y1);
|
||||||
|
int cx = x1 + stepX, cy = y1 + stepY;
|
||||||
while (cx != x2 || cy != y2) {
|
while (cx != x2 || cy != y2) {
|
||||||
if (getPieceAt(cx, cy) != null) return false;
|
if (getPieceAt(cx, cy) != null) return false;
|
||||||
cx+=sx; cy+=sy;
|
cx += stepX; cy += stepY;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── PRIVATE HELPERS ──────────────────────────────────────────────────
|
||||||
|
|
||||||
private Piece getPieceAt(int x, int y) {
|
private Piece getPieceAt(int x, int y) {
|
||||||
for (Piece p:pieces) if (p.getX()==x&&p.getY()==y) return p;
|
for (Piece p : pieces) {
|
||||||
|
if (p.getX() == x && p.getY() == y) return p;
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -367,115 +597,10 @@ public class Board {
|
||||||
private boolean in(int x, int y) {
|
private boolean in(int x, int y) {
|
||||||
return x >= 0 && x < width && y >= 0 && y < height;
|
return x >= 0 && x < width && y >= 0 && y < height;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void slide(ArrayList<int[]> out,int x,int y,boolean w,int[][] dirs) {
|
|
||||||
for (int[] d:dirs) {
|
|
||||||
for (int i=1;i<8;i++){
|
|
||||||
int nx=x+d[0]*i, ny=y+d[1]*i;
|
|
||||||
if (!in(nx,ny)) break;
|
|
||||||
Piece t = getPieceAt(nx,ny);
|
|
||||||
if (t==null) out.add(new int[]{nx,ny});
|
|
||||||
else {
|
|
||||||
if (t.isWhite()!=w) out.add(new int[]{nx,ny});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList<int[]> computeLegalMoves(Piece p) {
|
|
||||||
ArrayList<int[]> out = new ArrayList<>();
|
|
||||||
int x=p.getX(), y=p.getY();
|
|
||||||
boolean w=p.isWhite();
|
|
||||||
switch(p.getType()) {
|
|
||||||
case Pawn:
|
|
||||||
int dir = w?-1:1, sr = w?6:1;
|
|
||||||
if (in(x,y+dir)&&getPieceAt(x,y+dir)==null) {
|
|
||||||
out.add(new int[]{x,y+dir});
|
|
||||||
if (y==sr&&getPieceAt(x,y+2*dir)==null)
|
|
||||||
out.add(new int[]{x,y+2*dir});
|
|
||||||
}
|
|
||||||
for (int dx : new int[]{-1,1}) {
|
|
||||||
int nx=x+dx, ny=y+dir;
|
|
||||||
if (in(nx,ny)) {
|
|
||||||
Piece t=getPieceAt(nx,ny);
|
|
||||||
if (t!=null&&t.isWhite()!=w) out.add(new int[]{nx,ny});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!moveHistory.isEmpty()) {
|
|
||||||
Move last=moveHistory.peek();
|
|
||||||
if (last.getType()==PieceType.Pawn
|
|
||||||
&&Math.abs(last.getFromY()-last.getToY())==2
|
|
||||||
&&last.getToY()==y
|
|
||||||
&&Math.abs(last.getToX()-x)==1) {
|
|
||||||
int ex=last.getToX(), ey=y+dir;
|
|
||||||
if (in(ex,ey)&&getPieceAt(ex,ey)==null)
|
|
||||||
out.add(new int[]{ex,ey});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Rook:
|
|
||||||
slide(out,x,y,w,new int[][]{{1,0},{-1,0},{0,1},{0,-1}});
|
|
||||||
break;
|
|
||||||
case Bishop:
|
|
||||||
slide(out,x,y,w,new int[][]{{1,1},{1,-1},{-1,1},{-1,-1}});
|
|
||||||
break;
|
|
||||||
case Queen:
|
|
||||||
slide(out,x,y,w,new int[][]{
|
|
||||||
{1,0},{-1,0},{0,1},{0,-1},
|
|
||||||
{1,1},{1,-1},{-1,1},{-1,-1}});
|
|
||||||
break;
|
|
||||||
case Knight:
|
|
||||||
int[][] km={{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1}};
|
|
||||||
for (int[] m:km) {
|
|
||||||
int nx=x+m[0], ny=y+m[1];
|
|
||||||
if (in(nx,ny)) {
|
|
||||||
Piece t=getPieceAt(nx,ny);
|
|
||||||
if (t==null||t.isWhite()!=w) out.add(new int[]{nx,ny});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case King:
|
|
||||||
for (int dx=-1;dx<=1;dx++)
|
|
||||||
for (int dy=-1;dy<=1;dy++){
|
|
||||||
if (dx==0&&dy==0) continue;
|
|
||||||
int nx=x+dx, ny=y+dy;
|
|
||||||
if (in(nx,ny)){
|
|
||||||
Piece t=getPieceAt(nx,ny);
|
|
||||||
if (t==null||t.isWhite()!=w) out.add(new int[]{nx,ny});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boolean opp=!w;
|
|
||||||
if (!isSquareAttacked(x,y,opp)){
|
|
||||||
if (w&&x==4&&y==7){
|
|
||||||
if (canCastleWK&&getPieceAt(5,7)==null&&getPieceAt(6,7)==null
|
|
||||||
&&!isSquareAttacked(5,7,opp)&&!isSquareAttacked(6,7,opp))
|
|
||||||
out.add(new int[]{6,7});
|
|
||||||
if (canCastleWQ&&getPieceAt(3,7)==null&&getPieceAt(2,7)==null
|
|
||||||
&&getPieceAt(1,7)==null&& !isSquareAttacked(3,7,opp)
|
|
||||||
&&!isSquareAttacked(2,7,opp))
|
|
||||||
out.add(new int[]{2,7});
|
|
||||||
}
|
|
||||||
if (!w&&x==4&&y==0){
|
|
||||||
if (canCastleBK&&getPieceAt(5,0)==null&&getPieceAt(6,0)==null
|
|
||||||
&&!isSquareAttacked(5,0,opp)&&!isSquareAttacked(6,0,opp))
|
|
||||||
out.add(new int[]{6,0});
|
|
||||||
if (canCastleBQ&&getPieceAt(3,0)==null&&getPieceAt(2,0)==null
|
|
||||||
&&getPieceAt(1,0)==null&& !isSquareAttacked(3,0,opp)
|
|
||||||
&&!isSquareAttacked(2,0,opp))
|
|
||||||
out.add(new int[]{2,0});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Move getLastMove() {
|
|
||||||
return moveHistory.isEmpty() ? null : moveHistory.peek();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,20 +1,23 @@
|
||||||
package backend;
|
package backend;
|
||||||
|
|
||||||
import windowInterface.MyInterface;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import windowInterface.MyInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main game loop thread. Applies user & AI moves,
|
||||||
|
* counts captures, checks for end-of-game, and updates the GUI.
|
||||||
|
*/
|
||||||
public class Game extends Thread {
|
public class Game extends Thread {
|
||||||
|
|
||||||
private final AutoPlayer aiPlayer;
|
private final AutoPlayer aiPlayer;
|
||||||
private Board board;
|
private Board board;
|
||||||
private final MyInterface mjf;
|
private final MyInterface mjf;
|
||||||
private final boolean[] activationAIFlags = new boolean[2];
|
private final boolean[] activationAI = new boolean[2]; // [0]=blackAI, [1]=whiteAI
|
||||||
private boolean gameOver = false;
|
private boolean gameOver = false;
|
||||||
private int whiteCaptures = 0, blackCaptures = 0;
|
private int whiteCaptures = 0, blackCaptures = 0;
|
||||||
private int loopDelay = 250;
|
private int loopDelay = 250;
|
||||||
|
|
||||||
public Game(MyInterface mjfParam) {
|
public Game(MyInterface mjf) {
|
||||||
this.mjf = mjfParam;
|
this.mjf = mjf;
|
||||||
this.board = new Board(8, 8);
|
this.board = new Board(8, 8);
|
||||||
this.aiPlayer = new AutoPlayer();
|
this.aiPlayer = new AutoPlayer();
|
||||||
}
|
}
|
||||||
|
|
@ -23,38 +26,40 @@ public class Game extends Thread {
|
||||||
public int getHeight() { return board.getHeight(); }
|
public int getHeight() { return board.getHeight(); }
|
||||||
public int getWhiteCaptures() { return whiteCaptures; }
|
public int getWhiteCaptures() { return whiteCaptures; }
|
||||||
public int getBlackCaptures() { return blackCaptures; }
|
public int getBlackCaptures() { return blackCaptures; }
|
||||||
|
public Board getBoard() { return board; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
while (!gameOver) {
|
while (!gameOver) {
|
||||||
if (isAITurn()) {
|
// AI’s turn?
|
||||||
Move m = aiPlayer.computeBestMove(new Board(board));
|
if (activationAI[board.isTurnWhite() ? 1 : 0]) {
|
||||||
board.playMove(m);
|
Move aiMove = aiPlayer.computeBestMove(new Board(board));
|
||||||
recordCapture(m);
|
board.playMove(aiMove);
|
||||||
|
recordCapture(aiMove);
|
||||||
checkGameEnd();
|
checkGameEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// always update GUI
|
||||||
mjf.update(board.getTurnNumber(), board.isTurnWhite());
|
mjf.update(board.getTurnNumber(), board.isTurnWhite());
|
||||||
try { Thread.sleep(loopDelay); }
|
|
||||||
catch (InterruptedException e) { e.printStackTrace(); }
|
try {
|
||||||
|
Thread.sleep(loopDelay);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAITurn() {
|
/**
|
||||||
// [1] = white, [0] = black
|
* Called from the panel when the user clicks square (x,y).
|
||||||
return activationAIFlags[ board.isTurnWhite()?1:0 ];
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
public void clickCoords(int x, int y) {
|
public void clickCoords(int x, int y) {
|
||||||
if (gameOver) return;
|
if (gameOver) return;
|
||||||
if (x<0||y<0||x>=getWidth()||y>=getHeight()) return;
|
|
||||||
if (!isAITurn()) {
|
|
||||||
board.userTouch(x, y);
|
board.userTouch(x, y);
|
||||||
// lastMove not returned, but board.moveHistory.peek() is it:
|
|
||||||
Move last = board.getLastMove();
|
Move last = board.getLastMove();
|
||||||
recordCapture(last);
|
recordCapture(last);
|
||||||
checkGameEnd();
|
checkGameEnd();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void recordCapture(Move m) {
|
private void recordCapture(Move m) {
|
||||||
if (m != null && m.getCapturedPiece() != null) {
|
if (m != null && m.getCapturedPiece() != null) {
|
||||||
|
|
@ -63,43 +68,66 @@ public class Game extends Thread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** check for end‐of‐game and notify GUI */
|
|
||||||
private void checkGameEnd() {
|
private void checkGameEnd() {
|
||||||
boolean sideToMove = board.isTurnWhite();
|
boolean sideToMove = board.isTurnWhite();
|
||||||
List<Move> replies = ChessRules.generateLegalMoves(board, sideToMove);
|
List<Move> legal = ChessRules.generateLegalMoves(board, sideToMove);
|
||||||
if (replies.isEmpty()) {
|
if (legal.isEmpty()) {
|
||||||
String msg;
|
String message;
|
||||||
if (ChessRules.isCheckmate(board, sideToMove)) {
|
if (ChessRules.isCheckmate(board, sideToMove)) {
|
||||||
msg = (sideToMove?"White":"Black")
|
message = (sideToMove ? "White" : "Black")
|
||||||
+ " is checkmated. "
|
+ " is checkmated. "
|
||||||
+ (!sideToMove ? "White" : "Black")
|
+ (!sideToMove ? "White" : "Black")
|
||||||
+ " wins!";
|
+ " wins!";
|
||||||
} else {
|
} else {
|
||||||
msg = "Stalemate—draw.";
|
message = "Stalemate—draw.";
|
||||||
}
|
}
|
||||||
System.out.println(msg);
|
mjf.gameOver(message);
|
||||||
mjf.gameOver(msg);
|
|
||||||
gameOver = true;
|
gameOver = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- existing API below ---
|
// ─── API methods forwarded to Board ────────────────────────────────
|
||||||
|
|
||||||
public void setPiece(boolean w, PieceType t, int x, int y) {
|
public void setPiece(boolean w, PieceType t, int x, int y) {
|
||||||
board.setPiece(w, t, x, y);
|
board.setPiece(w, t, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] getFileRepresentation() {
|
public String[] getFileRepresentation() {
|
||||||
return board.toFileRep();
|
return board.toFileRep();
|
||||||
}
|
}
|
||||||
public void setLoopDelay(int d) { loopDelay = d; }
|
|
||||||
public void setDefaultSetup() { board.cleanBoard(); board.populateBoard(); }
|
public void setLoopDelay(int d) {
|
||||||
public void setBoard(String[] a){ board = new Board(a); }
|
loopDelay = d;
|
||||||
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 setDefaultSetup() {
|
||||||
public void undoLastMove() { board.undoLastMove(); }
|
board.cleanBoard();
|
||||||
public void toggleAI(boolean w) { activationAIFlags[w?1:0] = !activationAIFlags[w?1:0]; }
|
board.populateBoard();
|
||||||
public Board getBoard() { return board; }
|
}
|
||||||
|
|
||||||
|
public void setBoard(String[] rows) {
|
||||||
|
board = new Board(rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
board.undoLastMove();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggleAI(boolean isWhite) {
|
||||||
|
activationAI[isWhite ? 1 : 0] = !activationAI[isWhite ? 1 : 0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -109,3 +137,5 @@ public class Game extends Thread {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package backend;
|
||||||
|
|
||||||
|
/** Thrown by Board.playMove(...) if you ever capture a king. */
|
||||||
|
public class KingCapturedException extends RuntimeException {
|
||||||
|
/** true = white did the capture, false = black did it */
|
||||||
|
public final boolean byWhite;
|
||||||
|
|
||||||
|
public KingCapturedException(boolean byWhite) {
|
||||||
|
this.byWhite = byWhite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,42 +1,49 @@
|
||||||
package windowInterface;
|
package windowInterface;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.*;
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
import java.util.HashSet;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
import backend.*;
|
import backend.*;
|
||||||
|
import java.util.List;
|
||||||
|
import backend.Move;
|
||||||
|
|
||||||
|
|
||||||
public class JPanelChessBoard extends JPanel {
|
public class JPanelChessBoard extends JPanel {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private Game myGame;
|
private Game myGame;
|
||||||
private final MyInterface interfaceGlobal;
|
private final MyInterface interfaceGlobal;
|
||||||
private BufferedImage spriteSheet;
|
private BufferedImage spriteSheet;
|
||||||
private final int PIECE_W=16, PIECE_H=16, M=6;
|
private final int PIECE_W = 16, PIECE_H = 16, MARGIN = 6;
|
||||||
|
|
||||||
private boolean pieceSelectorMode=false, pieceAdderMode=false;
|
private boolean pieceSelectorMode = false;
|
||||||
private boolean selIsWhite=true; private PieceType selType=PieceType.Pawn;
|
private boolean pieceAdderMode = false;
|
||||||
|
private boolean selIsWhite = true;
|
||||||
|
private PieceType selType = PieceType.Pawn;
|
||||||
|
|
||||||
public JPanelChessBoard(MyInterface itf) {
|
public JPanelChessBoard(MyInterface itf) {
|
||||||
super(); interfaceGlobal=itf;
|
super();
|
||||||
try { spriteSheet=ImageIO.read(new File("pieces.png")); }
|
interfaceGlobal = itf;
|
||||||
catch(Exception e){ e.printStackTrace(); }
|
try {
|
||||||
|
spriteSheet = javax.imageio.ImageIO.read(new File("pieces.png"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
addMouseListener(new MouseAdapter() {
|
addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
public void mousePressed(MouseEvent me) {
|
public void mousePressed(MouseEvent me) {
|
||||||
if (pieceSelectorMode) {
|
if (pieceSelectorMode) {
|
||||||
int x=(int)(me.getX()/cellW()), y=(int)(me.getY()/cellH());
|
int x = (int) (me.getX() / cellW());
|
||||||
|
int y = (int) (me.getY() / cellH());
|
||||||
selType = PieceType.values()[5 - x];
|
selType = PieceType.values()[5 - x];
|
||||||
selIsWhite = y>1;
|
selIsWhite = (y > 1);
|
||||||
pieceSelectorMode = false;
|
pieceSelectorMode = false;
|
||||||
} else {
|
} else {
|
||||||
if (myGame == null) interfaceGlobal.instantiateSimu();
|
if (myGame == null) interfaceGlobal.instantiateSimu();
|
||||||
int x=(me.getX()*myGame.getWidth())/getWidth(),
|
int x = me.getX() * myGame.getWidth() / getWidth();
|
||||||
y=(me.getY()*myGame.getHeight())/getHeight();
|
int y = me.getY() * myGame.getHeight() / getHeight();
|
||||||
if (pieceAdderMode) {
|
if (pieceAdderMode) {
|
||||||
myGame.setPiece(selIsWhite, selType, x, y);
|
myGame.setPiece(selIsWhite, selType, x, y);
|
||||||
pieceAdderMode = false;
|
pieceAdderMode = false;
|
||||||
|
|
@ -49,30 +56,45 @@ public class JPanelChessBoard extends JPanel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGame(Game g){ myGame=g; }
|
public void setGame(Game g) {
|
||||||
|
myGame = g;
|
||||||
|
}
|
||||||
|
|
||||||
@Override protected void paintComponent(Graphics g){
|
@Override
|
||||||
super.paintComponent(g);
|
protected void paintComponent(Graphics gg) {
|
||||||
|
super.paintComponent(gg);
|
||||||
if (myGame == null) return;
|
if (myGame == null) return;
|
||||||
|
|
||||||
// compute legal highlights
|
Graphics2D g = (Graphics2D) gg;
|
||||||
|
float w = cellW(), h = cellH();
|
||||||
|
|
||||||
|
// find selected square
|
||||||
int selX = -1, selY = -1;
|
int selX = -1, selY = -1;
|
||||||
for(int xx=0;xx<myGame.getWidth();xx++) for(int yy=0;yy<myGame.getHeight();yy++)
|
for (int xx = 0; xx < myGame.getWidth(); xx++)
|
||||||
if(myGame.isSelected(xx,yy)){ selX=xx; selY=yy; break;}
|
for (int yy = 0; yy < myGame.getHeight(); yy++)
|
||||||
Set<Point> legalH = new HashSet<>();
|
if (myGame.isSelected(xx, yy)) {
|
||||||
|
selX = xx;
|
||||||
|
selY = yy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// legal highlights
|
||||||
|
Set<Point> legal = new HashSet<>();
|
||||||
if (selX >= 0) {
|
if (selX >= 0) {
|
||||||
List<Move> lm = ChessRules.generateLegalMoves(
|
List<Move> lm = ChessRules.generateLegalMoves(
|
||||||
myGame.getBoard(), myGame.getBoard().isTurnWhite()
|
myGame.getBoard(), myGame.getBoard().isTurnWhite()
|
||||||
);
|
);
|
||||||
for(Move m:lm) if(m.getFromX()==selX&&m.getFromY()==selY)
|
for (Move m : lm) {
|
||||||
legalH.add(new Point(m.getToX(),m.getToY()));
|
if (m.getFromX() == selX && m.getFromY() == selY) {
|
||||||
|
legal.add(new Point(m.getToX(), m.getToY()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float w=cellW(), h=cellH();
|
// draw board
|
||||||
for (int x = 0; x < myGame.getWidth(); x++) {
|
for (int x = 0; x < myGame.getWidth(); x++) {
|
||||||
for (int y = 0; y < myGame.getHeight(); y++) {
|
for (int y = 0; y < myGame.getHeight(); y++) {
|
||||||
boolean isSel= myGame.isSelected(x,y),
|
boolean isSel = (x == selX && y == selY);
|
||||||
isHl = legalH.contains(new Point(x,y));
|
boolean isHl = legal.contains(new Point(x, y));
|
||||||
if (isSel) g.setColor(Color.PINK);
|
if (isSel) g.setColor(Color.PINK);
|
||||||
else if (isHl) g.setColor(Color.GREEN);
|
else if (isHl) g.setColor(Color.GREEN);
|
||||||
else g.setColor((x + y) % 2 == 0 ? Color.WHITE : Color.RED);
|
else g.setColor((x + y) % 2 == 0 ? Color.WHITE : Color.RED);
|
||||||
|
|
@ -86,27 +108,38 @@ public class JPanelChessBoard extends JPanel {
|
||||||
g.drawLine(0, (int)(y*h), getWidth(), (int)(y*h));
|
g.drawLine(0, (int)(y*h), getWidth(), (int)(y*h));
|
||||||
|
|
||||||
// draw pieces
|
// draw pieces
|
||||||
for(Piece p: myGame.getPieces()) drawPiece(g,p);
|
for (Piece p : myGame.getPieces()) {
|
||||||
|
drawPiece(g, p, w, h);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawPiece(Graphics g, Piece p){
|
private void drawPiece(Graphics2D g, Piece p, float w, float h) {
|
||||||
Image sub = spriteSheet.getSubimage(
|
int idx = 5 - p.getType().ordinal();
|
||||||
spriteIndex(p.getType())*PIECE_W,
|
BufferedImage sub = spriteSheet.getSubimage(
|
||||||
|
idx * PIECE_W,
|
||||||
p.isWhite() ? PIECE_H : 0,
|
p.isWhite() ? PIECE_H : 0,
|
||||||
PIECE_W, PIECE_H
|
PIECE_W, PIECE_H
|
||||||
);
|
);
|
||||||
|
Image scaled = sub.getScaledInstance(
|
||||||
|
(int)(w - 2 * MARGIN),
|
||||||
|
(int)(h - 2 * MARGIN),
|
||||||
|
Image.SCALE_SMOOTH
|
||||||
|
);
|
||||||
g.drawImage(
|
g.drawImage(
|
||||||
sub.getScaledInstance((int)(cellW()-2*M),(int)(cellH()-2*M),0),
|
scaled,
|
||||||
(int)(p.getX()*cellW())+M, (int)(p.getY()*cellH())+M, null
|
(int)(p.getX() * w) + MARGIN,
|
||||||
|
(int)(p.getY() * h) + MARGIN,
|
||||||
|
null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int spriteIndex(PieceType t){ return 5 - t.ordinal(); }
|
|
||||||
private float cellW() { return getWidth() / (float) myGame.getWidth(); }
|
private float cellW() { return getWidth() / (float) myGame.getWidth(); }
|
||||||
private float cellH() { return getHeight() / (float) myGame.getHeight(); }
|
private float cellH() { return getHeight() / (float) myGame.getHeight(); }
|
||||||
|
|
||||||
|
// modes
|
||||||
public void togglePieceSelector() { pieceSelectorMode = !pieceSelectorMode; }
|
public void togglePieceSelector() { pieceSelectorMode = !pieceSelectorMode; }
|
||||||
public void toggleAdderMode() { pieceAdderMode = !pieceAdderMode; }
|
public void toggleAdderMode() { pieceAdderMode = !pieceAdderMode; }
|
||||||
public boolean isPieceSelectorMode() { return pieceSelectorMode; }
|
public boolean isPieceSelectorMode() { return pieceSelectorMode; }
|
||||||
public boolean isPieceAdderMode() { return pieceAdderMode; }
|
public boolean isPieceAdderMode() { return pieceAdderMode; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,10 @@ public class MyInterface extends JFrame {
|
||||||
start.addActionListener(e->clicButtonStart());
|
start.addActionListener(e->clicButtonStart());
|
||||||
top.add(start);
|
top.add(start);
|
||||||
|
|
||||||
turnLabel = new JLabel("Turn: 0");
|
turnLabel = new JLabel("Turn: 0 /");
|
||||||
top.add(turnLabel);
|
top.add(turnLabel);
|
||||||
|
|
||||||
whiteCapLabel = new JLabel("W captures: 0");
|
whiteCapLabel = new JLabel(" W captures: 0 /");
|
||||||
top.add(whiteCapLabel);
|
top.add(whiteCapLabel);
|
||||||
blackCapLabel = new JLabel(" B captures: 0");
|
blackCapLabel = new JLabel(" B captures: 0");
|
||||||
top.add(blackCapLabel);
|
top.add(blackCapLabel);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue