diff --git a/src/dev/kske/chess/board/Bishop.java b/src/dev/kske/chess/board/Bishop.java index 9412a30..ec2f388 100644 --- a/src/dev/kske/chess/board/Bishop.java +++ b/src/dev/kske/chess/board/Bishop.java @@ -7,12 +7,18 @@ import java.util.List; * Project: Chess
* File: Bishop.java
* Created: 01.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ public class Bishop extends Piece { + /** + * Creates bishop {@link Piece}. + * + * @param color the color of this bishop + * @param board the board on which this bishop will be placed + */ public Bishop(Color color, Board board) { super(color, board); } diff --git a/src/dev/kske/chess/board/Board.java b/src/dev/kske/chess/board/Board.java index c634dff..51bf632 100644 --- a/src/dev/kske/chess/board/Board.java +++ b/src/dev/kske/chess/board/Board.java @@ -1,11 +1,6 @@ package dev.kske.chess.board; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import dev.kske.chess.board.Piece.Color; import dev.kske.chess.event.EventBus; @@ -65,18 +60,17 @@ public class Board { public boolean attemptMove(Move move) { Piece piece = getPos(move); if (piece == null || !piece.isValidMove(move)) return false; - else { - // Move piece - move(move); - // Revert move if it caused a check for its team - if (checkCheck(piece.getColor())) { - revert(); - return false; - } + // Move piece + move(move); - return true; + // Revert move if it caused a check for its team + if (checkCheck(piece.getColor())) { + revert(); + return false; } + + return true; } /** @@ -122,6 +116,10 @@ public class Board { log.removeLast(); } + /** + * Reverts the last move without removing it from the log. After that, a + * {@link MoveEvent} is dispatched containing the inverse of the reverted move. + */ public void selectPreviousNode() { MoveNode moveNode = log.getLast(); Move move = moveNode.move; @@ -133,9 +131,15 @@ public class Board { log.selectPreviousNode(); // Dispatch move event - EventBus.getInstance().dispatch(new MoveEvent(move.invert(), getGameEventType(log.getActiveColor().opposite()))); + EventBus.getInstance().dispatch(new MoveEvent(move.invert(), getState(log.getActiveColor().opposite()))); } + /** + * Applies the next move stored in the log. After that, a {@link MoveEvent} is + * dispatched. + * + * @param index the variation index of the move to select + */ public void selectNextNode(int index) { log.selectNextNode(index); MoveNode moveNode = log.getLast(); @@ -145,7 +149,7 @@ public class Board { move.execute(this); // Dispatch move event - EventBus.getInstance().dispatch(new MoveEvent(move, getGameEventType(log.getActiveColor().opposite()))); + EventBus.getInstance().dispatch(new MoveEvent(move, getState(log.getActiveColor().opposite()))); } /** @@ -162,6 +166,12 @@ public class Board { return moves; } + /** + * Delegate method for {@link Piece#getMoves(Position)}. + * + * @param pos the position of the piece to invoke the method on + * @return a list of legal moves generated for the piece + */ public List getMoves(Position pos) { return get(pos).getMoves(pos); } /** @@ -198,18 +208,24 @@ public class Board { public boolean checkCheckmate(Color color) { // Return false immediately if the king can move if (!getMoves(kingPos.get(color)).isEmpty()) return false; - else { - for (Move move : getMoves(color)) { - move(move); - boolean check = checkCheck(color); - revert(); - if (!check) return false; - } - return true; + + for (Move move : getMoves(color)) { + move(move); + boolean check = checkCheck(color); + revert(); + if (!check) return false; } + return true; } - public BoardState getGameEventType(Color color) { + /** + * Checks whether the a check, checkmate, stalemate of none of the above is + * currently present. + * + * @param color the color to evaluate the board for + * @return the current {@link BoardState} + */ + public BoardState getState(Color color) { return checkCheck(color) ? checkCheckmate(color) ? BoardState.CHECKMATE : BoardState.CHECK : getMoves(color).isEmpty() || log.getLast().halfmoveClock >= 50 ? BoardState.STALEMATE : BoardState.NORMAL; } diff --git a/src/dev/kske/chess/board/BoardState.java b/src/dev/kske/chess/board/BoardState.java index b8db707..4b27887 100644 --- a/src/dev/kske/chess/board/BoardState.java +++ b/src/dev/kske/chess/board/BoardState.java @@ -4,10 +4,11 @@ package dev.kske.chess.board; * Project: Chess
* File: BoardState.java
* Created: 07.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ +@SuppressWarnings("javadoc") public enum BoardState { CHECK, CHECKMATE, STALEMATE, NORMAL; } diff --git a/src/dev/kske/chess/board/Castling.java b/src/dev/kske/chess/board/Castling.java index 9f973ad..1c499fb 100644 --- a/src/dev/kske/chess/board/Castling.java +++ b/src/dev/kske/chess/board/Castling.java @@ -12,11 +12,25 @@ public class Castling extends Move { private final Move rookMove; + /** + * Creates a castling move. + * + * @param pos the position of this castling move + * @param dest the destination of this castling move + */ public Castling(Position pos, Position dest) { super(pos, dest); rookMove = dest.x == 6 ? new Move(7, pos.y, 5, pos.y) : new Move(0, pos.y, 3, pos.y); } + /** + * Creates a castling move. + * + * @param xPos the horizontal position of this castling move + * @param yPos the vertical position of this castling move + * @param xDest the horizontal destination of this castling move + * @param yDest the vertical destination of this castling move + */ public Castling(int xPos, int yPos, int xDest, int yDest) { this(new Position(xPos, yPos), new Position(xDest, yDest)); } @Override diff --git a/src/dev/kske/chess/board/EnPassant.java b/src/dev/kske/chess/board/EnPassant.java index 69c48d9..b9d0aec 100644 --- a/src/dev/kske/chess/board/EnPassant.java +++ b/src/dev/kske/chess/board/EnPassant.java @@ -4,7 +4,7 @@ package dev.kske.chess.board; * Project: Chess
* File: EnPassant.java
* Created: 2 Nov 2019
- * + * * @since Chess v0.5-alpha * @author Kai S. K. Engelbart */ @@ -12,11 +12,25 @@ public class EnPassant extends Move { private final Position capturePos; + /** + * Initializes an en passant move. + * + * @param pos the position of this move + * @param dest the destination of this move + */ public EnPassant(Position pos, Position dest) { super(pos, dest); capturePos = new Position(dest.x, dest.y - ySign); } + /** + * Initializes an en passant move. + * + * @param xPos the horizontal position of this move + * @param yPos the vertical position of this move + * @param xDest the horizontal destination of this move + * @param yDest the vertical destination of this move + */ public EnPassant(int xPos, int yPos, int xDest, int yDest) { this(new Position(xPos, yPos), new Position(xDest, yDest)); } @Override @@ -31,5 +45,8 @@ public class EnPassant extends Move { board.set(capturePos, new Pawn(board.get(pos).getColor().opposite(), board)); } + /** + * @return the position of the piece captures by this move + */ public Position getCapturePos() { return capturePos; } } diff --git a/src/dev/kske/chess/board/FENString.java b/src/dev/kske/chess/board/FENString.java index 28c890a..a1ffd39 100644 --- a/src/dev/kske/chess/board/FENString.java +++ b/src/dev/kske/chess/board/FENString.java @@ -44,7 +44,7 @@ public class FENString { * Constructs a {@link FENString} by parsing an existing string. * * @param fen the FEN string to parse - * @throws ChessException + * @throws ChessException if the FEN string contains invalid syntax */ public FENString(String fen) throws ChessException { // Check fen string against regex diff --git a/src/dev/kske/chess/board/King.java b/src/dev/kske/chess/board/King.java index 6807bb3..b3db224 100644 --- a/src/dev/kske/chess/board/King.java +++ b/src/dev/kske/chess/board/King.java @@ -7,12 +7,18 @@ import java.util.List; * Project: Chess
* File: King.java
* Created: 01.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ public class King extends Piece { + /** + * Creates king {@link Piece}. + * + * @param color the color of this king + * @param board the board on which this king will be placed + */ public King(Color color, Board board) { super(color, board); } @Override @@ -47,7 +53,8 @@ public class King extends Piece { Position kingDest = new Position(6, y); Position rookPos = new Position(7, y); return canCastle(kingPos, kingDest, rookPos, jumpPos); - } else return false; + } + return false; } private boolean canCastleQueenside() { @@ -58,7 +65,8 @@ public class King extends Piece { Position freeDest = new Position(1, y); Position rookPos = new Position(0, y); return canCastle(kingPos, freeDest, rookPos, jumpPos); - } else return false; + } + return false; } private boolean canCastle(Position kingPos, Position freeDest, Position rookPos, Position jumpPos) { diff --git a/src/dev/kske/chess/board/Knight.java b/src/dev/kske/chess/board/Knight.java index 9e54fd8..c1d5b51 100644 --- a/src/dev/kske/chess/board/Knight.java +++ b/src/dev/kske/chess/board/Knight.java @@ -7,12 +7,18 @@ import java.util.List; * Project: Chess
* File: Knight.java
* Created: 01.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ public class Knight extends Piece { + /** + * Creates knight {@link Piece}. + * + * @param color the color of this knight + * @param board the board on which this knight will be placed + */ public Knight(Color color, Board board) { super(color, board); } diff --git a/src/dev/kske/chess/board/Log.java b/src/dev/kske/chess/board/Log.java index ba4c509..a614a2e 100644 --- a/src/dev/kske/chess/board/Log.java +++ b/src/dev/kske/chess/board/Log.java @@ -5,8 +5,11 @@ import java.util.Iterator; import java.util.Objects; import dev.kske.chess.board.Piece.Color; +import dev.kske.chess.game.Game; /** + * Manages the move history of a {@link Game}.
+ *
* Project: Chess
* File: Log.java
* Created: 09.07.2019
@@ -23,6 +26,9 @@ public class Log implements Iterable { private Position enPassant; private int fullmoveNumber, halfmoveClock; + /** + * Creates an instance of {@link Log} in the default state. + */ public Log() { reset(); } /** @@ -230,23 +236,64 @@ public class Log implements Iterable { */ public MoveNode getLast() { return current; } + /** + * @return the castling rights present during the current move + */ public boolean[] getCastlingRights() { return castlingRights; } + /** + * Sets the castling rights present during the current move. + * + * @param castlingRights the castling rights to set + */ public void setCastlingRights(boolean[] castlingRights) { this.castlingRights = castlingRights; } + /** + * @return the en passant target position of the current move or {@code null} if + * the current move is not an en passant move. + */ public Position getEnPassant() { return enPassant; } + /** + * Sets the en passant target position. + * + * @param enPassant the en passant target position to set + */ public void setEnPassant(Position enPassant) { this.enPassant = enPassant; } + /** + * @return the color active during the current move + */ public Color getActiveColor() { return activeColor; } + /** + * Sets the color active during the current move. + * + * @param activeColor the active color to set + */ public void setActiveColor(Color activeColor) { this.activeColor = activeColor; } + /** + * @return the number of moves made until the current move + */ public int getFullmoveNumber() { return fullmoveNumber; } + /** + * Sets the number of moves made until the current move. + * + * @param fullmoveNumber the fullmove number to set + */ public void setFullmoveNumber(int fullmoveNumber) { this.fullmoveNumber = fullmoveNumber; } + /** + * @return the number of halfmoves since the last capture move or pawn move + */ public int getHalfmoveClock() { return halfmoveClock; } + /** + * Sets then number of halfmoves since the last capture move or pawn move + * + * @param halfmoveClock the halfmove clock to set + */ public void setHalfmoveClock(int halfmoveClock) { this.halfmoveClock = halfmoveClock; } } \ No newline at end of file diff --git a/src/dev/kske/chess/board/Move.java b/src/dev/kske/chess/board/Move.java index 267f53e..6ea0443 100644 --- a/src/dev/kske/chess/board/Move.java +++ b/src/dev/kske/chess/board/Move.java @@ -21,6 +21,12 @@ public class Move { protected final Position pos, dest; protected final int xDist, yDist, xSign, ySign; + /** + * Creates an instance of {@link Move}. + * + * @param pos the position of this move + * @param dest the destination of this move + */ public Move(Position pos, Position dest) { this.pos = pos; this.dest = dest; @@ -30,24 +36,53 @@ public class Move { ySign = (int) Math.signum(dest.y - pos.y); } + /** + * Creates an instance of {@link Move}. + * + * @param xPos the horizontal position of this move + * @param yPos the vertical position of this move + * @param xDest the horizontal destination of this move + * @param yDest the vertical destination of this move + */ public Move(int xPos, int yPos, int xDest, int yDest) { this(new Position(xPos, yPos), new Position(xDest, yDest)); } + /** + * Executed this move on a board. + * + * @param board the board to execute this move on. + */ public void execute(Board board) { // Move the piece to the move's destination square and clean the old position board.set(dest, board.get(pos)); board.set(pos, null); } + /** + * Reverts this move on a board. + * + * @param board the board to revert this move on + * @param capturedPiece the piece to place at the destination of this move (used + * for reinstating captured pieces) + */ public void revert(Board board, Piece capturedPiece) { // Move the piece to the move's position square and clean the destination board.set(pos, board.get(dest)); board.set(dest, capturedPiece); } - - public Move invert() { - return new Move(dest, pos); - } + /** + * @return a new move containing this move's destination as its position and + * this move's position as its destination + */ + public Move invert() { return new Move(dest, pos); } + + /** + * Constructs a move from a string representation in Long Algebraic Notation + * (LAN). + * + * @param move the LAN string to construct the move from + * @return the constructed move + */ public static Move fromLAN(String move) { Position pos = Position.fromLAN(move.substring(0, 2)); Position dest = Position.fromLAN(move.substring(2)); @@ -58,9 +93,16 @@ public class Move { e.printStackTrace(); return null; } - } else return new Move(pos, dest); + } + return new Move(pos, dest); } + /** + * Generates a string representation of this move in Long Algebraic Notation + * (LAN). + * + * @return the LAN string + */ public String toLAN() { return getPos().toLAN() + getDest().toLAN(); } /** @@ -151,13 +193,19 @@ public class Move { return null; } + /** + * Generates a string representation of this move in Standard Algebraic Notation + * (SAN). + * + * @param board the {@link Board} providing the context of this move + * @return the SAN string + */ public String toSAN(Board board) { final Piece piece = board.get(pos); StringBuilder sb = new StringBuilder(8); // Piece symbol - if(!(piece instanceof Pawn)) - sb.append(Character.toUpperCase(piece.firstChar())); + if (!(piece instanceof Pawn)) sb.append(Character.toUpperCase(piece.firstChar())); // Position // TODO: Deconstruct position into optional file or rank @@ -173,10 +221,19 @@ public class Move { return sb.toString(); } + /** + * @return {@code true} if the move is purely horizontal + */ public boolean isHorizontal() { return getyDist() == 0; } + /** + * @return {@code true} if the move is purely vertical + */ public boolean isVertical() { return getxDist() == 0; } + /** + * @return {@code true} if the move is diagonal + */ public boolean isDiagonal() { return getxDist() == getyDist(); } @Override @@ -195,15 +252,33 @@ public class Move { && getxSign() == other.getxSign() && getyDist() == other.getyDist() && getySign() == other.getySign(); } + /** + * @return the position + */ public Position getPos() { return pos; } + /** + * @return the destination + */ public Position getDest() { return dest; } + /** + * @return the x distance + */ public int getxDist() { return xDist; } + /** + * @return the y distance + */ public int getyDist() { return yDist; } + /** + * @return the sign of the x distance + */ public int getxSign() { return xSign; } + /** + * @return the sign of the y distance + */ public int getySign() { return ySign; } } diff --git a/src/dev/kske/chess/board/MoveNode.java b/src/dev/kske/chess/board/MoveNode.java index e618716..608f2ff 100644 --- a/src/dev/kske/chess/board/MoveNode.java +++ b/src/dev/kske/chess/board/MoveNode.java @@ -1,9 +1,6 @@ package dev.kske.chess.board; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; +import java.util.*; import dev.kske.chess.board.Piece.Color; @@ -17,14 +14,61 @@ import dev.kske.chess.board.Piece.Color; */ public class MoveNode { - public static final int WHITE_KINGSIDE = 0, WHITE_QUEENSIDE = 1, BLACK_KINGSIDE = 2, BLACK_QUEENSIDE = 3; + /** + * The index of the white kingside casting in a casting rights array. + */ + public static final int WHITE_KINGSIDE = 0; - public final Move move; - public final Piece capturedPiece; - public final boolean[] castlingRights; - public final Position enPassant; - public final Color activeColor; - public final int fullmoveCounter, halfmoveClock; + /** + * The index of the white queenside castling in a castling rights array. + */ + public static final int WHITE_QUEENSIDE = 1; + + /** + * The index of the white kingside casting in a casting rights array. + */ + public static final int BLACK_KINGSIDE = 2; + + /** + * The index of the white queenside castling in a castling rights array. + */ + public static final int BLACK_QUEENSIDE = 3; + + /** + * The move on the board associated with this move node. + */ + public final Move move; + + /** + * The piece captured by the move. + */ + public final Piece capturedPiece; + + /** + * The castling rights present during the move. + */ + public final boolean[] castlingRights; + + /** + * The en passant target position or {@code null} if the move is not an en + * passant move. + */ + public final Position enPassant; + + /** + * The color active during the move. + */ + public final Color activeColor; + + /** + * The number of moves performed since the beginning of the game. + */ + public final int fullmoveCounter; + + /** + * The halfmoves performed since the last capture move or pawn move. + */ + public final int halfmoveClock; private MoveNode parent; private List variations; @@ -32,16 +76,18 @@ public class MoveNode { /** * Creates a new {@link MoveNode}. * - * @param move The logged {@link Move} - * @param capturedPiece The {@link Piece} captures by the logged {@link Move} - * @param enPassant The en passant {@link Position} valid after the logged + * @param move the logged {@link Move} + * @param capturedPiece the {@link Piece} captures by the logged {@link Move} + * @param castlingRights the castling rights present during the move + * @param enPassant the en passant {@link Position} valid after the logged * {@link Move}, or {@code null} if there is none - * @param activeColor The {@link Color} active after the logged {@link Move} - * @param fullmoveCounter - * @param halfmoveClock + * @param activeColor the {@link Color} active after the logged {@link Move} + * @param fullmoveCounter the number of moves made until the current move + * @param halfmoveClock the number of halfmoves since the last capture move or + * pawn move */ - public MoveNode(Move move, Piece capturedPiece, boolean castlingRights[], Position enPassant, Color activeColor, - int fullmoveCounter, int halfmoveClock) { + public MoveNode(Move move, Piece capturedPiece, boolean castlingRights[], Position enPassant, Color activeColor, int fullmoveCounter, + int halfmoveClock) { this.move = move; this.capturedPiece = capturedPiece; this.castlingRights = castlingRights; @@ -60,8 +106,8 @@ public class MoveNode { * considers subsequent variations */ public MoveNode(MoveNode other, boolean copyVariations) { - this(other.move, other.capturedPiece, other.castlingRights.clone(), other.enPassant, other.activeColor, - other.fullmoveCounter, other.halfmoveClock); + this(other.move, other.capturedPiece, other.castlingRights.clone(), other.enPassant, other.activeColor, other.fullmoveCounter, + other.halfmoveClock); if (copyVariations && other.variations != null) { if (variations == null) variations = new ArrayList<>(); for (MoveNode variation : other.variations) { @@ -90,25 +136,34 @@ public class MoveNode { */ public List getVariations() { return variations; } - public boolean hasVariations() { - return variations != null && variations.size() > 0; - } + /** + * @return {@code true} if this move node has any variations + */ + public boolean hasVariations() { return variations != null && variations.size() > 0; } + /** + * @return the parent node of this move node + */ public MoveNode getParent() { return parent; } + /** + * Sets the parent node of this move node + * + * @param parent the parent node to set + */ public void setParent(MoveNode parent) { this.parent = parent; } - public boolean hasParent() { - return parent != null; - } + /** + * @return {@code true} if this move node has a parent + */ + public boolean hasParent() { return parent != null; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Arrays.hashCode(castlingRights); - result = prime * result - + Objects.hash(activeColor, capturedPiece, enPassant, fullmoveCounter, halfmoveClock, move); + result = prime * result + Objects.hash(activeColor, capturedPiece, enPassant, fullmoveCounter, halfmoveClock, move); return result; } @@ -120,7 +175,6 @@ public class MoveNode { MoveNode other = (MoveNode) obj; return activeColor == other.activeColor && Objects.equals(capturedPiece, other.capturedPiece) && Arrays.equals(castlingRights, other.castlingRights) && Objects.equals(enPassant, other.enPassant) - && fullmoveCounter == other.fullmoveCounter && halfmoveClock == other.halfmoveClock - && Objects.equals(move, other.move); + && fullmoveCounter == other.fullmoveCounter && halfmoveClock == other.halfmoveClock && Objects.equals(move, other.move); } } \ No newline at end of file diff --git a/src/dev/kske/chess/board/Pawn.java b/src/dev/kske/chess/board/Pawn.java index 7b44b0a..9961787 100644 --- a/src/dev/kske/chess/board/Pawn.java +++ b/src/dev/kske/chess/board/Pawn.java @@ -13,6 +13,12 @@ import java.util.List; */ public class Pawn extends Piece { + /** + * Creates pawn {@link Piece}. + * + * @param color the color of this pawn + * @param board the board on which this pawn will be placed + */ public Pawn(Color color, Board board) { super(color, board); } @Override diff --git a/src/dev/kske/chess/board/PawnPromotion.java b/src/dev/kske/chess/board/PawnPromotion.java index 2c78750..097daf7 100644 --- a/src/dev/kske/chess/board/PawnPromotion.java +++ b/src/dev/kske/chess/board/PawnPromotion.java @@ -19,6 +19,19 @@ public class PawnPromotion extends Move { private final Constructor promotionPieceConstructor; private final char promotionPieceChar; + /** + * Initializes a pawn promotion move. + * + * @param pos the position of this move + * @param dest the destination of this move + * @param promotionPieceClass the class of the piece to which the pawn is + * promoted + * + * @throws ReflectiveOperationException if the promotion piece could not be + * instantiated + * @throws RuntimeException if the promotion piece could not be + * instantiated + */ public PawnPromotion(Position pos, Position dest, Class promotionPieceClass) throws ReflectiveOperationException, RuntimeException { super(pos, dest); @@ -31,9 +44,24 @@ public class PawnPromotion extends Move { promotionPieceChar = (char) promotionPieceClass.getMethod("firstChar").invoke(promotionPieceConstructor.newInstance(null, null)); } - public PawnPromotion(int xPos, int yPos, int xDest, int yDest, Class promotionPiece) + /** + * + * Creates an instance of {@link PawnPromotion}. + * + * @param xPos the horizontal position of this move + * @param yPos the vertical position of this move + * @param xDest the horizontal destination of this move + * @param yDest the vertical destination of this move + * @param promotionPieceClass the class of the piece to which the pawn is + * promoted + * @throws ReflectiveOperationException if the promotion piece could not be + * instantiated + * @throws RuntimeException if the promotion piece could not be + * instantiated + */ + public PawnPromotion(int xPos, int yPos, int xDest, int yDest, Class promotionPieceClass) throws ReflectiveOperationException, RuntimeException { - this(new Position(xPos, yPos), new Position(xDest, yDest), promotionPiece); + this(new Position(xPos, yPos), new Position(xDest, yDest), promotionPieceClass); } @Override diff --git a/src/dev/kske/chess/board/Piece.java b/src/dev/kske/chess/board/Piece.java index 848dca5..a0ea314 100644 --- a/src/dev/kske/chess/board/Piece.java +++ b/src/dev/kske/chess/board/Piece.java @@ -5,10 +5,12 @@ import java.util.List; import java.util.Objects; /** + * Represents a piece on a board with a color.
+ *
* Project: Chess
* File: Piece.java
* Created: 01.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ @@ -17,11 +19,23 @@ public abstract class Piece implements Cloneable { private final Color color; protected Board board; + /** + * Initializes a piece. + * + * @param color the color of this piece + * @param board the board on which this piece is placed + */ public Piece(Color color, Board board) { this.color = color; this.board = board; } + /** + * Generated a list of legal moves this piece can make. + * + * @param pos the position of this piece + * @return a list of legal moves this piece can make + */ public List getMoves(Position pos) { List moves = getPseudolegalMoves(pos); for (Iterator iterator = moves.iterator(); iterator.hasNext();) { @@ -33,15 +47,28 @@ public abstract class Piece implements Cloneable { return moves; } + /** + * Generates a list of pseudo legal moves this piece can make. + * + * @param pos the position of this piece + * @return a list of pseudo legal moves this piece can make + */ protected abstract List getPseudolegalMoves(Position pos); + /** + * Checks, if a given move is valid. + * + * @param move the move to check + * @return {@code true} if the move is valid + */ public abstract boolean isValidMove(Move move); /** * Checks, if the squares between the position and the destination of a move are * free. - * + * * @param move The move to check + * @return {@true} if the path is free */ protected boolean isFreePath(Move move) { for (int i = move.getPos().x + move.getxSign(), j = move.getPos().y + move.getySign(); i != move.getDest().x @@ -53,7 +80,7 @@ public abstract class Piece implements Cloneable { /** * Checks if the destination of a move is empty or a piece from the opposing * team - * + * * @param move The move to check * @return {@code false} if the move's destination is from the same team */ @@ -97,6 +124,11 @@ public abstract class Piece implements Cloneable { */ public char firstChar() { return Character.toLowerCase(toString().charAt(0)); } + /** + * @param firstChar the first character of a piece's name + * @return the class of the piece associated with that character or {@code null} + * if no piece is associated with the given character + */ public static Class fromFirstChar(char firstChar) { switch (Character.toLowerCase(firstChar)) { case 'k': @@ -121,14 +153,41 @@ public abstract class Piece implements Cloneable { */ public Color getColor() { return color; } - public static enum Color { + /** + * Project: Chess
+ * File: Piece.java
+ * Created: 01.07.2019
+ * + * @author Kai S. K. Engelbart + * @since Chess v0.1-alpha + */ + public enum Color { - WHITE, BLACK; + /** + * Represents the color of the white pieces on a board. + */ + WHITE, + /** + * Represents the color of the black pieces on a board. + */ + BLACK; + + /** + * @param c the first character of a color's name + * @return {@code WHITE} if the character is {@code w} or {@code W}, else + * {@code BLACK} + */ public static Color fromFirstChar(char c) { return Character.toLowerCase(c) == 'w' ? WHITE : BLACK; } + /** + * @return the first character (lower case) of this color + */ public char firstChar() { return this == WHITE ? 'w' : 'b'; } + /** + * @return the opposite of this color + */ public Color opposite() { return this == WHITE ? BLACK : WHITE; } } } diff --git a/src/dev/kske/chess/board/Position.java b/src/dev/kske/chess/board/Position.java index 15a185a..ed9bc08 100644 --- a/src/dev/kske/chess/board/Position.java +++ b/src/dev/kske/chess/board/Position.java @@ -4,31 +4,50 @@ package dev.kske.chess.board; * Project: Chess
* File: Position.java
* Created: 02.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ public class Position { - public final int x, y; + /** + * The horizontal component of this position. + */ + public final int x; + /** + * The vertical component of this position. + */ + public final int y; + + /** + * Initializes a position. + * + * @param x the horizontal component of this position + * @param y the vertical component of this position + */ public Position(int x, int y) { this.x = x; this.y = y; } - public static Position fromLAN(String pos) { - return new Position(pos.charAt(0) - 97, 8 - Character.getNumericValue(pos.charAt(1))); - } + /** + * Constructs a position from Long Algebraic Notation (LAN) + * + * @param pos the LAN string to construct a position from + * @return the position constructed from LAN + */ + public static Position fromLAN(String pos) { return new Position(pos.charAt(0) - 97, 8 - Character.getNumericValue(pos.charAt(1))); } - public String toLAN() { - return String.valueOf((char) (x + 97)) + String.valueOf(8 - y); - } + /** + * Converts this position to Long Algebraic Notation (LAN) + * + * @return a LAN string representing this position + */ + public String toLAN() { return String.valueOf((char) (x + 97)) + String.valueOf(8 - y); } @Override - public String toString() { - return String.format("[%d, %d]", x, y); - } + public String toString() { return String.format("[%d, %d]", x, y); } @Override public int hashCode() { diff --git a/src/dev/kske/chess/board/Queen.java b/src/dev/kske/chess/board/Queen.java index b2482ce..f0fdc6f 100644 --- a/src/dev/kske/chess/board/Queen.java +++ b/src/dev/kske/chess/board/Queen.java @@ -7,12 +7,18 @@ import java.util.List; * Project: Chess
* File: Queen.java
* Created: 01.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ public class Queen extends Piece { + /** + * Creates queen {@link Piece}. + * + * @param color the color of this queen + * @param board the board on which this queen will be placed + */ public Queen(Color color, Board board) { super(color, board); } diff --git a/src/dev/kske/chess/board/Rook.java b/src/dev/kske/chess/board/Rook.java index b2efd5c..45b5c09 100644 --- a/src/dev/kske/chess/board/Rook.java +++ b/src/dev/kske/chess/board/Rook.java @@ -7,12 +7,18 @@ import java.util.List; * Project: Chess
* File: Rook.java
* Created: 01.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ public class Rook extends Piece { + /** + * Creates rook {@link Piece}. + * + * @param color the color of this rook + * @param board the board on which this rook will be placed + */ public Rook(Color color, Board board) { super(color, board); } diff --git a/src/dev/kske/chess/event/Event.java b/src/dev/kske/chess/event/Event.java index 5e31830..2cc982f 100644 --- a/src/dev/kske/chess/event/Event.java +++ b/src/dev/kske/chess/event/Event.java @@ -4,9 +4,10 @@ package dev.kske.chess.event; * Project: Chess
* File: Event.java
* Created: 7 Aug 2019
- * + * * @since Chess v0.4-alpha * @author Kai S. K. Engelbart + * @param the type of the event's value */ public interface Event { diff --git a/src/dev/kske/chess/event/EventBus.java b/src/dev/kske/chess/event/EventBus.java index b8cb2df..5c25bf7 100644 --- a/src/dev/kske/chess/event/EventBus.java +++ b/src/dev/kske/chess/event/EventBus.java @@ -4,6 +4,8 @@ import java.util.ArrayList; import java.util.List; /** + * Dispatches {@link Event}s to various {@link Subscriber}s.
+ *
* Project: Chess
* File: EventBus.java
* Created: 7 Aug 2019
@@ -13,26 +15,38 @@ import java.util.List; */ public class EventBus { - private List subscribers; + private List subscribers; private static EventBus instance; + /** + * @return a singleton instance of {@link EventBus} + */ public static EventBus getInstance() { if (instance == null) instance = new EventBus(); return instance; } - private EventBus() { - subscribers = new ArrayList<>(); - } + private EventBus() { subscribers = new ArrayList<>(); } - public void register(Subscribable subscribable) { - subscribers.add(subscribable); - } + /** + * Registers a subscriber to which future events will be dispatched. + * + * @param subscribable the subscriber to register + */ + public void register(Subscriber subscribable) { subscribers.add(subscribable); } + /** + * Dispatches an event to all {@Subscriber}s registered at this event bus. + * + * @param event the event to dispatch + */ public void dispatch(Event event) { subscribers.stream().filter(e -> e.supports().contains(event.getClass())).forEach(e -> e.handle(event)); } - public List getSubscribers() { return subscribers; } + /** + * @return a list of all registered subscribers + */ + public List getSubscribers() { return subscribers; } } diff --git a/src/dev/kske/chess/event/GameStartEvent.java b/src/dev/kske/chess/event/GameStartEvent.java index 4097550..29ffb65 100644 --- a/src/dev/kske/chess/event/GameStartEvent.java +++ b/src/dev/kske/chess/event/GameStartEvent.java @@ -14,6 +14,11 @@ public class GameStartEvent implements Event { private final Game game; + /** + * Creates an instance of {@link GameStartEvent}. + * + * @param source the game started + */ public GameStartEvent(Game source) { game = source; } @Override diff --git a/src/dev/kske/chess/event/MoveEvent.java b/src/dev/kske/chess/event/MoveEvent.java index a60a785..9892bf4 100644 --- a/src/dev/kske/chess/event/MoveEvent.java +++ b/src/dev/kske/chess/event/MoveEvent.java @@ -16,6 +16,12 @@ public class MoveEvent implements Event { private final Move move; private final BoardState boardState; + /** + * Creates an instance of {@link MoveEvent}. + * + * @param move the move by which the event was triggered + * @param boardState the state of the board after the move + */ public MoveEvent(Move move, BoardState boardState) { this.move = move; this.boardState = boardState; @@ -24,5 +30,8 @@ public class MoveEvent implements Event { @Override public Move getData() { return move; } + /** + * @return the state of the board after the move + */ public BoardState getBoardState() { return boardState; } } diff --git a/src/dev/kske/chess/event/Subscribable.java b/src/dev/kske/chess/event/Subscriber.java similarity index 69% rename from src/dev/kske/chess/event/Subscribable.java rename to src/dev/kske/chess/event/Subscriber.java index 4de8380..12b4cbc 100644 --- a/src/dev/kske/chess/event/Subscribable.java +++ b/src/dev/kske/chess/event/Subscriber.java @@ -3,6 +3,10 @@ package dev.kske.chess.event; import java.util.Set; /** + * Implementations of this interface can register themselves at the + * {@link EventBus} and will be triggered every time an {@link Event} of a + * supported type.
+ *
* Project: Chess
* File: Subscribable.java
* Created: 7 Aug 2019
@@ -10,7 +14,7 @@ import java.util.Set; * @since Chess v0.4-alpha * @author Kai S. K. Engelbart */ -public interface Subscribable { +public interface Subscriber { /** * Consumes an event dispatched by an event bus. diff --git a/src/dev/kske/chess/exception/ChessException.java b/src/dev/kske/chess/exception/ChessException.java index 3bedea7..e2c8bfc 100644 --- a/src/dev/kske/chess/exception/ChessException.java +++ b/src/dev/kske/chess/exception/ChessException.java @@ -4,7 +4,7 @@ package dev.kske.chess.exception; * Project: Chess
* File: ChessException.java
* Created: 22 Sep 2019
- * + * * @since Chess v0.5-alpha * @author Kai S. K. Engelbart */ @@ -12,14 +12,30 @@ public class ChessException extends Exception { private static final long serialVersionUID = -2208596063548245189L; + /** + * Initializes chess exception. + * + * @param message the message associated with this exception + * @param cause the cause of this exception + */ public ChessException(String message, Throwable cause) { super(message, cause); } + /** + * Initializes chess exception. + * + * @param message the message associated with this exception + */ public ChessException(String message) { super(message); } + /** + * Initializes chess exception. + * + * @param cause the cause of this exception + */ public ChessException(Throwable cause) { super(cause); } diff --git a/src/dev/kske/chess/game/Game.java b/src/dev/kske/chess/game/Game.java index b3f5171..8905c1c 100644 --- a/src/dev/kske/chess/game/Game.java +++ b/src/dev/kske/chess/game/Game.java @@ -23,7 +23,7 @@ import dev.kske.chess.ui.OverlayComponent; * Project: Chess
* File: Game.java
* Created: 06.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ @@ -34,11 +34,26 @@ public class Game { private OverlayComponent overlayComponent; private BoardComponent boardComponent; + /** + * Initializes game with a new {@link Board}. + * + * @param boardPane the board pane which will display the newly created board + * @param whiteName the name of the player controlling the white pieces + * @param blackName the name of the player controlling the black pieces + */ public Game(BoardPane boardPane, String whiteName, String blackName) { board = new Board(); init(boardPane, whiteName, blackName); } + /** + * Initializes game with an existing {@link Board}. + * + * @param boardPane the board pane which will display the newly created board + * @param whiteName the name of the player controlling the white pieces + * @param blackName the name of the player controlling the black pieces + * @param board the board on which the game will be played + */ public Game(BoardPane boardPane, String whiteName, String blackName, Board board) { this.board = board; init(boardPane, whiteName, blackName); @@ -59,6 +74,17 @@ public class Game { players.values().forEach(player -> player.setGame(this)); } + /** + * Initializes player subclass. + * + * @param name the name of the player. {@code Natural Player} will initialize a + * {@link NaturalPlayer}, {@code AI Player} will initialize an + * {@link AIPlayer}. Everything else will attempt to load an engine + * with that name + * @param color the color of the player + * @return the instantiated player or {@code null} if the name could not be + * recognized + */ private Player getPlayer(String name, Color color) { switch (name) { case "Natural Player": @@ -73,6 +99,14 @@ public class Game { } } + /** + * Should be called once a player makes a move. Depending on the legality of + * that move and the state of the game another move might be requested from one + * of the players. + * + * @param player the player who generated the move + * @param move the generated move + */ public void onMove(Player player, Move move) { if (board.getPos(move).getColor() == player.color && board.attemptMove(move)) { @@ -83,7 +117,7 @@ public class Game { // Run garbage collection System.gc(); - BoardState boardState = board.getGameEventType(board.getDest(move).getColor().opposite()); + BoardState boardState = board.getState(board.getDest(move).getColor().opposite()); EventBus.getInstance().dispatch(new MoveEvent(move, boardState)); switch (boardState) { case CHECKMATE: @@ -100,11 +134,19 @@ public class Game { } else player.requestMove(); } + /** + * Starts the game by requesting a move from the player of the currently active + * color. + */ public void start() { EventBus.getInstance().dispatch(new GameStartEvent(this)); players.get(board.getLog().getActiveColor()).requestMove(); } + /** + * Cancels move calculations, initializes the default position and clears the + * {@link OverlayComponent}. + */ public void reset() { players.values().forEach(Player::cancelMove); board.initDefaultPositions(); @@ -114,11 +156,9 @@ public class Game { } /** - * Stops the game by disconnecting its players form the UI. + * Stops the game by disconnecting its players from the UI. */ - public void stop() { - players.values().forEach(Player::disconnect); - } + public void stop() { players.values().forEach(Player::disconnect); } /** * Assigns the players their opposite colors. diff --git a/src/dev/kske/chess/game/NaturalPlayer.java b/src/dev/kske/chess/game/NaturalPlayer.java index 864c4e5..563b1a0 100644 --- a/src/dev/kske/chess/game/NaturalPlayer.java +++ b/src/dev/kske/chess/game/NaturalPlayer.java @@ -15,10 +15,14 @@ import dev.kske.chess.board.Position; import dev.kske.chess.ui.OverlayComponent; /** + * Enables the user to make moves in a {@link Game} by clicking on a + * {@link Piece} and then selecting one of the highlighted positions as the move + * destination.
+ *
* Project: Chess
* File: NaturalPlayer.java
* Created: 06.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ @@ -30,6 +34,13 @@ public class NaturalPlayer extends Player implements MouseListener { private Piece selectedPiece; private List possibleMoves; + /** + * Creates an instance of {@link NaturalPlayer}. + * + * @param color the piece color this player will control + * @param overlayComponent the overlay component that will be used to display + * possible moves to the user + */ public NaturalPlayer(Color color, OverlayComponent overlayComponent) { super(color); this.overlayComponent = overlayComponent; @@ -82,7 +93,7 @@ public class NaturalPlayer extends Player implements MouseListener { if (selectedMoves.size() > 1) { // Let the user select a promotion piece - JComboBox comboBox = new JComboBox(selectedMoves.toArray(new Move[0])); + JComboBox comboBox = new JComboBox<>(selectedMoves.toArray(new Move[0])); JOptionPane.showMessageDialog(overlayComponent, comboBox, "Select a promotion", JOptionPane.QUESTION_MESSAGE); move = selectedMoves.get(comboBox.getSelectedIndex()); diff --git a/src/dev/kske/chess/game/Player.java b/src/dev/kske/chess/game/Player.java index 0b88f9d..4c323b2 100644 --- a/src/dev/kske/chess/game/Player.java +++ b/src/dev/kske/chess/game/Player.java @@ -1,13 +1,17 @@ package dev.kske.chess.game; import dev.kske.chess.board.Board; +import dev.kske.chess.board.Move; import dev.kske.chess.board.Piece.Color; /** + * Acts as the interface between the {@link Game} class and some kind of move + * generation backend implemented as a subclass.
+ *
* Project: Chess
* File: Player.java
* Created: 06.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ @@ -18,32 +22,71 @@ public abstract class Player { protected Color color; protected String name; - public Player(Color color) { - this.color = color; - } + /** + * Initializes the color of this player. + * + * @param color the piece color that this player will control + */ + public Player(Color color) { this.color = color; } + /** + * Initiates a move generation and reports the result to the game by calling + * {@link Game#onMove(Player, Move)}. + */ public abstract void requestMove(); + /** + * Cancels the move generation process. + */ public abstract void cancelMove(); + /** + * Closes all resources required for move generation. + */ public abstract void disconnect(); + /** + * @return the game in which this player is used + */ public Game getGame() { return game; } + /** + * Sets the game in which this player is used. The board of that game will be + * assigned as well. + * + * @param game the game to set + */ public void setGame(Game game) { this.game = game; board = game.getBoard(); } + /** + * @return the board on which this player is used + */ public Board getBoard() { return board; } - public void setBoard(Board board) { this.board = board; } - + /** + * @return the color of pieces controlled by this player + */ public Color getColor() { return color; } + /** + * Sets the color of pieces controlled by this player. + * + * @param color the color to set + */ public void setColor(Color color) { this.color = color; } + /** + * @return the name of this player + */ public String getName() { return name; } + /** + * Sets the name of this player + * + * @param name the name to set + */ public void setName(String name) { this.name = name; } } diff --git a/src/dev/kske/chess/game/UCIPlayer.java b/src/dev/kske/chess/game/UCIPlayer.java index 2aa320d..0a06645 100644 --- a/src/dev/kske/chess/game/UCIPlayer.java +++ b/src/dev/kske/chess/game/UCIPlayer.java @@ -9,22 +9,32 @@ import dev.kske.chess.uci.UCIHandle; import dev.kske.chess.uci.UCIListener; /** + * Acts as the interface between the {@link Game} class and the + * {@link dev.kske.chess.uci} package enabling an engine to make moves in a + * game.
+ *
* Project: Chess
* File: UCIPlayer.java
* Created: 18.07.2019
- * + * * @since Chess v0.3-alpha * @author Kai S. K. Engelbart */ public class UCIPlayer extends Player implements UCIListener { - private UCIHandle handle; + private UCIHandle handle; + /** + * Creates an instance of {@link UCIPlayer}. + * + * @param color the piece color that this player will control + * @param enginePath the path to the engine executable + */ public UCIPlayer(Color color, String enginePath) { super(color); try { handle = new UCIHandle(enginePath); - handle.setListener(this); + handle.registerListener(this); handle.start(); } catch (IOException ex) { ex.printStackTrace(); @@ -38,19 +48,13 @@ public class UCIPlayer extends Player implements UCIListener { } @Override - public void cancelMove() { - handle.stop(); - } + public void cancelMove() { handle.stop(); } @Override - public void disconnect() { - handle.quit(); - } + public void disconnect() { handle.quit(); } @Override - public void onIdName(String name) { - this.name = name; - } + public void onIdName(String name) { this.name = name; } @Override public void onBestMove(String move) { @@ -59,37 +63,23 @@ public class UCIPlayer extends Player implements UCIListener { } @Override - public void onBestMove(String move, Move ponderMove) { - onBestMove(move); - } + public void onBestMove(String move, Move ponderMove) { onBestMove(move); } @Override - public void onCopyProtectionChecking() { - System.out.println("Copy protection checking..."); - } + public void onCopyProtectionChecking() { System.out.println("Copy protection checking..."); } @Override - public void onCopyProtectionOk() { - System.out.println("Copy protection ok"); - } + public void onCopyProtectionOk() { System.out.println("Copy protection ok"); } @Override - public void onCopyProtectionError() { - System.err.println("Copy protection error!"); - } + public void onCopyProtectionError() { System.err.println("Copy protection error!"); } @Override - public void onRegistrationChecking() { - System.out.println("Registration checking..."); - } + public void onRegistrationChecking() { System.out.println("Registration checking..."); } @Override - public void onRegistrationOk() { - System.out.println("Registration ok"); - } + public void onRegistrationOk() { System.out.println("Registration ok"); } @Override - public void onRegistrationError() { - System.err.println("Registration error!"); - } + public void onRegistrationError() { System.err.println("Registration error!"); } } diff --git a/src/dev/kske/chess/game/ai/AIPlayer.java b/src/dev/kske/chess/game/ai/AIPlayer.java index 9e53326..ee68765 100644 --- a/src/dev/kske/chess/game/ai/AIPlayer.java +++ b/src/dev/kske/chess/game/ai/AIPlayer.java @@ -2,11 +2,7 @@ package dev.kske.chess.game.ai; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import javax.swing.SwingUtilities; @@ -19,7 +15,7 @@ import dev.kske.chess.game.Player; * Project: Chess
* File: AIPlayer.java
* Created: 06.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ @@ -32,6 +28,15 @@ public class AIPlayer extends Player { private volatile boolean exitRequested; private volatile ExecutorService executor; + /** + * Creates an instance of {@link AIPlayer}. + * + * @param color the piece color this player will control + * @param maxDepth the maximum search depth + * @param alphaBetaThreshold the board evaluation threshold that has to be + * reached to continue searching the children of a + * move + */ public AIPlayer(Color color, int maxDepth, int alphaBetaThreshold) { super(color); name = "AIPlayer"; diff --git a/src/dev/kske/chess/game/ai/MoveProcessor.java b/src/dev/kske/chess/game/ai/MoveProcessor.java index 112bc30..5139113 100644 --- a/src/dev/kske/chess/game/ai/MoveProcessor.java +++ b/src/dev/kske/chess/game/ai/MoveProcessor.java @@ -5,22 +5,16 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Callable; -import dev.kske.chess.board.Bishop; -import dev.kske.chess.board.Board; -import dev.kske.chess.board.King; -import dev.kske.chess.board.Knight; -import dev.kske.chess.board.Move; -import dev.kske.chess.board.Pawn; -import dev.kske.chess.board.Piece; +import dev.kske.chess.board.*; import dev.kske.chess.board.Piece.Color; -import dev.kske.chess.board.Queen; -import dev.kske.chess.board.Rook; /** + * Implements a basic minimax move search algorithm for testing purposes.
+ *
* Project: Chess
* File: MoveProcessor.java
* Created: 08.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ @@ -68,6 +62,16 @@ public class MoveProcessor implements Callable { new int[] { 0, 1, 1, -2, -2, 1, 1, 0 }, new int[] { 0, 0, 0, 0, 0, 0, 0, 0 } }); } + /** + * Creates an instance of {@link MoveProcessor}. + * + * @param board the board to search + * @param rootMoves the moves on which the search is based + * @param color the color for which to search + * @param maxDepth the maximal recursion depth to search to + * @param alphaBetaThreshold the threshold necessary to continue a search for a + * specific move + */ public MoveProcessor(Board board, List rootMoves, Color color, int maxDepth, int alphaBetaThreshold) { this.board = board; this.rootMoves = rootMoves; @@ -105,9 +109,10 @@ public class MoveProcessor implements Callable { /** * Evaluated a board. - * + * + * @param board the board to evaluate * @param color The color to evaluate for - * @return An positive number representing how good the position is + * @return a positive number representing how good the position is */ private int evaluate(Board board, Color color) { int score = 0; diff --git a/src/dev/kske/chess/game/ai/ProcessingResult.java b/src/dev/kske/chess/game/ai/ProcessingResult.java index 951ac71..dca22b8 100644 --- a/src/dev/kske/chess/game/ai/ProcessingResult.java +++ b/src/dev/kske/chess/game/ai/ProcessingResult.java @@ -3,25 +3,38 @@ package dev.kske.chess.game.ai; import dev.kske.chess.board.Move; /** + * Contains information about a move search performed by a chess engine. + *
* Project: Chess
* File: ProcessingResult.java
* Created: 08.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ public class ProcessingResult { - public final Move move; - public final int score; + /** + * The best move found by the search + */ + public final Move move; + /** + * The score associated with the best move + */ + public final int score; + + /** + * Creates an instance of {@link ProcessingResult}. + * + * @param move the best move found by the search + * @param score the score associated with the best move + */ public ProcessingResult(Move move, int score) { this.move = move; this.score = score; } @Override - public String toString() { - return String.format("ProcessingResult[Move = %s, Score = %d]", move, score); - } + public String toString() { return String.format("ProcessingResult[Move = %s,Score = %d]", move, score); } } diff --git a/src/dev/kske/chess/io/EngineUtil.java b/src/dev/kske/chess/io/EngineUtil.java index 4c82842..56b9cc8 100644 --- a/src/dev/kske/chess/io/EngineUtil.java +++ b/src/dev/kske/chess/io/EngineUtil.java @@ -1,11 +1,6 @@ package dev.kske.chess.io; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; +import java.io.*; import java.util.ArrayList; import java.util.List; @@ -14,9 +9,9 @@ import dev.kske.chess.uci.UCIListener; /** * Project: Chess
- * File: MenuBar.java
+ * File: EngineUtil.java
* Created: 23.07.2019
- * + * * @since Chess v0.2-alpha * @author Kai S. K. Engelbart * @author Leon Hofmeister @@ -33,21 +28,22 @@ public class EngineUtil { private EngineUtil() {} + /** + * Stores information about an engine while checking its availability. + * + * @param enginePath the path to the executable of the engine + */ public static void addEngine(String enginePath) { try { EngineInfo info = new EngineInfo(enginePath); UCIHandle handle = new UCIHandle(enginePath); - handle.setListener(new UCIListener() { + handle.registerListener(new UCIListener() { @Override - public void onIdName(String name) { - info.name = name; - } + public void onIdName(String name) { info.name = name; } @Override - public void onIdAuthor(String author) { - info.author = author; - } + public void onIdAuthor(String author) { info.author = author; } @Override public void onUCIOk() { @@ -81,21 +77,49 @@ public class EngineUtil { } } + /** + * Stores the name and author of an engine, as well as a path to its + * executable.
+ *
+ * Project: Chess
+ * File: EngineUtil.java
+ * Created: 23.07.2019
+ * + * @since Chess v0.2-alpha + * @author Kai S. K. Engelbart + */ public static class EngineInfo implements Serializable { private static final long serialVersionUID = -474177108900833005L; - public String path, name, author; + /** + * The path to the executable of the engine + */ + public String path; - public EngineInfo(String path) { - this.path = path; - } + /** + * The name of the engine + */ + public String name; + + /** + * The author of the engine + */ + public String author; + + /** + * Creates an instance of {@link EngineInfo}. + * + * @param path the path of the engine executable + */ + public EngineInfo(String path) { this.path = path; } @Override - public String toString() { - return name + " by " + author + " at " + path; - } + public String toString() { return name + " by " + author + " at " + path; } } + /** + * @return a list of all stored engine infos + */ public static List getEngineInfos() { return engineInfos; } } diff --git a/src/dev/kske/chess/pgn/PGNDatabase.java b/src/dev/kske/chess/pgn/PGNDatabase.java index 127f9a6..0dea921 100644 --- a/src/dev/kske/chess/pgn/PGNDatabase.java +++ b/src/dev/kske/chess/pgn/PGNDatabase.java @@ -1,9 +1,6 @@ package dev.kske.chess.pgn; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.PrintWriter; +import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.Scanner; @@ -17,7 +14,7 @@ import dev.kske.chess.exception.ChessException; *
* Contains a series of {@link PGNGame} objects that can be stored inside a PGN * file. - * + * * @since Chess v0.5-alpha * @author Kai S. K. Engelbart */ @@ -27,30 +24,33 @@ public class PGNDatabase { /** * Loads PGN games from a file. - * + * * @param pgnFile the file to load the games from * @throws FileNotFoundException if the specified file is not found * @throws ChessException if an error occurs while parsing the file */ public void load(File pgnFile) throws FileNotFoundException, ChessException { - Scanner sc = new Scanner(pgnFile); - while (sc.hasNext()) - games.add(PGNGame.parse(sc)); - sc.close(); + try (Scanner sc = new Scanner(pgnFile)) { + while (sc.hasNext()) + games.add(PGNGame.parse(sc)); + } } /** * Saves PGN games to a file. - * + * * @param pgnFile the file to save the games to. * @throws IOException if the file could not be created */ public void save(File pgnFile) throws IOException { pgnFile.getParentFile().mkdirs(); - PrintWriter pw = new PrintWriter(pgnFile); - games.forEach(g -> g.writePGN(pw)); - pw.close(); + try (PrintWriter pw = new PrintWriter(pgnFile)) { + games.forEach(g -> g.writePGN(pw)); + } } + /** + * @return all games contained inside this database + */ public List getGames() { return games; } } diff --git a/src/dev/kske/chess/pgn/PGNGame.java b/src/dev/kske/chess/pgn/PGNGame.java index 609a8f9..420be63 100644 --- a/src/dev/kske/chess/pgn/PGNGame.java +++ b/src/dev/kske/chess/pgn/PGNGame.java @@ -1,12 +1,7 @@ package dev.kske.chess.pgn; import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Scanner; +import java.util.*; import java.util.regex.MatchResult; import java.util.regex.Pattern; @@ -14,7 +9,6 @@ import dev.kske.chess.board.Board; import dev.kske.chess.board.FENString; import dev.kske.chess.board.Move; import dev.kske.chess.board.Piece.Color; -import dev.kske.chess.exception.ChessException; /** * Project: Chess
@@ -29,11 +23,27 @@ public class PGNGame { private final Map tagPairs = new HashMap<>(7); private final Board board; + /** + * Creates an instance of {@link PGNGame}. A new default {@link Board} will be + * created. + */ public PGNGame() { board = new Board(); } + /** + * Creates an instance of {@link PGNGame}. + * + * @param board the board associated with the game + */ public PGNGame(Board board) { this.board = board; } - public static PGNGame parse(Scanner sc) throws ChessException { + /** + * Parses a game in {@code PGN} format from a {@link Scanner} instance + * + * @param sc the {@link Scanner} to parse the game from, which is not closed + * after this process + * @return the parsed {@link PGNGame} + */ + public static PGNGame parse(Scanner sc) { PGNGame game = new PGNGame(); MatchResult matchResult; @@ -72,6 +82,11 @@ public class PGNGame { return game; } + /** + * Serializes this game to {@code PGN} format. + * + * @param pw the writer to write the game to + */ public void writePGN(PrintWriter pw) { // Set the unknown result tag if no result tag is specified tagPairs.putIfAbsent("Result", "*"); @@ -111,11 +126,28 @@ public class PGNGame { pw.print(tagPairs.get("Result")); } + /** + * @param tagName the name of a game tag + * @return the value of the game tag + */ public String getTag(String tagName) { return tagPairs.get(tagName); } + /** + * @param tagName the name of a game tag + * @return {@code true} if the tag is present + */ public boolean hasTag(String tagName) { return tagPairs.containsKey(tagName); } + /** + * Sets a game tag. + * + * @param tagName the name of the tag + * @param tagValue the value of the tag + */ public void setTag(String tagName, String tagValue) { tagPairs.put(tagName, tagValue); } + /** + * @return the board associated with this game + */ public Board getBoard() { return board; } } diff --git a/src/dev/kske/chess/uci/UCIHandle.java b/src/dev/kske/chess/uci/UCIHandle.java index 777d769..35aafed 100644 --- a/src/dev/kske/chess/uci/UCIHandle.java +++ b/src/dev/kske/chess/uci/UCIHandle.java @@ -11,7 +11,7 @@ import dev.kske.chess.board.Move; * Project: Chess
* File: UCIHandle.java
* Created: 18.07.2019
- * + * * @since Chess v0.3-alpha * @author Kai S. K. Engelbart */ @@ -21,12 +21,22 @@ public class UCIHandle { private final PrintWriter out; private final UCIReceiver receiver; + /** + * Creates an instance of {@link UCIHandle}. The engine process is started and + * passed to a new {@link UCIReceiver}. + * + * @param enginePath the path to the engine executable + * @throws IOException if the engine process could not be started + */ public UCIHandle(String enginePath) throws IOException { process = new ProcessBuilder(enginePath).start(); out = new PrintWriter(process.getOutputStream(), true); receiver = new UCIReceiver(process.getInputStream()); } + /** + * Starts the {@link UCIReceiver} used to gather engine output. + */ public void start() { new Thread(receiver, "UCI Receiver").start(); uci(); @@ -39,7 +49,7 @@ public class UCIHandle { /** * Switches the debug mode of the engine on or off. - * + * * @param debug Enables debugging if set to {@code true}, disables it otherwise */ public void debug(boolean debug) { out.println("debug " + (debug ? "on" : "off")); } @@ -51,14 +61,14 @@ public class UCIHandle { /** * Signifies a button press to the engine. - * + * * @param name The name of the button */ public void setOption(String name) { out.println("setoption name " + name); } /** * Changes an internal parameter of the engine. - * + * * @param name The name of the parameter * @param value The value of the parameter */ @@ -66,7 +76,7 @@ public class UCIHandle { /** * Registers the engine - * + * * @param name The name the engine should be registered with * @param code The code the engine should be registered with */ @@ -89,14 +99,14 @@ public class UCIHandle { /** * Sets up the position described in the FEN string. - * + * * @param fen FEN representation of the current board */ public void positionFEN(String fen) { out.println("position fen " + fen); } /** * Sets up the position described by a list of moves. - * + * * @param moves the moves to execute from the starting position to reach the * desired position */ @@ -117,7 +127,7 @@ public class UCIHandle { * the call if they are not {@code null}, greater than zero or {@code true} for * {@code searchMoves}, all integer parameters and all boolean parameters * respectively. - * + * * @param searchMoves restrict the search to these moves only * @param ponder start the search in ponder mode * @param wTime the amount of milliseconds left on white's clock @@ -196,5 +206,10 @@ public class UCIHandle { */ public void quit() { out.println("quit"); } - public void setListener(UCIListener listener) { receiver.addListener(listener); } + /** + * Registers a UCI listener. + * + * @param listener the UCI listener to register + */ + public void registerListener(UCIListener listener) { receiver.registerListener(listener); } } diff --git a/src/dev/kske/chess/uci/UCIInfo.java b/src/dev/kske/chess/uci/UCIInfo.java index 074b09a..45a9147 100644 --- a/src/dev/kske/chess/uci/UCIInfo.java +++ b/src/dev/kske/chess/uci/UCIInfo.java @@ -1,10 +1,6 @@ package dev.kske.chess.uci; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import dev.kske.chess.board.Move; @@ -12,7 +8,7 @@ import dev.kske.chess.board.Move; * Project: Chess
* File: UCIInfo.java
* Created: 28.07.2019
- * + * * @since Chess v0.3-alpha * @author Kai S. K. Engelbart */ @@ -47,6 +43,12 @@ public class UCIInfo { "refutation", "currline"); + /** + * Creates an instance of {@link UCIInfo} by parsing the argument list of a UCI + * info command generated from an engine. + * + * @param line the UCI info argument list to parse + */ public UCIInfo(String line) { String[] tokens = line.split(" "); @@ -106,7 +108,7 @@ public class UCIInfo { break; case "currline": // A CPU number of 1 can be omitted - final Integer cpu = tokens[i].matches("\\d+") ? Integer.valueOf(tokens[i++]) : 1; + final Integer cpu = tokens[i].matches("\\d+") ? Integer.parseInt(tokens[i++]) : 1; final ArrayList moves = new ArrayList<>(); while (i < tokens.length && !params.contains(tokens[i])) moves.add(Move.fromLAN(tokens[i++])); diff --git a/src/dev/kske/chess/uci/UCIReceiver.java b/src/dev/kske/chess/uci/UCIReceiver.java index 1d80b6b..7877eae 100644 --- a/src/dev/kske/chess/uci/UCIReceiver.java +++ b/src/dev/kske/chess/uci/UCIReceiver.java @@ -1,9 +1,6 @@ package dev.kske.chess.uci; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.io.*; import java.util.ArrayList; import java.util.List; @@ -13,7 +10,7 @@ import dev.kske.chess.board.Move; * Project: Chess
* File: UCIReceiver.java
* Created: 19.07.2019
- * + * * @since Chess v0.3-alpha * @author Kai S. K. Engelbart */ @@ -23,11 +20,19 @@ public class UCIReceiver implements Runnable { private List listeners; + /** + * Creates an instance of {@link UCIReceiver}. + * + * @param in the input stream to parse for commands generated by the engine + */ public UCIReceiver(InputStream in) { this.in = new BufferedReader(new InputStreamReader(in)); listeners = new ArrayList<>(); } + /** + * Starts listening for UCI commands passed through the input stream. + */ @Override public void run() { String line; @@ -135,5 +140,10 @@ public class UCIReceiver implements Runnable { private void parseOption(String line) { listeners.forEach(l -> l.onOption(new UCIOption((line)))); } - public void addListener(UCIListener listener) { listeners.add(listener); } + /** + * Registers a UCI listener + * + * @param listener the UCI listener to register + */ + public void registerListener(UCIListener listener) { listeners.add(listener); } } diff --git a/src/dev/kske/chess/ui/AIConfigDialog.java b/src/dev/kske/chess/ui/AIConfigDialog.java index 64515cb..54ef3d3 100644 --- a/src/dev/kske/chess/ui/AIConfigDialog.java +++ b/src/dev/kske/chess/ui/AIConfigDialog.java @@ -2,21 +2,18 @@ package dev.kske.chess.ui; import java.awt.Dimension; -import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JSpinner; -import javax.swing.SpinnerNumberModel; +import javax.swing.*; /** * Project: Chess
* File: AIConfigDialog.java
* Created: 16.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ @Deprecated +@SuppressWarnings("javadoc") public class AIConfigDialog extends JDialog { private static final long serialVersionUID = -8047984368152479992L; @@ -28,7 +25,7 @@ public class AIConfigDialog extends JDialog { public AIConfigDialog() { setSize(new Dimension(337, 212)); setModal(true); - setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); setTitle("AI Configuration"); getContentPane().setLayout(null); @@ -50,8 +47,8 @@ public class AIConfigDialog extends JDialog { btnOk.setBounds(16, 137, 84, 28); getContentPane().add(btnOk); btnOk.addActionListener((evt) -> { - maxDepth = ((Integer) spMaxDepth.getValue()).intValue(); - alphaBetaThreshold = ((Integer) spAlphaBetaThreshold.getValue()).intValue(); + maxDepth = ((Integer) spMaxDepth.getValue()); + alphaBetaThreshold = ((Integer) spAlphaBetaThreshold.getValue()); startGame = true; dispose(); }); diff --git a/src/dev/kske/chess/ui/BoardComponent.java b/src/dev/kske/chess/ui/BoardComponent.java index b4120c8..efeac5e 100644 --- a/src/dev/kske/chess/ui/BoardComponent.java +++ b/src/dev/kske/chess/ui/BoardComponent.java @@ -16,7 +16,7 @@ import dev.kske.chess.io.TextureUtil; * A square panel for rendering the chess board. To work correctly, * this must be added to a parent component that allows the child to decide the * size. - * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ @@ -28,6 +28,12 @@ public class BoardComponent extends JComponent { private Board board; + /** + * Creates an instance of {@link BoardComponent}. + * + * @param boardPane the board pane inside which this board component is + * contained + */ public BoardComponent(BoardPane boardPane) { this.boardPane = boardPane; setSize(boardPane.getPreferredSize()); @@ -37,7 +43,7 @@ public class BoardComponent extends JComponent { protected void paintComponent(Graphics g) { super.paintComponent(g); - final int tileSize = getTileSize(); + final int tileSize = boardPane.getTileSize(); // Draw the board g.setColor(Color.white); @@ -54,10 +60,16 @@ public class BoardComponent extends JComponent { g.drawImage(TextureUtil.getPieceTexture(board.getBoardArr()[i][j]), i * tileSize, j * tileSize, this); } - public int getTileSize() { return boardPane.getTileSize(); } - + /** + * @return the board rendered by this board component + */ public Board getBoard() { return board; } + /** + * Sets the board rendered by this board component and repaints the component + * + * @param board the board rendered by this board component + */ public void setBoard(Board board) { this.board = board; repaint(); diff --git a/src/dev/kske/chess/ui/BoardPane.java b/src/dev/kske/chess/ui/BoardPane.java index cb9740e..e18ea52 100644 --- a/src/dev/kske/chess/ui/BoardPane.java +++ b/src/dev/kske/chess/ui/BoardPane.java @@ -5,10 +5,13 @@ import java.awt.Dimension; import javax.swing.JLayeredPane; /** + * Combines a {@link BoardComponent} and an {@link OverlayComponent} into a + * layered pane. + *
* Project: Chess
* File: BoardPane.java
* Created: 08.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ @@ -21,6 +24,9 @@ public class BoardPane extends JLayeredPane { private int tileSize; + /** + * Creates an instance of {@link BoardPane}. + */ public BoardPane() { boardComponent = new BoardComponent(this); overlayComponent = new OverlayComponent(this); @@ -37,9 +43,18 @@ public class BoardPane extends JLayeredPane { @Override public Dimension getPreferredSize() { return new Dimension(480, 480); } + /** + * @return the board component contained inside this board pane + */ public BoardComponent getBoardComponent() { return boardComponent; } + /** + * @return overlay component contained inside this board pane + */ public OverlayComponent getOverlayComponent() { return overlayComponent; } + /** + * @return the size of an individual board tile in pixels + */ public int getTileSize() { return tileSize; } } diff --git a/src/dev/kske/chess/ui/DialogUtil.java b/src/dev/kske/chess/ui/DialogUtil.java index b8dc9b1..6a41eae 100644 --- a/src/dev/kske/chess/ui/DialogUtil.java +++ b/src/dev/kske/chess/ui/DialogUtil.java @@ -3,20 +3,12 @@ package dev.kske.chess.ui; import java.awt.Component; import java.awt.Font; import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import java.util.*; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.prefs.Preferences; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JFileChooser; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; +import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import dev.kske.chess.io.EngineUtil; @@ -38,6 +30,13 @@ public class DialogUtil { */ private static Preferences preferences = Preferences.userNodeForPackage(DialogUtil.class); + /** + * Displays a parameterized file opening dialog. + * + * @param parent the parent component of the dialog + * @param action the action executed with the selected files a its argument + * @param filters the file extension filters passed to the dialog + */ public static void showFileSelectionDialog(Component parent, Consumer> action, Collection filters) { JFileChooser fileChooser = createFileChooser(filters); if (fileChooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) { @@ -46,6 +45,13 @@ public class DialogUtil { } } + /** + * Displays a parameterized file saving dialog. + * + * @param parent the parent component of the dialog + * @param action the action executed with the selected file a its argument + * @param filters the file extension filters passed to the dialog + */ public static void showFileSaveDialog(Component parent, Consumer action, Collection filters) { JFileChooser fileChooser = createFileChooser(filters); if (fileChooser.showSaveDialog(parent) == JFileChooser.APPROVE_OPTION) { @@ -63,6 +69,16 @@ public class DialogUtil { return fileChooser; } + /** + * Displays a dialog in which the user can select the player types for a + * game.
+ *
+ * The dialog will always display {@code Natural Player} and {@code AIPlayer}, + * as well as all engine names stored by {@link EngineUtil}. + * + * @param parent the parent component of the dialog + * @param action the action executed with the two selected names as arguments + */ public static void showGameConfigurationDialog(Component parent, BiConsumer action) { JPanel dialogPanel = new JPanel(); diff --git a/src/dev/kske/chess/ui/GameDropTarget.java b/src/dev/kske/chess/ui/GameDropTarget.java index 4419495..3a5e9b4 100644 --- a/src/dev/kske/chess/ui/GameDropTarget.java +++ b/src/dev/kske/chess/ui/GameDropTarget.java @@ -10,10 +10,13 @@ import java.io.IOException; import java.util.List; /** + * Enables drag and drop support of {@code FEN} and {@code PGN} files for the + * {@link MainWindow}.
+ *
* Project: Chess
* File: GameDropTarget.java
* Created: 13 Aug 2019
- * + * * @since Chess v0.3-alpha * @author Kai S. K. Engelbart */ @@ -21,6 +24,12 @@ public class GameDropTarget extends DropTargetAdapter { private MainWindow mainWindow; + /** + * Creates an instance of {@link GameDropTarget}. + * + * @param mainWindow the {@link MainWindow} onto which {@code FEN} and + * {@code PGN} files can be dropped + */ public GameDropTarget(MainWindow mainWindow) { this.mainWindow = mainWindow; } @SuppressWarnings("unchecked") diff --git a/src/dev/kske/chess/ui/GamePane.java b/src/dev/kske/chess/ui/GamePane.java index 2b542eb..ef88934 100644 --- a/src/dev/kske/chess/ui/GamePane.java +++ b/src/dev/kske/chess/ui/GamePane.java @@ -7,28 +7,19 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Set; -import javax.swing.DefaultListModel; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.ListSelectionModel; +import javax.swing.*; import dev.kske.chess.board.BoardState; import dev.kske.chess.board.MoveNode; import dev.kske.chess.board.Piece.Color; -import dev.kske.chess.event.Event; -import dev.kske.chess.event.EventBus; -import dev.kske.chess.event.GameStartEvent; -import dev.kske.chess.event.MoveEvent; -import dev.kske.chess.event.Subscribable; +import dev.kske.chess.event.*; import dev.kske.chess.game.Game; import dev.kske.chess.game.NaturalPlayer; /** + * The part of this application's {@link MainWindow} that displays {@link Game}s + * and other components allowing to manipulate them.
+ *
* Project: Chess
* File: GamePane.java
* Created: 23.08.2019
@@ -47,6 +38,9 @@ public class GamePane extends JComponent { private JPanel moveSelectionPanel; private JButton btnFirst, btnPrevious, btnNext, btnLast; + /** + * Creates an instance of {@link GamePane}. + */ public GamePane() { activeColor = Color.WHITE; @@ -107,13 +101,11 @@ public class GamePane extends JComponent { btnNext = new JButton("Next"); btnNext.addActionListener((evt) -> { - if(game != null) { - int numVariations = game.getBoard().getLog().getLast().getVariations().size(); - int index; - if(numVariations == 1) - index = 1; - else - index = Integer.parseInt(JOptionPane.showInputDialog("Enter the variation index.")); + if (game != null) { + int numVariations = game.getBoard().getLog().getLast().getVariations().size(); + int index; + if (numVariations == 1) index = 1; + else index = Integer.parseInt(JOptionPane.showInputDialog("Enter the variation index.")); game.getBoard().selectNextNode(index); getBoardPane().getOverlayComponent().clearArrow(); repaint(); @@ -152,7 +144,7 @@ public class GamePane extends JComponent { for (int i = 0; i < 8; i++) { numberPanel.add(new JLabel(String.valueOf(8 - i))); JLabel letterLabel = new JLabel(String.valueOf((char) (65 + i))); - letterLabel.setHorizontalAlignment(JLabel.CENTER); + letterLabel.setHorizontalAlignment(SwingConstants.CENTER); letterPanel.add(letterLabel); } @@ -172,7 +164,7 @@ public class GamePane extends JComponent { // Listen to moves and game (re-)starts and update the move list or disable the // color switching buttons if necessary - EventBus.getInstance().register(new Subscribable() { + EventBus.getInstance().register(new Subscriber() { @Override public void handle(Event event) { diff --git a/src/dev/kske/chess/ui/GameTabComponent.java b/src/dev/kske/chess/ui/GameTabComponent.java index ff492b4..44f4ca0 100644 --- a/src/dev/kske/chess/ui/GameTabComponent.java +++ b/src/dev/kske/chess/ui/GameTabComponent.java @@ -1,22 +1,15 @@ package dev.kske.chess.ui; -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Graphics; -import java.awt.Graphics2D; +import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JTabbedPane; +import javax.swing.*; import javax.swing.plaf.basic.BasicButtonUI; /** + * Renders the title and the closing button of a {@link JTabbedPane}.
+ *
* Project: Chess
* File: GameTabComponent.java
* Created: 11 Dec 2019
@@ -29,6 +22,12 @@ public class GameTabComponent extends JPanel { private static final long serialVersionUID = 9022979950018125935L; + /** + * Creates an instance of {@link GameTabComponent}. + * + * @param tabbedPane the tabbed pane which contains this + * {@link GameTabComponent} + */ public GameTabComponent(JTabbedPane tabbedPane) { super(new FlowLayout(FlowLayout.LEFT, 0, 0)); if (tabbedPane == null) throw new NullPointerException("TabbedPane is null"); diff --git a/src/dev/kske/chess/ui/MainWindow.java b/src/dev/kske/chess/ui/MainWindow.java index 22ccbc3..fee7aa4 100644 --- a/src/dev/kske/chess/ui/MainWindow.java +++ b/src/dev/kske/chess/ui/MainWindow.java @@ -10,10 +10,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.List; -import javax.swing.JComboBox; -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.swing.JTabbedPane; +import javax.swing.*; import dev.kske.chess.board.Board; import dev.kske.chess.board.FENString; @@ -38,6 +35,8 @@ public class MainWindow extends JFrame { /** * Launch the application. + * + * @param args command line arguments are ignored */ public static void main(String[] args) { EventQueue.invokeLater(() -> { @@ -81,7 +80,7 @@ public class MainWindow extends JFrame { } /** - * @return The currently selected {@link GamePane} component + * @return the currently selected {@link GamePane} component */ public GamePane getSelectedGamePane() { return (GamePane) tabbedPane.getSelectedComponent(); } @@ -89,7 +88,7 @@ public class MainWindow extends JFrame { * Creates a new {@link GamePane}, adds it to the tabbed pane and opens it. * The new tab has the title {@code Game n} where {@code n} is its number. * - * @return The new {@link GamePane} + * @return the new {@link GamePane} */ public GamePane addGamePane() { return addGamePane("Game " + (tabbedPane.getTabCount() + 1)); } @@ -97,7 +96,7 @@ public class MainWindow extends JFrame { * Creates a new {@link GamePane}, adds it to the tabbed pane and opens it. * * @param title The title of the {@link GamePane} - * @return The new {@link GamePane} + * @return the new {@link GamePane} */ public GamePane addGamePane(String title) { GamePane gamePane = new GamePane(); @@ -107,6 +106,15 @@ public class MainWindow extends JFrame { return gamePane; } + /** + * Creates a new {@link GamePane}, adds it to the tabbed pane and immediately + * displays a game configuration dialog for a new game on an existing + * {@link Board}. + * + * @param title the title of the {@link GamePane} + * @param board the {@link Board} with which the new {@link Game} is started + * @return the new {@link GamePane} + */ public GamePane addGamePane(String title, Board board) { GamePane gamePane = addGamePane(title); DialogUtil.showGameConfigurationDialog(this, @@ -168,6 +176,12 @@ public class MainWindow extends JFrame { }); } + /** + * Saves the current {@link Game} as a file in {@code PGN} or {@code FEN} + * format. + * + * @param file the file in which to save the current {@link Game} + */ public void saveFile(File file) { final int dotIndex = file.getName().lastIndexOf('.'); final String extension = file.getName().substring(dotIndex).toLowerCase(); diff --git a/src/dev/kske/chess/ui/MenuBar.java b/src/dev/kske/chess/ui/MenuBar.java index 3ea86cc..b86f844 100644 --- a/src/dev/kske/chess/ui/MenuBar.java +++ b/src/dev/kske/chess/ui/MenuBar.java @@ -4,10 +4,7 @@ import java.awt.Toolkit; import java.awt.datatransfer.StringSelection; import java.util.Arrays; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; +import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import dev.kske.chess.board.FENString; @@ -19,7 +16,7 @@ import dev.kske.chess.io.EngineUtil; * Project: Chess
* File: MenuBar.java
* Created: 16.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ @@ -29,6 +26,11 @@ public class MenuBar extends JMenuBar { private final MainWindow mainWindow; + /** + * Creates an instance of {@link MenuBar}. + * + * @param mainWindow the main window inside which this menu bar is contained + */ public MenuBar(MainWindow mainWindow) { this.mainWindow = mainWindow; diff --git a/src/dev/kske/chess/ui/OverlayComponent.java b/src/dev/kske/chess/ui/OverlayComponent.java index cee5449..604e712 100644 --- a/src/dev/kske/chess/ui/OverlayComponent.java +++ b/src/dev/kske/chess/ui/OverlayComponent.java @@ -1,12 +1,6 @@ package dev.kske.chess.ui; -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Polygon; -import java.awt.Shape; +import java.awt.*; import java.awt.geom.AffineTransform; import java.util.ArrayList; import java.util.List; @@ -20,7 +14,7 @@ import dev.kske.chess.board.Position; * Project: Chess
* File: OverlayComponent.java
* Created: 08.07.2019
- * + * * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ @@ -33,6 +27,12 @@ public class OverlayComponent extends JComponent { private List dots; private Move arrow; + /** + * Creates an instance of {@link OverlayComponent}. + * + * @param boardPane the board pane inside which this overlay component is + * contained + */ public OverlayComponent(BoardPane boardPane) { this.boardPane = boardPane; dots = new ArrayList<>(); @@ -43,7 +43,7 @@ public class OverlayComponent extends JComponent { protected void paintComponent(Graphics g) { super.paintComponent(g); - final int tileSize = getTileSize(); + final int tileSize = boardPane.getTileSize(); // Draw an arrow representing the last move and mark its position and // destination @@ -107,26 +107,46 @@ public class OverlayComponent extends JComponent { return new Point((int) ((p1.x + p2.x) / 2.0), (int) ((p1.y + p2.y) / 2.0)); } + /** + * Displays green dots at a list of positions. + * + * @param dots the positions at which the dots should be displayed + */ public void displayDots(List dots) { this.dots.clear(); this.dots.addAll(dots); repaint(); } + /** + * Clears all dots displayed at some positions. + */ public void clearDots() { dots.clear(); repaint(); } + /** + * Displays an arrow from the position to the destination of a move. + * + * @param arrow the move indicating the arrows position and destination + */ public void displayArrow(Move arrow) { this.arrow = arrow; repaint(); } + /** + * Clears the arrow displayed to indicate a move. + */ public void clearArrow() { arrow = null; repaint(); } + /** + * @return the size of one board tile in pixels. + * @see dev.kske.chess.ui.BoardPane#getTileSize() + */ public int getTileSize() { return boardPane.getTileSize(); } } diff --git a/test/dev/kske/chess/board/BoardTest.java b/test/dev/kske/chess/board/BoardTest.java index 776d221..01a73bc 100644 --- a/test/dev/kske/chess/board/BoardTest.java +++ b/test/dev/kske/chess/board/BoardTest.java @@ -1,7 +1,6 @@ package dev.kske.chess.board; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -16,7 +15,7 @@ import dev.kske.chess.board.Piece.Color; */ class BoardTest { - Board board; + private Board board; /** * @throws java.lang.Exception @@ -27,7 +26,7 @@ class BoardTest { } /** - * Test method for {@link dev.kske.chess.board.Board#clone()}. + * Test method for {@link Board#Board(Board, boolean)}. */ @Test void testClone() { diff --git a/test/dev/kske/chess/board/FENStringTest.java b/test/dev/kske/chess/board/FENStringTest.java index b2c1e43..893282a 100644 --- a/test/dev/kske/chess/board/FENStringTest.java +++ b/test/dev/kske/chess/board/FENStringTest.java @@ -16,14 +16,19 @@ import dev.kske.chess.exception.ChessException; * Project: Chess
* File: FENStringTest.java
* Created: 24 Oct 2019
- * + * * @author Kai S. K. Engelbart */ class FENStringTest { - List fenStrings = new ArrayList<>(); - List boards = new ArrayList<>(); + private List fenStrings = new ArrayList<>(); + private List boards = new ArrayList<>(); + /** + * Removes all pieces from a board + * + * @param board the board to clean + */ void cleanBoard(Board board) { for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++) @@ -51,7 +56,7 @@ class FENStringTest { } /** - * Test method for {@link dev.kske.chess.board.FENString#toString()}. + * Test method for {@link FENString#toString()}. */ @Test void testToString() { @@ -60,8 +65,8 @@ class FENStringTest { } /** - * Test method for {@link dev.kske.chess.board.FENString#getBoard()}. - * + * Test method for {@link FENString#getBoard()}. + * * @throws ChessException */ @Test diff --git a/test/dev/kske/chess/board/LogTest.java b/test/dev/kske/chess/board/LogTest.java index fafc6dc..cbbc067 100644 --- a/test/dev/kske/chess/board/LogTest.java +++ b/test/dev/kske/chess/board/LogTest.java @@ -1,10 +1,6 @@ package dev.kske.chess.board; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; @@ -18,10 +14,10 @@ import dev.kske.chess.board.Piece.Color; */ class LogTest { - Log log = new Log(); + private Log log = new Log(); /** - * Test method for {@link dev.kske.chess.board.Log#Log()}. + * Test method for {@link Log#Log()}. */ @Test void testLog() { @@ -35,7 +31,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#clone()}. + * Test method for {@link Log#Log(Log, boolean)}. */ @Test void testClone() { @@ -52,7 +48,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#add(dev.kske.chess.board.Move, dev.kske.chess.board.Piece, boolean)}. + * Test method for {@link Log#add(Move, Piece, Piece)}. */ @Test void testAdd() { @@ -60,7 +56,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#removeLast()}. + * Test method for {@link Log#removeLast()}. */ @Test void testRemoveLast() { @@ -68,7 +64,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#isEmpty()}. + * Test method for {@link Log#isEmpty()}. */ @Test void testIsEmpty() { @@ -76,7 +72,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#reset()}. + * Test method for {@link Log#reset()}. */ @Test void testReset() { @@ -84,7 +80,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#getRoot()}. + * Test method for {@link Log#getRoot()}. */ @Test void testGetRoot() { @@ -92,7 +88,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#getLast()}. + * Test method for {@link Log#getLast()}. */ @Test void testGetLast() { @@ -100,7 +96,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#getEnPassant()}. + * Test method for {@link Log#getEnPassant()}. */ @Test void testGetEnPassant() { @@ -108,7 +104,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#setEnPassant(dev.kske.chess.board.Position)}. + * Test method for {@link Log#setEnPassant(dev.kske.chess.board.Position)}. */ @Test void testSetEnPassant() { @@ -116,7 +112,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#getActiveColor()}. + * Test method for {@link Log#getActiveColor()}. */ @Test void testGetActiveColor() { @@ -124,7 +120,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#setActiveColor(dev.kske.chess.board.Piece.Color)}. + * Test method for {@link Log#setActiveColor(dev.kske.chess.board.Piece.Color)}. */ @Test void testSetActiveColor() { @@ -132,7 +128,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#getFullmoveNumber()}. + * Test method for {@link Log#getFullmoveNumber()}. */ @Test void testGetFullmoveCounter() { @@ -140,7 +136,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#setFullmoveNumber(int)}. + * Test method for {@link Log#setFullmoveNumber(int)}. */ @Test void testSetFullmoveCounter() { @@ -148,7 +144,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#getHalfmoveClock()}. + * Test method for {@link Log#getHalfmoveClock()}. */ @Test void testGetHalfmoveClock() { @@ -156,7 +152,7 @@ class LogTest { } /** - * Test method for {@link dev.kske.chess.board.Log#setHalfmoveClock(int)}. + * Test method for {@link Log#setHalfmoveClock(int)}. */ @Test void testSetHalfmoveClock() { diff --git a/test/dev/kske/chess/board/PositionTest.java b/test/dev/kske/chess/board/PositionTest.java index 5cb4a1b..78509c6 100644 --- a/test/dev/kske/chess/board/PositionTest.java +++ b/test/dev/kske/chess/board/PositionTest.java @@ -12,14 +12,14 @@ import org.junit.jupiter.api.Test; */ class PositionTest { - final int n = 4; - Position[] positions = new Position[] { new Position(0, 0), new Position(7, 7), new Position(0, 7), new Position(7, 0) }; - String[] sans = new String[] { "a8", "h1", "a1", "h8" }; - String[] strings = new String[] { "[0, 0]", "[7, 7]", "[0, 7]", "[7, 0]" }; + private final int n = 4; + private Position[] positions = new Position[] { new Position(0, 0), new Position(7, 7), new Position(0, 7), new Position(7, 0) }; + private String[] sans = new String[] { "a8", "h1", "a1", "h8" }; + private String[] strings = new String[] { "[0, 0]", "[7, 7]", "[0, 7]", "[7, 0]" }; /** * Test method for - * {@link dev.kske.chess.board.Position#fromLAN(java.lang.String)}. + * {@link Position#fromLAN(java.lang.String)}. */ @Test void testFromSAN() { @@ -28,7 +28,7 @@ class PositionTest { } /** - * Test method for {@link dev.kske.chess.board.Position#toLAN()}. + * Test method for {@link Position#toLAN()}. */ @Test void testToSAN() { @@ -37,7 +37,7 @@ class PositionTest { } /** - * Test method for {@link dev.kske.chess.board.Position#toString()}. + * Test method for {@link Position#toString()}. */ @Test void testToString() { diff --git a/test/dev/kske/chess/pgn/PGNDatabaseTest.java b/test/dev/kske/chess/pgn/PGNDatabaseTest.java index 35e31e1..567dcb5 100644 --- a/test/dev/kske/chess/pgn/PGNDatabaseTest.java +++ b/test/dev/kske/chess/pgn/PGNDatabaseTest.java @@ -1,7 +1,5 @@ package dev.kske.chess.pgn; -import static org.junit.jupiter.api.Assertions.fail; - import java.io.File; import java.io.FileNotFoundException; @@ -19,18 +17,14 @@ class PGNDatabaseTest { /** * Test method for {@link dev.kske.chess.pgn.PGNDatabase#load(java.io.File)}. - * - * @throws ChessException - * @throws FileNotFoundException + * + * @throws ChessException if an error occurs while parsing the file + * @throws FileNotFoundException if the test file {@code test.pgn} is not + * present */ @Test - void testLoad() { + void testLoad() throws FileNotFoundException, ChessException { PGNDatabase db = new PGNDatabase(); - try { - db.load(new File(getClass().getClassLoader().getResource("test.pgn").getFile())); - } catch (FileNotFoundException | ChessException e) { - e.printStackTrace(); - fail(e); - } + db.load(new File(getClass().getClassLoader().getResource("test.pgn").getFile())); } }