Added javadoc

This commit is contained in:
Filip Znachor 2023-05-06 22:54:26 +02:00
parent 0b4c7d440c
commit 91608f1f7b
16 changed files with 640 additions and 96 deletions

View file

@ -39,14 +39,23 @@ public abstract class APiece {
*/
protected PieceColor color;
/**
* Player owning the piece
*/
protected Player player;
/**
* Piece's move count
*/
public int moveCount = 0;
/**
* Piece's scale
*/
public double scale = 1;
/**
* Create new piece
* Create a new piece
* @param player player
* @param x piece's X location
* @param y piece's Y location
@ -147,31 +156,37 @@ public abstract class APiece {
}
/**
* Set piece's new position
* @param pos piece position
* Set piece's new position with animation
* @param newX new X position
* @param newY new Y position
*/
public void setPosition(PiecePosition pos) {
setPosition(pos, true);
public void setPosition(int newX, int newY) {
setPosition(newX, newY, true);
}
/**
* Set piece's new position
* @param pos piece position
* @param newX new X position
* @param newY new Y position
* @param animate animate piece's move
*/
public void setPosition(PiecePosition pos, boolean animate) {
if(animate) animateMove(x, y, pos.x, pos.y);
public void setPosition(int newX, int newY, boolean animate) {
if(animate) animateMove(x, y, newX, newY);
moveCount++;
chessboard.removePiece(x, y);
APiece piece = chessboard.getPiece(pos.x, pos.y);
APiece piece = chessboard.getPiece(newX, newY);
if(piece != null) {
piece.remove(animate);
}
x = pos.x;
y = pos.y;
x = newX;
y = newY;
chessboard.addPiece(this, x, y);
}
/**
* Determines if the piece is floating
* @return is floating
*/
public boolean isFloating() {
return overrideX != 0 && overrideY != 0;
}
@ -187,31 +202,31 @@ public abstract class APiece {
}
/**
* Get piece's override X location
* @return override X location
* Get X position in px on the chessboard
* @return real X position in px
*/
public double getOverrideX() {
return (overrideX-chessboard.startX)/chessboard.boardScale;
}
/**
* Get piece's override Y location
* @return override Y location
*/
public double getOverrideY() {
return (overrideY-chessboard.startY)/chessboard.boardScale;
}
public double getRealX() {
if(overrideX != 0) return getOverrideX();
if(overrideX != 0) return (overrideX-chessboard.startX)/chessboard.boardScale;
return chessboard.SQUARE_SIZE*x;
}
/**
* Get Y position in px on the chessboard
* @return real Y position in px
*/
public double getRealY() {
if(overrideY != 0) return getOverrideY();
if(overrideY != 0) return (overrideY-chessboard.startY)/chessboard.boardScale;;
return chessboard.SQUARE_SIZE*y;
}
/**
* Get piece's scale
* @return piece's scale
*/
public double getScale() {
return scale;
}
/**
* Get piece's X location
* @return piece's X location
@ -228,56 +243,116 @@ public abstract class APiece {
return y;
}
/**
* Get piece's color
* @return piece's color
*/
public PieceColor getColor() {
return color;
}
public void setPossibleMove(boolean[][] moves, int x, int y) {
/**
* Get piece's player
* @return piece's player
*/
public Player getPlayer() {
return player;
}
/**
* Get piece's move count
* @return move count
*/
public int getMoveCount() {
return moveCount;
}
/**
* Set piece's move count
* @param moveCount new move count
*/
public void setMoveCount(int moveCount) {
this.moveCount = moveCount;
}
/**
* Checks, if passed move is possible
* @param moves two-dimensional array of moves
* @param x new X position
* @param y new Y position
*/
public void setPossibleMove(boolean[][] moves, int x, int y) {
if(x == this.x && y == this.y) return;
if(x < 0 || x >= moves.length || y < 0 || y >= moves.length) return;
APiece piece = chessboard.getPiece(x, y);
if(piece != null && player == piece.getPlayer()) return;
if(tryMove(piece, x, y)) return;
moves[y][x] = true;
}
/**
* Tests whether the player will be in check if the piece moves to a new position
* @param piece selected piece
* @param x new X position
* @param y new Y position
* @return true, if player will be in check
*/
public boolean tryMove(APiece piece, int x, int y) {
chessboard.removePiece(this.x, this.y);
chessboard.addPiece(this, x, y);
int xBefore = this.x;
int yBefore = this.y;
this.x = x;
this.y = y;
int xBefore = this.x, yBefore = this.y;
this.x = x; this.y = y;
boolean inCheck = player.inCheck();
this.x = xBefore;
this.y = yBefore;
this.x = xBefore; this.y = yBefore;
chessboard.addPiece(piece, x, y);
chessboard.addPiece(this, this.x, this.y);
return inCheck;
}
/**
* Tests, whether the piece is endangered by another piece
* @return true, if endangered
*/
public boolean isEndangered() {
return chessboard.isEndangered(x, y);
}
public void move(PiecePosition pos) {
move(pos, true);
/**
* Move the piece to the new position with animation
* @param newX new X position
* @param newY new Y position
*/
public void move(int newX, int newY) {
move(newX, newY, true);
}
public void move(PiecePosition pos, boolean animate) {
/**
* Move the piece to the new position with animation
* @param newX new X position
* @param newY new Y position
* @param animate animate piece's move
*/
public void move(int newX, int newY, boolean animate) {
boolean[][] lastMove = new boolean[chessboard.SQUARE_COUNT][chessboard.SQUARE_COUNT];
lastMove[y][x] = true;
lastMove[pos.y][pos.x] = true;
setPosition(pos, animate);
lastMove[newY][newX] = true;
setPosition(newX, newY, animate);
chessboard.showLastMove(lastMove);
}
/**
* Get repaint rectangle containing the piece
* @return repaint rectangle
*/
public Rectangle getRepaintRectangle() {
return getRepaintRectangle(Chess.menuBar.getHeight());
}
/**
* Get repaint rectangle containing the piece with vertical offset
* @param offsetY vertical offset
* @return repaint rectangle
*/
public Rectangle getRepaintRectangle(int offsetY) {
int rectSize = (int) (chessboard.SQUARE_SIZE*chessboard.boardScale);
double x = getRealX()*chessboard.boardScale+chessboard.startX;
@ -288,6 +363,12 @@ public abstract class APiece {
);
}
/**
* Trace the piece's path in X, Y direction
* @param moves two-dimensional array of moves
* @param xDirection X direction
* @param yDirection Y direction
*/
public void tracePath(boolean[][] moves, int xDirection, int yDirection) {
int i = x + xDirection;
int j = y + yDirection;
@ -300,26 +381,34 @@ public abstract class APiece {
}
}
/**
* Get piece's possible moves
* @return two-dimensional array of moves
*/
public boolean[][] getPossibleMoves() {
return getPossibleMoves(false);
}
/**
* Get piece's possible moves
* @param attack force the attack mode
* @return two-dimensional array of moves
*/
abstract public boolean[][] getPossibleMoves(boolean attack);
/**
* Paint the piece
* @param g2 Graphics2D
*/
abstract public void paint(Graphics2D g2);
public Player getPlayer() {
return player;
}
public int getMoveCount() {
return moveCount;
}
public void setMoveCount(int moveCount) {
this.moveCount = moveCount;
}
/**
* Animate piece's move
* @param fromX original X position
* @param fromY original Y position
* @param toX new X position
* @param toY new Y position
*/
public void animateMove(int fromX, int fromY, int toX, int toY) {
double startX = chessboard.startX + (fromX*chessboard.SQUARE_SIZE)*chessboard.boardScale;
double startY = chessboard.startY + (fromY*chessboard.SQUARE_SIZE)*chessboard.boardScale;
@ -348,6 +437,10 @@ public abstract class APiece {
}, 2, 2);
}
/**
* Remove the piece
* @param animate animate piece's removal
*/
public void remove(boolean animate) {
chessboard.removePiece(x, y);
if(!animate) return;
@ -367,8 +460,4 @@ public abstract class APiece {
}, 10, 10);
}
public double getScale() {
return scale;
}
}

View file

@ -41,6 +41,11 @@ public class Bishop extends APiece {
}
/**
* Get Bishop's possible moves
* @param attack force the attack mode
* @return two-dimensional array of possible moves
*/
@Override
public boolean[][] getPossibleMoves(boolean attack) {
boolean[][] moves = new boolean[chessboard.SQUARE_COUNT][chessboard.SQUARE_COUNT];

View file

@ -1,7 +1,6 @@
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.renderer.category.BarRenderer;
@ -18,7 +17,6 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
/**
@ -35,7 +33,7 @@ public class Chess {
static final String DEFAULT_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
/**
* Create the main window and chessboard with pieces
* Create the main window and the chessboard
* @param args command-line arguments
*/
public static void main(String[] args) {
@ -57,6 +55,9 @@ public class Chess {
}
/**
* Try to inherit design from the OS
*/
public static void setLookAndFeel() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
@ -65,6 +66,10 @@ public class Chess {
}
}
/**
* Create window's menu bar
* @return menu bar
*/
public static JMenuBar createMenuBar() {
menuBar = new JMenuBar();
@ -76,6 +81,10 @@ public class Chess {
return menuBar;
}
/**
* Create Game menu
* @return Game menu
*/
public static JMenu createMenuGame() {
JMenu menuGame = new JMenu("Game");
@ -123,6 +132,10 @@ public class Chess {
}
/**
* Create Theme menu
* @return Theme menu
*/
public static JMenu createMenuTheme() {
JMenu menuTheme = new JMenu("Theme");
@ -154,6 +167,10 @@ public class Chess {
}
/**
* Create CPU menu
* @return CPU menu
*/
public static JMenu createMenuCPU() {
JMenu menuCPU = new JMenu("CPU");
@ -181,34 +198,40 @@ public class Chess {
};
whiteDisabled.addActionListener(l -> {
setCPU(chessboard.getPlayer1(), null, updateWhite);
setCPU(chessboard.getPlayer1(), null);
updateWhite.run();
});
menuCPU.add(whiteDisabled);
whiteAutomaticRandom.addActionListener(l -> {
setCPU(chessboard.getPlayer1(), "random", updateWhite);
setCPU(chessboard.getPlayer1(), "random");
updateWhite.run();
});
menuCPU.add(whiteAutomaticRandom);
whiteAutomaticSmart.addActionListener(l -> {
setCPU(chessboard.getPlayer1(), "smart", updateWhite);
setCPU(chessboard.getPlayer1(), "smart");
updateWhite.run();
});
menuCPU.add(whiteAutomaticSmart);
menuCPU.add(new JPopupMenu.Separator());
blackDisabled.addActionListener(l -> {
setCPU(chessboard.getPlayer2(), null, updateBlack);
setCPU(chessboard.getPlayer2(), null);
updateBlack.run();
});
menuCPU.add(blackDisabled);
blackAutomaticRandom.addActionListener(l -> {
setCPU(chessboard.getPlayer2(), "random", updateBlack);
setCPU(chessboard.getPlayer2(), "random");
updateBlack.run();
});
menuCPU.add(blackAutomaticRandom);
blackAutomaticSmart.addActionListener(l -> {
setCPU(chessboard.getPlayer2(), "smart", updateBlack);
setCPU(chessboard.getPlayer2(), "smart");
updateBlack.run();
});
menuCPU.add(blackAutomaticSmart);
@ -219,6 +242,10 @@ public class Chess {
}
/**
* Create Export menu
* @return Export menu
*/
public static JMenu createMenuExport() {
JMenu menuExport = new JMenu("Export");
@ -235,7 +262,12 @@ public class Chess {
}
public static void setCPU(Player player, String mode, Runnable update) {
/**
* Set player's CPU mode
* @param player selected player
* @param mode null, "random" or "smart"
*/
public static void setCPU(Player player, String mode) {
if(mode != null && mode.equals("smart")) {
String binary = Stockfish.findBinary();
if(binary == null) {
@ -253,9 +285,11 @@ public class Chess {
}
}
player.setCPU(mode);
update.run();
}
/**
* Repaint entire window
*/
public static void repaintWindow() {
// Hack to force repaint of the chessboard (repaint is not working)
int winWidth = window.getWidth();
@ -264,6 +298,10 @@ public class Chess {
window.setSize(winWidth, winHeight);
}
/**
* Starts a new game with specified chessboard
* @param newChessboard chessboard
*/
public static void newGame(Chessboard newChessboard) {
if(chessboard != null) window.remove(chessboard);
chessboard = newChessboard;
@ -274,6 +312,9 @@ public class Chess {
repaintWindow();
}
/**
* Export current chessboard to SVG file
*/
public static void exportSVG() {
SVGGraphics2D g2 = new SVGGraphics2D(1000, 1000);
chessboard.paint(g2, 1000, 1000);
@ -288,6 +329,10 @@ public class Chess {
}
}
/**
* Export current chessboard to PNG file
*/
public static void exportPNG() {
BufferedImage image = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
@ -301,6 +346,9 @@ public class Chess {
}
}
/**
* Show window with statistics
*/
public static void showStatistics() {
JFrame window = new JFrame();
window.setTitle("Statistics");

View file

@ -49,22 +49,49 @@ public class Chessboard extends JPanel {
*/
public APiece[][] pieces = new APiece[SQUARE_COUNT][SQUARE_COUNT];
/**
* Set of removed pieces
*/
public Set<APiece> removedPieces = new HashSet<>();
/**
* Two-dimensional boolean array of shown possible moves
*/
public boolean[][] possibleMoves = new boolean[SQUARE_COUNT][SQUARE_COUNT];
/**
* Two-dimensional boolean array of shown last move
*/
public boolean[][] lastMove = new boolean[SQUARE_COUNT][SQUARE_COUNT];
/**
* Currently active player
*/
private Player activePlayer;
/**
* White player instance
*/
private Player player1;
/**
* Black player instance
*/
private Player player2;
/**
* Blind mode toggle
*/
public boolean blindMode = false;
/**
* Current theme instance
*/
static private Theme theme = Theme.activeTheme;
/**
* Last move time in ms
*/
private long lastMoveTime = System.currentTimeMillis();
/**
@ -78,6 +105,11 @@ public class Chessboard extends JPanel {
}
/**
* Chessboard creation from FEN
* @param input FEN
* @return Chessbord instance or null, if FEN is invalid
*/
public static Chessboard fromFEN(String input) {
String[] parts = input.split(" ");
@ -148,6 +180,10 @@ public class Chessboard extends JPanel {
}
/**
* Get current FEN of the chessboard
* @return FEN
*/
public String toFEN() {
StringBuilder chessboardSB = new StringBuilder();
@ -313,12 +349,23 @@ public class Chessboard extends JPanel {
}
/**
* Paint dark or light square
* @param g Graphics context
* @param isBlack whether square is dark or not
*/
private void paintSquare(Graphics g, boolean isBlack) {
if(isBlack) g.setColor(theme.darkSquare);
else g.setColor(theme.lightSquare);
g.fillRect(0, 0, SQUARE_SIZE+1, SQUARE_SIZE+1);
}
/**
* Paint last move and possible moves
* @param g Graphics context
* @param x X position
* @param y Y position
*/
private void paintHighlights(Graphics g, int x, int y) {
if(lastMove != null && lastMove[y][x]) {
g.setColor(new Color(50, 50, 250, 30));
@ -332,6 +379,11 @@ public class Chessboard extends JPanel {
}
}
/**
* Paint piece
* @param g2 Graphics2D context
* @param piece painted piece
*/
private void paintPiece(Graphics2D g2, APiece piece) {
g2.translate(20+piece.getRealX(), 20+piece.getRealY());
double pieceScale = piece.getScale();
@ -343,10 +395,17 @@ public class Chessboard extends JPanel {
piece.paint(g2);
}
/**
* Repaint entire root pane
*/
public void repaintRootPane() {
repaintRootPane(null);
}
/**
* Repaint rectangle on the root pane
* @param r repaint rectangle
*/
public void repaintRootPane(Rectangle r) {
JComponent pane = getRootPane();
if(pane != null) {
@ -355,6 +414,10 @@ public class Chessboard extends JPanel {
}
}
/**
* Set active theme
* @param newTheme new theme
*/
public static void setTheme(Theme newTheme) {
theme = newTheme;
}
@ -420,6 +483,12 @@ public class Chessboard extends JPanel {
return selected;
}
/**
* Check if piece on specified XY position
* @param x piece's X position
* @param y piece's Y position
* @return whether piece is endangered
*/
public boolean isEndangered(int x, int y) {
APiece piece = getPiece(x, y);
@ -469,47 +538,92 @@ public class Chessboard extends JPanel {
}
/**
* Set current possible moves
* @param moves two-dimensional boolean array of possible moves
*/
public void showPossibleMoves(boolean[][] moves) {
possibleMoves = moves;
}
/**
* Set last move
* @param move two-dimensional boolean array with from-to positions
*/
public void showLastMove(boolean[][] move) {
lastMove = move;
}
/**
* Check if XY position is on the chessboard or not
* @param x X position
* @param y Y position
* @return whether XY position is on the chessboard
*/
public boolean isOnBoard(int x, int y) {
return !(y < 0 || y >= SQUARE_COUNT || x < 0 || x >= SQUARE_COUNT);
}
/**
* Set first player
* @param player white player instance
*/
public void setPlayer1(Player player) {
activePlayer = player;
player1 = player;
}
/**
* Set second player
* @param player black player instance
*/
public void setPlayer2(Player player) {
player2 = player;
}
public Player getActivePlayer() {
return activePlayer;
}
public void setActivePlayer(Player player) {
activePlayer = player;
}
public Player getOtherPlayer() {
return activePlayer == player1 ? player2 : player1;
}
/**
* Get first player
* @return white player instance
*/
public Player getPlayer1() {
return player1;
}
/**
* Get second player
* @return black player instance
*/
public Player getPlayer2() {
return player2;
}
/**
* Get active player
* @return active player instance
*/
public Player getActivePlayer() {
return activePlayer;
}
/**
* Set active player
* @param player player instance
*/
public void setActivePlayer(Player player) {
activePlayer = player;
}
/**
* Get non-active player
* @return non-active player instance
*/
public Player getOtherPlayer() {
return activePlayer == player1 ? player2 : player1;
}
/**
* Check if some specified game state is or is not met
*/
public void checkGame() {
Player[] players = new Player[]{player1, player2};
String[] playerNames = new String[]{"White", "Black"};
@ -528,6 +642,10 @@ public class Chessboard extends JPanel {
}
}
/**
* Display message after 1 second
* @param msg displayed message
*/
public void delayedMessage(String msg) {
Timer t = new Timer();
t.schedule(new TimerTask() {
@ -538,6 +656,9 @@ public class Chessboard extends JPanel {
}, 1000);
}
/**
* End player's move, set other player as active and check game state
*/
public void changeActivePlayer() {
long now = System.currentTimeMillis();
activePlayer.addDelay((int) (now-lastMoveTime));

View file

@ -2,7 +2,7 @@ import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
/**
* Chessboard mouse adapter class
* Chessboard mouse adapter class for detecting user input on the chessboard
*/
public class ChessboardMouseAdapter extends MouseAdapter {
@ -11,7 +11,7 @@ public class ChessboardMouseAdapter extends MouseAdapter {
boolean dragging;
/**
* Detect mouse press and select the piece
* Detect mouse press
* @param me mouse event
*/
public void mousePressed(MouseEvent me) {
@ -21,7 +21,7 @@ public class ChessboardMouseAdapter extends MouseAdapter {
}
/**
* Detect mouse dragging and show floating piece
* Detect mouse dragging
* @param me mouse event
*/
@Override
@ -34,7 +34,7 @@ public class ChessboardMouseAdapter extends MouseAdapter {
}
/**
* Detect mouse release and place piece on the chessboard
* Detect mouse release
* @param me mouse event
*/
@Override
@ -43,7 +43,10 @@ public class ChessboardMouseAdapter extends MouseAdapter {
else if(lastClicked > System.currentTimeMillis()) onClick(me);
}
/**
* Select, unselect or move the piece on the chessboard
* @param me mouse event
*/
public void onClick(MouseEvent me) {
Chessboard c = (Chessboard) me.getSource();
PiecePosition pos = c.getPieceCoordinates(me.getX(), me.getY());
@ -55,7 +58,7 @@ public class ChessboardMouseAdapter extends MouseAdapter {
c.repaintRootPane();
} else if(selectedPiece != null) {
if(selectedPiece.getPossibleMoves()[pos.y][pos.x]) {
selectedPiece.move(pos);
selectedPiece.move(pos.x, pos.y);
c.changeActivePlayer();
} else {
c.repaintRootPane();
@ -65,6 +68,10 @@ public class ChessboardMouseAdapter extends MouseAdapter {
}
}
/**
* Select piece on drag start
* @param me mouse event
*/
public void onDragStart(MouseEvent me) {
Chessboard c = (Chessboard) me.getSource();
PiecePosition pos = c.getPieceCoordinates(me.getX(), me.getY());
@ -76,6 +83,10 @@ public class ChessboardMouseAdapter extends MouseAdapter {
}
}
/**
* Render and move selected piece on drag
* @param me
*/
public void onDrag(MouseEvent me) {
Chessboard c = (Chessboard) me.getSource();
APiece piece = c.getSelectedPiece();
@ -88,13 +99,17 @@ public class ChessboardMouseAdapter extends MouseAdapter {
}
}
/**
* Move piece to the new position on drag end
* @param me mouse event
*/
public void onDragEnd(MouseEvent me) {
Chessboard c = (Chessboard) me.getSource();
APiece piece = c.getSelectedPiece();
PiecePosition pos = c.getPieceCoordinates(me.getX(), me.getY());
if(piece != null) {
if(piece.getPossibleMoves()[pos.y][pos.x]) {
piece.move(pos, false);
piece.move(pos.x, pos.y, false);
c.changeActivePlayer();
} else {
c.repaintRootPane();
@ -104,6 +119,12 @@ public class ChessboardMouseAdapter extends MouseAdapter {
c.showPossibleMoves(null);
}
/**
* Detect if selected piece can be moved
* @param chessboard chessboard instance
* @param piece selected piece
* @return true, if you can move the piece or false
*/
public boolean canMove(Chessboard chessboard, APiece piece) {
if(piece == null) return false;
Player player = piece.getPlayer();

View file

@ -33,6 +33,11 @@ public class King extends APiece {
}
/**
* Get King's possible moves
* @param attack force the attack mode
* @return two-dimensional array of moves
*/
@Override
public boolean[][] getPossibleMoves(boolean attack) {
boolean[][] moves = new boolean[chessboard.SQUARE_COUNT][chessboard.SQUARE_COUNT];
@ -46,6 +51,11 @@ public class King extends APiece {
return moves;
}
/**
* Check if left (-1) or right (1) castling is possible
* @param direction -1 for left, 1 for right
* @return castling possibility
*/
private boolean checkCastling(int direction) {
if(moveCount == 0 && !player.inCheck() && checkRook(direction)) {
for (int pX = x + direction; (pX > 0 && pX < 7); pX += direction) {
@ -56,22 +66,33 @@ public class King extends APiece {
return false;
}
/**
* Check if left or right Rook is able to do castling
* @param direction -1 for left Rook, 1 for right Rook
* @return rook castling ability
*/
private boolean checkRook(int direction) {
int rookX = direction == -1 ? 0 : 7;
APiece rook = chessboard.getPiece(rookX, y);
return rook != null && rook instanceof Rook && rook.getMoveCount() == 0;
}
/**
* Move the piece to the new position
* @param newX new X position
* @param newY new Y position
* @param animate animate piece's move
*/
@Override
public void move(PiecePosition pos, boolean animate) {
super.move(pos, animate);
public void move(int newX, int newY, boolean animate) {
super.move(newX, newY, animate);
if(moveCount == 1 && x == 6) {
APiece rook = chessboard.getPiece(7, y);
if(rook != null) rook.setPosition(new PiecePosition(5, y));
if(rook != null) rook.setPosition(5, y);
}
if(moveCount == 1 && x == 1) {
APiece rook = chessboard.getPiece(0, y);
if(rook != null) rook.setPosition(new PiecePosition(3, y));
if(rook != null) rook.setPosition(3, y);
}
}
}

View file

@ -29,6 +29,11 @@ public class Knight extends APiece {
}
/**
* Get Knight's possible moves
* @param attack force the attack mode
* @return two-dimensional array of moves
*/
@Override
public boolean[][] getPossibleMoves(boolean attack) {
boolean[][] moves = new boolean[chessboard.SQUARE_COUNT][chessboard.SQUARE_COUNT];

View file

@ -28,6 +28,11 @@ public class Pawn extends APiece {
}
/**
* Get Pawn's possible moves
* @param attack force the attack mode
* @return two-dimensional array of moves
*/
@Override
public boolean[][] getPossibleMoves(boolean attack) {
@ -51,6 +56,11 @@ public class Pawn extends APiece {
}
/**
* Check if en passant possible
* @param directionX en passant X direction
* @return true, if possible
*/
private boolean checkEnPassant(int directionX) {
APiece piece = chessboard.getPiece(x+directionX, y);
if(piece == null) return false;
@ -58,17 +68,23 @@ public class Pawn extends APiece {
return chessboard.lastMove[y][x+directionX] && piece instanceof Pawn && y == pieceY && piece.getMoveCount() == 1;
}
/**
* Move the piece to the new position
* @param newX new X position
* @param newY new Y position
* @param animate animate piece's move
*/
@Override
public void move(PiecePosition pos, boolean animate) {
public void move(int newX, int newY, boolean animate) {
for (int directionX : new int[]{-1, 1}) {
if(checkEnPassant(directionX) && pos.x == x+directionX) {
if(checkEnPassant(directionX) && newX == x+directionX) {
APiece piece = chessboard.getPiece(x+directionX, y);
if(piece != null) piece.remove(true);
}
}
super.move(pos, animate);
super.move(newX, newY, animate);
int changeY = player.getStartPosition() == StartPosition.BOTTOM ? 0 : chessboard.SQUARE_COUNT-1;
if(y == changeY) {

View file

@ -34,11 +34,20 @@ public enum PieceColor {
setColors(fillColor, drawColor);
}
/**
* Set piece's fill and draw colors
* @param fillColor piece's fill color
* @param drawColor piece's border color
*/
public void setColors(Color fillColor, Color drawColor) {
fill = fillColor;
draw = drawColor;
}
/**
* Set theme to the piece color
* @param theme new theme
*/
public static void setTheme(Theme theme) {
PieceColor.WHITE.setColors(theme.whiteFill, theme.whiteBorder);
PieceColor.BLACK.setColors(theme.blackFill, theme.blackBorder);

View file

@ -13,10 +13,19 @@ public class PiecePosition {
*/
public int y;
/**
* Conversion to the string
* @return string representation
*/
public String toString() {
return String.valueOf((char) (x+97)) + (8-y);
}
/**
* Parse PiecePosition from String
* @param position parsed String
* @return PiecePosition or null
*/
public static PiecePosition fromString(String position) {
try {
int x = position.charAt(0) - 97;

View file

@ -1,31 +1,69 @@
import java.util.*;
/**
* Player class
*/
public class Player {
/**
* Player's chessboard
*/
private Chessboard chessboard;
/**
* Player's piece color
*/
private PieceColor color;
/**
* Player's start position
*/
private StartPosition startPosition;
/**
* King piece
*/
private King king;
/**
* Active CPU mode
*/
private String cpu;
/**
* Waiting for CPU move
*/
private boolean isPlaying = false;
/**
* Delays between player's moves
*/
private ArrayList<Integer> delays = new ArrayList<>();
/**
* Player's constructor
* @param chessboard player's chessboard
* @param startPosition player's start position
* @param color player's color
*/
public Player(Chessboard chessboard, StartPosition startPosition, PieceColor color) {
this.chessboard = chessboard;
this.color = color;
this.startPosition = startPosition;
}
/**
* Detects if player is in check
* @return true, if in check
*/
public boolean inCheck() {
return king.isEndangered();
}
/**
* Detects if player has no possible moves
* @return true, if no possible moves
*/
public boolean hasNoPossibleMove() {
for (APiece piece : getPieces()) {
boolean[][] possibleMoves = piece.getPossibleMoves();
@ -38,6 +76,9 @@ public class Player {
return true;
}
/**
* Play automatically as CPU
*/
public void play() {
if(cpu == null) return;
isPlaying = true;
@ -55,6 +96,10 @@ public class Player {
}, 1000);
}
/**
* Select random piece and move it randomly
* @return true, if piece was moved
*/
public boolean randomMove() {
ArrayList<APiece> myPieces = getPieces();
while(myPieces.size() != 0) {
@ -78,13 +123,17 @@ public class Player {
}
PiecePosition randomMove = possibleMoves.get(r.nextInt(moveCount));
selectedPiece.move(randomMove, true);
selectedPiece.move(randomMove.x, randomMove.y, true);
chessboard.changeActivePlayer();
return true;
}
return false;
}
/**
* Move piece according to the stockfish engine
* @return true, if piece was moved
*/
public boolean smartMove() {
Stockfish stockfish = Stockfish.getInstance();
if(stockfish == null) return false;
@ -95,31 +144,55 @@ public class Player {
PiecePosition toPos = PiecePosition.fromString(bestMove.substring(2, 4));
if(fromPos == null || toPos == null) return false;
APiece piece = chessboard.getPiece(fromPos.x, fromPos.y);
piece.move(toPos);
piece.move(toPos.x, toPos.y);
chessboard.changeActivePlayer();
return true;
}
/**
* Get player's chessboard
* @return chessboard
*/
public Chessboard getChessboard() {
return chessboard;
}
/**
* Get player's piece color
* @return piece color
*/
public PieceColor getColor() {
return color;
}
/**
* Get player's start position
* @return start position
*/
public StartPosition getStartPosition() {
return startPosition;
}
/**
* Get player's King piece
* @return King
*/
public King getKing() {
return king;
}
/**
* Set player's King piece
* @param king new King
*/
public void setKing(King king) {
this.king = king;
}
/**
* Get all player's pieces
* @return player's pieces
*/
public ArrayList<APiece> getPieces() {
ArrayList<APiece> myPieces = new ArrayList<>();
for (APiece[] pieces : chessboard.pieces) {
@ -132,19 +205,36 @@ public class Player {
return myPieces;
}
/**
* Set player's CPU mode
* @param cpu CPU mode
*/
public void setCPU(String cpu) {
// TODO: use enums
this.cpu = cpu;
if(chessboard.getActivePlayer() == this && !isPlaying) play();
}
/**
* Get player's CPU mode
* @return CPU mode
*/
public String getCPU() {
return cpu;
}
/**
* Add new delay before last move
* @param delay delay in ms
*/
public void addDelay(int delay) {
delays.add(delay);
}
/**
* Get all delays between moves
* @return delays list
*/
public List<Integer> getDelays() {
return delays;
}

View file

@ -41,6 +41,11 @@ public class Queen extends APiece {
}
/**
* Get Queen's possible moves
* @param attack force the attack mode
* @return two-dimensional array of moves
*/
@Override
public boolean[][] getPossibleMoves(boolean attack) {
boolean[][] moves = new boolean[chessboard.SQUARE_COUNT][chessboard.SQUARE_COUNT];

View file

@ -29,6 +29,11 @@ public class Rook extends APiece {
}
/**
* Get Rook's possible moves
* @param attack force the attack mode
* @return two-dimensional array of moves
*/
@Override
public boolean[][] getPossibleMoves(boolean attack) {
boolean[][] moves = new boolean[chessboard.SQUARE_COUNT][chessboard.SQUARE_COUNT];

View file

@ -1,3 +1,6 @@
/**
* Starting position of the player
*/
public enum StartPosition {
TOP, BOTTOM;

View file

@ -2,22 +2,52 @@ import java.io.*;
import java.nio.file.Paths;
import java.util.ArrayList;
/**
* Stockfish class
*/
public class Stockfish {
/**
* Stockfish process
*/
private Process process;
/**
* Process reader
*/
private BufferedReader reader;
/**
* Process writer
*/
private OutputStreamWriter writer;
/**
* Singleton Stockfish instance
*/
public static Stockfish instance;
/**
* Create instance
* @param binary stockfish binary path
* @throws IOException
*/
public static void create(String binary) throws IOException {
instance = new Stockfish(binary);
}
/**
* Get singleton Stockfish instance
* @return instance
*/
public static Stockfish getInstance() {
return instance;
}
/**
* Get stockfish binary path
* @return stockfish binary
*/
public static String findBinary() {
ArrayList<String> paths = new ArrayList<>();
paths.add(".");
@ -33,12 +63,21 @@ public class Stockfish {
return null;
}
/**
* Constructor which starts the stockfish engine
* @param binary stockfish binary path
* @throws IOException
*/
public Stockfish(String binary) throws IOException {
process = Runtime.getRuntime().exec(binary);
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
writer = new OutputStreamWriter(process.getOutputStream());
}
/**
* Send command to the process
* @param command stockfish command
*/
public void sendCommand(String command) {
try {
writer.write(command + "\n");
@ -48,6 +87,12 @@ public class Stockfish {
}
}
/**
* Get stockfish output
* @param waitTime wait time in ms
* @return stockfish output
* @throws Exception
*/
public String getOutput(int waitTime) throws Exception {
StringBuffer buffer = new StringBuffer();
Thread.sleep(waitTime);
@ -62,6 +107,13 @@ public class Stockfish {
return buffer.toString();
}
/**
* Get the best possible move
* @param fen FEN of the chessboard
* @param skill stockfish skill level
* @param waitTime wait time in ms
* @return the best possible move
*/
public String getBestMove(String fen, int skill, int waitTime) {
sendCommand("setoption name Skill Level value " + skill);
sendCommand("position fen " + fen);
@ -79,6 +131,9 @@ public class Stockfish {
return parts[1].split(" ")[0];
}
/**
* Stop the stockfish engine
*/
public void stopEngine() {
try {
sendCommand("quit");

View file

@ -1,5 +1,8 @@
import java.awt.*;
/**
* Color themes for the chessboard and pieces
*/
public enum Theme {
CLASSIC(
@ -18,21 +21,60 @@ public enum Theme {
Color.decode("#FF8A80"), Color.decode("#e2514c")
);
/**
* Current active theme
*/
public static Theme activeTheme = Theme.CLASSIC;
/**
* Set new active theme
* @param theme selected theme
*/
public static void setTheme(Theme theme) {
activeTheme = theme;
Chessboard.setTheme(theme);
PieceColor.setTheme(theme);
}
/**
* White piece's fill color
*/
Color whiteFill;
/**
* White piece's border color
*/
Color whiteBorder;
/**
* Black piece's fill color
*/
Color blackFill;
/**
* Black piece's border color
*/
Color blackBorder;
/**
* Chessboard light square's color
*/
Color lightSquare;
/**
* Chessboard dark square's color
*/
Color darkSquare;
/**
* Constructor of the theme
* @param whiteFill white piece's fill
* @param whiteBorder white piece's border
* @param blackFill black piece's fill
* @param blackBorder black piece's border
* @param lightSquare light square's color
* @param darkSquare dark square's color
*/
Theme(Color whiteFill, Color whiteBorder, Color blackFill, Color blackBorder, Color lightSquare, Color darkSquare) {
this.whiteFill = whiteFill;
this.whiteBorder = whiteBorder;