From 91608f1f7b47331790c62adc3221420ba34219f1 Mon Sep 17 00:00:00 2001 From: Filip Znachor Date: Sat, 6 May 2023 22:54:26 +0200 Subject: [PATCH] Added javadoc --- src/APiece.java | 201 +++++++++++++++++++++++--------- src/Bishop.java | 5 + src/Chess.java | 70 +++++++++-- src/Chessboard.java | 145 +++++++++++++++++++++-- src/ChessboardMouseAdapter.java | 35 ++++-- src/King.java | 29 ++++- src/Knight.java | 5 + src/Pawn.java | 22 +++- src/PieceColor.java | 9 ++ src/PiecePosition.java | 9 ++ src/Player.java | 96 ++++++++++++++- src/Queen.java | 5 + src/Rook.java | 5 + src/StartPosition.java | 3 + src/Stockfish.java | 55 +++++++++ src/Theme.java | 42 +++++++ 16 files changed, 640 insertions(+), 96 deletions(-) diff --git a/src/APiece.java b/src/APiece.java index 85d6a65..ead9e87 100644 --- a/src/APiece.java +++ b/src/APiece.java @@ -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; - } - } \ No newline at end of file diff --git a/src/Bishop.java b/src/Bishop.java index 8094772..924fffc 100644 --- a/src/Bishop.java +++ b/src/Bishop.java @@ -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]; diff --git a/src/Chess.java b/src/Chess.java index 2aba2da..f2103b3 100644 --- a/src/Chess.java +++ b/src/Chess.java @@ -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"); diff --git a/src/Chessboard.java b/src/Chessboard.java index af5c248..431c329 100644 --- a/src/Chessboard.java +++ b/src/Chessboard.java @@ -49,22 +49,49 @@ public class Chessboard extends JPanel { */ public APiece[][] pieces = new APiece[SQUARE_COUNT][SQUARE_COUNT]; + /** + * Set of removed pieces + */ public Set 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)); diff --git a/src/ChessboardMouseAdapter.java b/src/ChessboardMouseAdapter.java index 9d019de..5f0e01e 100644 --- a/src/ChessboardMouseAdapter.java +++ b/src/ChessboardMouseAdapter.java @@ -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(); diff --git a/src/King.java b/src/King.java index 2409138..11fe158 100644 --- a/src/King.java +++ b/src/King.java @@ -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); } } } diff --git a/src/Knight.java b/src/Knight.java index 32ae25d..a947e7b 100644 --- a/src/Knight.java +++ b/src/Knight.java @@ -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]; diff --git a/src/Pawn.java b/src/Pawn.java index 125942f..8b484d9 100644 --- a/src/Pawn.java +++ b/src/Pawn.java @@ -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) { diff --git a/src/PieceColor.java b/src/PieceColor.java index fd17b69..2713e02 100644 --- a/src/PieceColor.java +++ b/src/PieceColor.java @@ -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); diff --git a/src/PiecePosition.java b/src/PiecePosition.java index a6ac15d..3346ed8 100644 --- a/src/PiecePosition.java +++ b/src/PiecePosition.java @@ -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; diff --git a/src/Player.java b/src/Player.java index e618cce..c71c21a 100644 --- a/src/Player.java +++ b/src/Player.java @@ -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 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 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 getPieces() { ArrayList 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 getDelays() { return delays; } diff --git a/src/Queen.java b/src/Queen.java index 160b33c..3801184 100644 --- a/src/Queen.java +++ b/src/Queen.java @@ -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]; diff --git a/src/Rook.java b/src/Rook.java index 119d615..c96819f 100644 --- a/src/Rook.java +++ b/src/Rook.java @@ -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]; diff --git a/src/StartPosition.java b/src/StartPosition.java index 82f2b0d..f94e9c3 100644 --- a/src/StartPosition.java +++ b/src/StartPosition.java @@ -1,3 +1,6 @@ +/** + * Starting position of the player + */ public enum StartPosition { TOP, BOTTOM; diff --git a/src/Stockfish.java b/src/Stockfish.java index e84c5fe..c3ec695 100644 --- a/src/Stockfish.java +++ b/src/Stockfish.java @@ -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 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"); diff --git a/src/Theme.java b/src/Theme.java index 2ee944a..bbce60a 100644 --- a/src/Theme.java +++ b/src/Theme.java @@ -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;