diff --git a/.classpath b/.classpath
index 8469573..afa4751 100644
--- a/.classpath
+++ b/.classpath
@@ -1,18 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/dev/kske/chess/board/Bishop.java b/src/dev/kske/chess/board/Bishop.java
index 9412a30..0321e80 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);
}
@@ -29,37 +35,54 @@ public class Bishop extends Piece {
// Diagonal moves to the lower right
for (int i = pos.x + 1, j = pos.y + 1; i < 8 && j < 8; i++, j++) {
Move move = new Move(pos, new Position(i, j));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
-
// Diagonal moves to the lower left
for (int i = pos.x - 1, j = pos.y + 1; i >= 0 && j < 8; i--, j++) {
Move move = new Move(pos, new Position(i, j));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
-
// Diagonal moves to the upper right
for (int i = pos.x + 1, j = pos.y - 1; i < 8 && j >= 0; i++, j--) {
Move move = new Move(pos, new Position(i, j));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
-
// Diagonal moves to the upper left
for (int i = pos.x - 1, j = pos.y - 1; i >= 0 && j >= 0; i--, j--) {
Move move = new Move(pos, new Position(i, j));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
return moves;
}
diff --git a/src/dev/kske/chess/board/Board.java b/src/dev/kske/chess/board/Board.java
index 114f32e..a435c17 100644
--- a/src/dev/kske/chess/board/Board.java
+++ b/src/dev/kske/chess/board/Board.java
@@ -1,13 +1,10 @@
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;
+import dev.kske.chess.event.MoveEvent;
/**
* Project: Chess
@@ -19,29 +16,35 @@ import dev.kske.chess.board.Piece.Color;
*/
public class Board {
- private Piece[][] boardArr = new Piece[8][8];
- private Map kingPos = new HashMap<>();
- private Log log = new Log();
+ private Piece[][] boardArr = new Piece[8][8];
+ private Map kingPos = new EnumMap<>(Color.class);
+ private Log log = new Log();
/**
* Initializes the board with the default chess starting position.
*/
- public Board() { initDefaultPositions(); }
+ public Board() {
+ initDefaultPositions();
+ }
/**
* Creates a copy of another {@link Board} instance.
- * The created object is a deep copy, but does not contain any move history
- * apart from the current {@link MoveNode}.
+ * The created object is a deep copy, and can optionally contain the move
+ * history of the Board to copy.
*
- * @param other The {@link Board} instance to copy
- * @param copyVariations TODO
+ * @param other The Board instance to copy
+ * @param copyVariations if set to {@code true}, the {@link Log} object of
+ * the
+ * other Board instance is copied with its entire move
+ * history
*/
public Board(Board other, boolean copyVariations) {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
- if (other.boardArr[i][j] == null) continue;
- boardArr[i][j] = (Piece) other.boardArr[i][j].clone();
- boardArr[i][j].board = this;
+ if (other.boardArr[i][j] == null)
+ continue;
+ boardArr[i][j] = (Piece) other.boardArr[i][j].clone();
+ boardArr[i][j].board = this;
}
kingPos.putAll(other.kingPos);
@@ -60,19 +63,18 @@ 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);
+ if (piece == null || !piece.isValidMove(move))
+ return false;
- // 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;
}
/**
@@ -81,14 +83,15 @@ public class Board {
* @param move The move to execute
*/
public void move(Move move) {
- Piece piece = getPos(move);
- Piece capturePiece = getDest(move);
+ Piece piece = getPos(move);
+ Piece capturePiece = getDest(move);
// Execute the move
move.execute(this);
// Update the king's position if the moved piece is the king
- if (piece instanceof King) kingPos.put(piece.getColor(), move.getDest());
+ if (piece instanceof King)
+ kingPos.put(piece.getColor(), move.getDest());
// Update log
log.add(move, piece, capturePiece);
@@ -107,19 +110,67 @@ public class Board {
* Reverts the last move and removes it from the log.
*/
public void revert() {
- MoveNode moveNode = log.getLast();
- Move move = moveNode.move;
+ MoveNode moveNode = log.getLast();
+ Move move = moveNode.move;
// Revert the move
move.revert(this, moveNode.capturedPiece);
// Update the king's position if the moved piece is the king
- if (getPos(move) instanceof King) kingPos.put(getPos(move).getColor(), move.getPos());
+ if (getPos(move) instanceof King)
+ kingPos.put(getPos(move).getColor(), move.getPos());
// Update log
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;
+
+ // Revert the move
+ move.revert(this, moveNode.capturedPiece);
+
+ // Select previous move node
+ log.selectPreviousNode();
+
+ // Dispatch move event
+ 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();
+ Move move = moveNode.move;
+
+ // Execute the next move
+ move.execute(this);
+
+ // Dispatch move event
+ EventBus.getInstance()
+ .dispatch(
+ new MoveEvent(move, getState(log.getActiveColor().opposite()))
+ );
+ }
+
/**
* Generated every legal move for one color
*
@@ -130,11 +181,22 @@ public class Board {
List moves = new ArrayList<>();
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
- if (boardArr[i][j] != null && boardArr[i][j].getColor() == color) moves.addAll(boardArr[i][j].getMoves(new Position(i, j)));
+ if (
+ boardArr[i][j] != null && boardArr[i][j].getColor() == color
+ )
+ moves.addAll(boardArr[i][j].getMoves(new Position(i, j)));
return moves;
}
- public List getMoves(Position pos) { return get(pos).getMoves(pos); }
+ /**
+ * 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);
+ }
/**
* Checks, if the king is in check.
@@ -142,7 +204,9 @@ public class Board {
* @param color The color of the king to check
* @return {@code true}, if the king is in check
*/
- public boolean checkCheck(Color color) { return isAttacked(kingPos.get(color), color.opposite()); }
+ public boolean checkCheck(Color color) {
+ return isAttacked(kingPos.get(color), color.opposite());
+ }
/**
* Checks, if a field can be attacked by pieces of a certain color.
@@ -155,7 +219,11 @@ public class Board {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
Position pos = new Position(i, j);
- if (get(pos) != null && get(pos).getColor() == color && get(pos).isValidMove(new Move(pos, dest))) return true;
+ if (
+ get(pos) != null && get(pos).getColor() == color
+ && get(pos).isValidMove(new Move(pos, dest))
+ )
+ return true;
}
return false;
}
@@ -169,21 +237,30 @@ 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;
+ if (!getMoves(kingPos.get(color)).isEmpty())
+ return false;
+
+ for (Move move : getMoves(color)) {
+ move(move);
+ boolean check = checkCheck(color);
+ revert();
+ if (!check)
+ return false;
}
+ return true;
}
- public BoardState getGameEventType(Color color) {
- return checkCheck(color) ? checkCheckmate(color) ? BoardState.CHECKMATE : BoardState.CHECK
- : getMoves(color).isEmpty() || log.getLast().halfmoveClock >= 50 ? BoardState.STALEMATE : BoardState.NORMAL;
+ /**
+ * 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;
}
/**
@@ -192,39 +269,38 @@ public class Board {
public void initDefaultPositions() {
// Initialize pawns
for (int i = 0; i < 8; i++) {
- boardArr[i][1] = new Pawn(Color.BLACK, this);
- boardArr[i][6] = new Pawn(Color.WHITE, this);
+ boardArr[i][1] = new Pawn(Color.BLACK, this);
+ boardArr[i][6] = new Pawn(Color.WHITE, this);
}
-
// Initialize kings
- boardArr[4][0] = new King(Color.BLACK, this);
- boardArr[4][7] = new King(Color.WHITE, this);
+ boardArr[4][0] = new King(Color.BLACK, this);
+ boardArr[4][7] = new King(Color.WHITE, this);
// Initialize king position objects
kingPos.put(Color.BLACK, new Position(4, 0));
kingPos.put(Color.WHITE, new Position(4, 7));
// Initialize queens
- boardArr[3][0] = new Queen(Color.BLACK, this);
- boardArr[3][7] = new Queen(Color.WHITE, this);
+ boardArr[3][0] = new Queen(Color.BLACK, this);
+ boardArr[3][7] = new Queen(Color.WHITE, this);
// Initialize rooks
- boardArr[0][0] = new Rook(Color.BLACK, this);
- boardArr[0][7] = new Rook(Color.WHITE, this);
- boardArr[7][0] = new Rook(Color.BLACK, this);
- boardArr[7][7] = new Rook(Color.WHITE, this);
+ boardArr[0][0] = new Rook(Color.BLACK, this);
+ boardArr[0][7] = new Rook(Color.WHITE, this);
+ boardArr[7][0] = new Rook(Color.BLACK, this);
+ boardArr[7][7] = new Rook(Color.WHITE, this);
// Initialize knights
- boardArr[1][0] = new Knight(Color.BLACK, this);
- boardArr[1][7] = new Knight(Color.WHITE, this);
- boardArr[6][0] = new Knight(Color.BLACK, this);
- boardArr[6][7] = new Knight(Color.WHITE, this);
+ boardArr[1][0] = new Knight(Color.BLACK, this);
+ boardArr[1][7] = new Knight(Color.WHITE, this);
+ boardArr[6][0] = new Knight(Color.BLACK, this);
+ boardArr[6][7] = new Knight(Color.WHITE, this);
// Initialize bishops
- boardArr[2][0] = new Bishop(Color.BLACK, this);
- boardArr[2][7] = new Bishop(Color.WHITE, this);
- boardArr[5][0] = new Bishop(Color.BLACK, this);
- boardArr[5][7] = new Bishop(Color.WHITE, this);
+ boardArr[2][0] = new Bishop(Color.BLACK, this);
+ boardArr[2][7] = new Bishop(Color.WHITE, this);
+ boardArr[5][0] = new Bishop(Color.BLACK, this);
+ boardArr[5][7] = new Bishop(Color.WHITE, this);
// Clear all other tiles
for (int i = 0; i < 8; i++)
@@ -236,27 +312,33 @@ public class Board {
@Override
public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + Arrays.deepHashCode(boardArr);
- result = prime * result + Objects.hash(kingPos, log);
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.deepHashCode(boardArr);
+ result = prime * result + Objects.hash(kingPos, log);
return result;
}
@Override
public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
Board other = (Board) obj;
- return Arrays.deepEquals(boardArr, other.boardArr) && Objects.equals(kingPos, other.kingPos) && Objects.equals(log, other.log);
+ return Arrays.deepEquals(boardArr, other.boardArr) && Objects
+ .equals(kingPos, other.kingPos) && Objects.equals(log, other.log);
}
/**
* @param pos The position from which to return a piece
* @return The piece at the position
*/
- public Piece get(Position pos) { return boardArr[pos.x][pos.y]; }
+ public Piece get(Position pos) {
+ return boardArr[pos.x][pos.y];
+ }
/**
* Searches for a {@link Piece} inside a file (A - H).
@@ -269,7 +351,12 @@ public class Board {
public int get(Class extends Piece> pieceClass, char file) {
int x = file - 97;
for (int i = 0; i < 8; i++)
- if (boardArr[x][i] != null && boardArr[x][i].getClass() == pieceClass && boardArr[x][i].getColor() == log.getActiveColor()) return 8 - i;
+ if (
+ boardArr[x][i] != null
+ && boardArr[x][i].getClass() == pieceClass
+ && boardArr[x][i].getColor() == log.getActiveColor()
+ )
+ return 8 - i;
return -1;
}
@@ -284,7 +371,11 @@ public class Board {
public char get(Class extends Piece> pieceClass, int rank) {
int y = rank - 1;
for (int i = 0; i < 8; i++)
- if (boardArr[i][y] != null && boardArr[i][y].getClass() == pieceClass && boardArr[i][y].getColor() == log.getActiveColor())
+ if (
+ boardArr[i][y] != null
+ && boardArr[i][y].getClass() == pieceClass
+ && boardArr[i][y].getColor() == log.getActiveColor()
+ )
return (char) (i + 97);
return '-';
}
@@ -294,14 +385,20 @@ public class Board {
*
* @param pieceClass The class of the piece to search for
* @param dest The destination that the piece is required to reach
- * @return The position of a piece that can move to the specified destination
+ * @return The position of a piece that can move to the specified
+ * destination
*/
public Position get(Class extends Piece> pieceClass, Position dest) {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
- if (boardArr[i][j] != null && boardArr[i][j].getClass() == pieceClass && boardArr[i][j].getColor() == log.getActiveColor()) {
+ if (
+ boardArr[i][j] != null
+ && boardArr[i][j].getClass() == pieceClass
+ && boardArr[i][j].getColor() == log.getActiveColor()
+ ) {
Position pos = new Position(i, j);
- if (boardArr[i][j].isValidMove(new Move(pos, dest))) return pos;
+ if (boardArr[i][j].isValidMove(new Move(pos, dest)))
+ return pos;
}
return null;
}
@@ -312,19 +409,25 @@ public class Board {
* @param pos The position to place the piece at
* @param piece The piece to place
*/
- public void set(Position pos, Piece piece) { boardArr[pos.x][pos.y] = piece; }
+ public void set(Position pos, Piece piece) {
+ boardArr[pos.x][pos.y] = piece;
+ }
/**
* @param move The move from which position to return a piece
* @return The piece at the position of the move
*/
- public Piece getPos(Move move) { return get(move.getPos()); }
+ public Piece getPos(Move move) {
+ return get(move.getPos());
+ }
/**
* @param move The move from which destination to return a piece
* @return The piece at the destination of the move
*/
- public Piece getDest(Move move) { return get(move.getDest()); }
+ public Piece getDest(Move move) {
+ return get(move.getDest());
+ }
/**
* Places a piece at the position of a move.
@@ -332,7 +435,9 @@ public class Board {
* @param move The move at which position to place the piece
* @param piece The piece to place
*/
- public void setPos(Move move, Piece piece) { set(move.getPos(), piece); }
+ public void setPos(Move move, Piece piece) {
+ set(move.getPos(), piece);
+ }
/**
* Places a piece at the destination of a move.
@@ -340,7 +445,9 @@ public class Board {
* @param move The move at which destination to place the piece
* @param piece The piece to place
*/
- public void setDest(Move move, Piece piece) { set(move.getDest(), piece); }
+ public void setDest(Move move, Piece piece) {
+ set(move.getDest(), piece);
+ }
/**
* @return The board array
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..27b882e 100644
--- a/src/dev/kske/chess/board/Castling.java
+++ b/src/dev/kske/chess/board/Castling.java
@@ -12,12 +12,29 @@ 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);
+ rookMove = dest.x == 6 ? new Move(7, pos.y, 5, pos.y)
+ : new Move(0, pos.y, 3, pos.y);
}
- public Castling(int xPos, int yPos, int xDest, int yDest) { this(new Position(xPos, yPos), new Position(xDest, yDest)); }
+ /**
+ * 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
public void execute(Board board) {
@@ -34,7 +51,8 @@ public class Castling extends Move {
}
/**
- * @return {@code O-O-O} for a queenside castling or {@code O-O} for a kingside
+ * @return {@code O-O-O} for a queenside castling or {@code O-O} for a
+ * kingside
* castling
*/
@Override
diff --git a/src/dev/kske/chess/board/EnPassant.java b/src/dev/kske/chess/board/EnPassant.java
index 69c48d9..96c41de 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,12 +12,28 @@ 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);
}
- public EnPassant(int xPos, int yPos, int xDest, int yDest) { this(new Position(xPos, yPos), new Position(xDest, yDest)); }
+ /**
+ * 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
public void execute(Board board) {
@@ -28,8 +44,14 @@ public class EnPassant extends Move {
@Override
public void revert(Board board, Piece capturedPiece) {
super.revert(board, capturedPiece);
- board.set(capturePos, new Pawn(board.get(pos).getColor().opposite(), board));
+ 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..fd84df4 100644
--- a/src/dev/kske/chess/board/FENString.java
+++ b/src/dev/kske/chess/board/FENString.java
@@ -15,51 +15,58 @@ import dev.kske.chess.exception.ChessException;
*
* Represents a FEN string and enables parsing an existing FEN string or
* serializing a {@link Board} to one.
- *
+ *
* @since Chess v0.5-alpha
* @author Kai S. K. Engelbart
*/
public class FENString {
- private Board board;
- private String piecePlacement, castlingAvailability;
- private int halfmoveClock, fullmoveNumber;
- private Color activeColor;
- private Position enPassantTargetSquare;
+ private Board board;
+ private String piecePlacement, castlingAvailability;
+ private int halfmoveClock, fullmoveNumber;
+ private Color activeColor;
+ private Position enPassantTargetSquare;
/**
* Constructs a {@link FENString} representing the starting position
* {@code rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1}.
*/
public FENString() {
- board = new Board();
- piecePlacement = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR";
- activeColor = Color.WHITE;
- castlingAvailability = "KQkq";
- halfmoveClock = 0;
- fullmoveNumber = 1;
+ board = new Board();
+ piecePlacement = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR";
+ activeColor = Color.WHITE;
+ castlingAvailability = "KQkq";
+ halfmoveClock = 0;
+ fullmoveNumber = 1;
}
/**
* 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
- Pattern fenPattern = Pattern.compile(
- "^(?(?:[1-8nbrqkpNBRQKP]{1,8}\\/){7}[1-8nbrqkpNBRQKP]{1,8}) (?[wb]) (?-|[KQkq]{1,4}) (?-|[a-h][1-8]) (?\\d+) (?\\d+)$");
- Matcher matcher = fenPattern.matcher(fen);
- if (!matcher.find()) throw new ChessException("FEN string does not match pattern " + fenPattern.pattern());
+ Pattern fenPattern = Pattern.compile(
+ "^(?(?:[1-8nbrqkpNBRQKP]{1,8}\\/){7}[1-8nbrqkpNBRQKP]{1,8}) (?[wb]) (?-|[KQkq]{1,4}) (?-|[a-h][1-8]) (?\\d+) (?\\d+)$"
+ );
+ Matcher matcher = fenPattern.matcher(fen);
+ if (!matcher.find())
+ throw new ChessException(
+ "FEN string does not match pattern " + fenPattern.pattern()
+ );
// Initialize data fields
- piecePlacement = matcher.group("piecePlacement");
- activeColor = Color.fromFirstChar(matcher.group("activeColor").charAt(0));
- castlingAvailability = matcher.group("castlingAvailability");
- if (!matcher.group("enPassantTargetSquare").equals("-")) enPassantTargetSquare = Position.fromLAN(matcher.group("enPassantTargetSquare"));
- halfmoveClock = Integer.parseInt(matcher.group("halfmoveClock"));
- fullmoveNumber = Integer.parseInt(matcher.group("fullmoveNumber"));
+ piecePlacement = matcher.group("piecePlacement");
+ activeColor
+ = Color.fromFirstChar(matcher.group("activeColor").charAt(0));
+ castlingAvailability = matcher.group("castlingAvailability");
+ if (!matcher.group("enPassantTargetSquare").equals("-"))
+ enPassantTargetSquare
+ = Position.fromLAN(matcher.group("enPassantTargetSquare"));
+ halfmoveClock = Integer.parseInt(matcher.group("halfmoveClock"));
+ fullmoveNumber = Integer.parseInt(matcher.group("fullmoveNumber"));
// Initialize and clean board
board = new Board();
@@ -71,30 +78,40 @@ public class FENString {
// Piece placement
final String[] rows = piecePlacement.split("/");
- if (rows.length != 8) throw new ChessException("FEN string contains invalid piece placement");
+ if (rows.length != 8)
+ throw new ChessException(
+ "FEN string contains invalid piece placement"
+ );
for (int i = 0; i < 8; i++) {
- final char[] cols = rows[i].toCharArray();
- int j = 0;
- for (char c : cols) {
-
+ final char[] cols = rows[i].toCharArray();
+ int j = 0;
+ for (char c : cols)
// Empty space
- if (Character.isDigit(c)) {
+ if (Character.isDigit(c))
j += Character.getNumericValue(c);
- } else {
- Color color = Character.isUpperCase(c) ? Color.WHITE : Color.BLACK;
+ else {
+ Color color
+ = Character.isUpperCase(c) ? Color.WHITE : Color.BLACK;
try {
- Constructor extends Piece> pieceConstructor = Piece.fromFirstChar(c).getDeclaredConstructor(Color.class, Board.class);
+ Constructor extends Piece> pieceConstructor = Piece
+ .fromFirstChar(c)
+ .getDeclaredConstructor(Color.class, Board.class);
pieceConstructor.setAccessible(true);
- board.getBoardArr()[j][i] = pieceConstructor.newInstance(color, board);
- } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
- | NoSuchMethodException | SecurityException e) {
+ board.getBoardArr()[j][i]
+ = pieceConstructor.newInstance(color, board);
+ } catch (
+ InstantiationException
+ | IllegalAccessException
+ | IllegalArgumentException
+ | InvocationTargetException
+ | NoSuchMethodException
+ | SecurityException e
+ ) {
e.printStackTrace();
}
++j;
}
- }
}
-
// Active color
board.getLog().setActiveColor(activeColor);
@@ -129,7 +146,7 @@ public class FENString {
/**
* Constructs a {@link FENString} form a {@link Board} object.
- *
+ *
* @param board the {@link Board} object to encode in this {@link FENString}
*/
public FENString(Board board) {
@@ -144,7 +161,8 @@ public class FENString {
for (int j = 0; j < 8; j++) {
final Piece piece = board.getBoardArr()[j][i];
- if (piece == null) ++empty;
+ if (piece == null)
+ ++empty;
else {
// Write empty field count
@@ -152,20 +170,22 @@ public class FENString {
sb.append(empty);
empty = 0;
}
-
// Write piece character
char p = piece.firstChar();
- sb.append(piece.getColor() == Color.WHITE ? Character.toUpperCase(p) : p);
+ sb.append(
+ piece.getColor() == Color.WHITE
+ ? Character.toUpperCase(p)
+ : p
+ );
}
}
-
// Write empty field count
if (empty > 0) {
sb.append(empty);
empty = 0;
}
-
- if (i < 7) sb.append('/');
+ if (i < 7)
+ sb.append('/');
}
piecePlacement = sb.toString();
@@ -174,10 +194,14 @@ public class FENString {
// Castling availability
castlingAvailability = "";
- final char castlingRightsChars[] = new char[] { 'K', 'Q', 'k', 'q' };
+ final char castlingRightsChars[] = new char[] {
+ 'K', 'Q', 'k', 'q'
+ };
for (int i = 0; i < 4; i++)
- if (board.getLog().getCastlingRights()[i]) castlingAvailability += castlingRightsChars[i];
- if (castlingAvailability.isEmpty()) castlingAvailability = "-";
+ if (board.getLog().getCastlingRights()[i])
+ castlingAvailability += castlingRightsChars[i];
+ if (castlingAvailability.isEmpty())
+ castlingAvailability = "-";
// En passant availability
enPassantTargetSquare = board.getLog().getEnPassant();
@@ -191,18 +215,20 @@ public class FENString {
/**
* Exports this {@link FENString} object to a FEN string.
- *
+ *
* @return a FEN string representing the board
*/
@Override
public String toString() {
- return String.format("%s %c %s %s %d %d",
- piecePlacement,
- activeColor.firstChar(),
- castlingAvailability,
- enPassantTargetSquare == null ? "-" : enPassantTargetSquare.toLAN(),
- halfmoveClock,
- fullmoveNumber);
+ return String.format(
+ "%s %c %s %s %d %d",
+ piecePlacement,
+ activeColor.firstChar(),
+ castlingAvailability,
+ enPassantTargetSquare == null ? "-" : enPassantTargetSquare.toLAN(),
+ halfmoveClock,
+ fullmoveNumber
+ );
}
/**
diff --git a/src/dev/kske/chess/board/King.java b/src/dev/kske/chess/board/King.java
index 6807bb3..8356ef3 100644
--- a/src/dev/kske/chess/board/King.java
+++ b/src/dev/kske/chess/board/King.java
@@ -7,66 +7,100 @@ 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 {
- public King(Color color, Board board) { super(color, board); }
+ /**
+ * 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
public boolean isValidMove(Move move) {
- return (move.getxDist() == 2 && move.getyDist() == 0
- && (move.getDest().x == 6 && canCastleKingside() || move.getDest().x == 2 && canCastleQueenside()))
- || move.getxDist() <= 1 && move.getyDist() <= 1 && checkDestination(move);
+ return move.getxDist() == 2 && move.getyDist() == 0
+ && (move.getDest().x == 6 && canCastleKingside()
+ || move.getDest().x == 2 && canCastleQueenside())
+ || move.getxDist() <= 1 && move.getyDist() <= 1
+ && checkDestination(move);
}
@Override
protected List getPseudolegalMoves(Position pos) {
List moves = new ArrayList<>();
for (int i = Math.max(0, pos.x - 1); i < Math.min(8, pos.x + 2); i++)
- for (int j = Math.max(0, pos.y - 1); j < Math.min(8, pos.y + 2); j++)
+ for (
+ int j = Math.max(0, pos.y - 1);
+ j < Math.min(8, pos.y + 2);
+ j++
+ )
if (i != pos.x || j != pos.y) {
Move move = new Move(pos, new Position(i, j));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) moves.add(move);
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ )
+ moves.add(move);
}
// Castling
- if (canCastleKingside()) moves.add(new Castling(pos, new Position(6, pos.y)));
- if (canCastleQueenside()) moves.add(new Castling(pos, new Position(2, pos.y)));
+ if (canCastleKingside())
+ moves.add(new Castling(pos, new Position(6, pos.y)));
+ if (canCastleQueenside())
+ moves.add(new Castling(pos, new Position(2, pos.y)));
return moves;
}
private boolean canCastleKingside() {
- if (board.getLog().getCastlingRights()[getColor() == Color.WHITE ? MoveNode.WHITE_KINGSIDE : MoveNode.BLACK_KINGSIDE]) {
- int y = getColor() == Color.WHITE ? 7 : 0;
- Position kingPos = new Position(4, y);
- Position jumpPos = new Position(5, y);
- Position kingDest = new Position(6, y);
- Position rookPos = new Position(7, y);
+ if (
+ board.getLog().getCastlingRights()[getColor() == Color.WHITE
+ ? MoveNode.WHITE_KINGSIDE
+ : MoveNode.BLACK_KINGSIDE]
+ ) {
+ int y = getColor() == Color.WHITE ? 7 : 0;
+ Position kingPos = new Position(4, y);
+ Position jumpPos = new Position(5, y);
+ 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() {
- if (board.getLog().getCastlingRights()[getColor() == Color.WHITE ? MoveNode.WHITE_QUEENSIDE : MoveNode.BLACK_QUEENSIDE]) {
- int y = getColor() == Color.WHITE ? 7 : 0;
- Position kingPos = new Position(4, y);
- Position jumpPos = new Position(3, y);
- Position freeDest = new Position(1, y);
- Position rookPos = new Position(0, y);
+ if (
+ board.getLog().getCastlingRights()[getColor() == Color.WHITE
+ ? MoveNode.WHITE_QUEENSIDE
+ : MoveNode.BLACK_QUEENSIDE]
+ ) {
+ int y = getColor() == Color.WHITE ? 7 : 0;
+ Position kingPos = new Position(4, y);
+ Position jumpPos = new Position(3, y);
+ 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) {
+ private boolean canCastle(
+ Position kingPos, Position freeDest, Position rookPos, Position jumpPos
+ ) {
Piece rook = board.get(rookPos);
- return rook != null && rook instanceof Rook && isFreePath(new Move(kingPos, freeDest)) && !board.isAttacked(kingPos, getColor().opposite())
- && !board.isAttacked(jumpPos, getColor().opposite());
+ return rook != null && rook instanceof Rook && isFreePath(
+ new Move(kingPos, freeDest)
+ ) && !board.isAttacked(kingPos, getColor().opposite())
+ && !board.isAttacked(jumpPos, getColor().opposite());
}
@Override
public int getValue() { return 0; }
-}
\ No newline at end of file
+}
diff --git a/src/dev/kske/chess/board/Knight.java b/src/dev/kske/chess/board/Knight.java
index 9e54fd8..c3fe96e 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);
}
@@ -20,13 +26,22 @@ public class Knight extends Piece {
@Override
public boolean isValidMove(Move move) {
return Math.abs(move.getxDist() - move.getyDist()) == 1
- && (move.getxDist() == 1 && move.getyDist() == 2 || move.getxDist() == 2 && move.getyDist() == 1) && checkDestination(move);
+ && (move.getxDist() == 1 && move.getyDist() == 2
+ || move.getxDist() == 2 && move.getyDist() == 1)
+ && checkDestination(move);
}
- private void checkAndInsertMove(List moves, Position pos, int offsetX, int offsetY) {
- if (pos.x + offsetX >= 0 && pos.x + offsetX < 8 && pos.y + offsetY >= 0 && pos.y + offsetY < 8) {
- Move move = new Move(pos, new Position(pos.x + offsetX, pos.y + offsetY));
- if (checkDestination(move)) moves.add(move);
+ private void checkAndInsertMove(
+ List moves, Position pos, int offsetX, int offsetY
+ ) {
+ if (
+ pos.x + offsetX >= 0 && pos.x + offsetX < 8 && pos.y + offsetY >= 0
+ && pos.y + offsetY < 8
+ ) {
+ Move move
+ = new Move(pos, new Position(pos.x + offsetX, pos.y + offsetY));
+ if (checkDestination(move))
+ moves.add(move);
}
}
@@ -48,5 +63,7 @@ public class Knight extends Piece {
public int getValue() { return 35; }
@Override
- public char firstChar() { return 'n'; }
+ public char firstChar() {
+ return 'n';
+ }
}
diff --git a/src/dev/kske/chess/board/Log.java b/src/dev/kske/chess/board/Log.java
index ba4c509..2def86c 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
@@ -18,28 +21,35 @@ public class Log implements Iterable {
private MoveNode root, current;
- private Color activeColor;
- private boolean[] castlingRights;
- private Position enPassant;
- private int fullmoveNumber, halfmoveClock;
-
- public Log() { reset(); }
+ private Color activeColor;
+ private boolean[] castlingRights;
+ private Position enPassant;
+ private int fullmoveNumber, halfmoveClock;
/**
- * Creates a (partially deep) copy of another {@link Log} instance which begins
+ * Creates an instance of {@link Log} in the default state.
+ */
+ public Log() {
+ reset();
+ }
+
+ /**
+ * Creates a (partially deep) copy of another {@link Log} instance which
+ * begins
* with the current {@link MoveNode}.
*
* @param other The {@link Log} instance to copy
- * @param copyVariations If set to {@code true}, subsequent variations of the
+ * @param copyVariations If set to {@code true}, subsequent variations of
+ * the
* current {@link MoveNode} are copied with the
* {@link Log}
*/
public Log(Log other, boolean copyVariations) {
- enPassant = other.enPassant;
- castlingRights = other.castlingRights.clone();
- activeColor = other.activeColor;
- fullmoveNumber = other.fullmoveNumber;
- halfmoveClock = other.halfmoveClock;
+ enPassant = other.enPassant;
+ castlingRights = other.castlingRights.clone();
+ activeColor = other.activeColor;
+ fullmoveNumber = other.fullmoveNumber;
+ halfmoveClock = other.halfmoveClock;
// The new root is the current node of the copied instance
if (!other.isEmpty()) {
@@ -58,17 +68,21 @@ public class Log implements Iterable {
public Iterator iterator() {
return new Iterator() {
- private MoveNode current = root;
- private boolean hasNext = !isEmpty();
+ private MoveNode current = root;
+ private boolean hasNext = !isEmpty();
@Override
- public boolean hasNext() { return hasNext; }
+ public boolean hasNext() {
+ return hasNext;
+ }
@Override
public MoveNode next() {
MoveNode result = current;
- if (current.hasVariations()) current = current.getVariations().get(0);
- else hasNext = false;
+ if (current.hasVariations())
+ current = current.getVariations().get(0);
+ else
+ hasNext = false;
return result;
}
};
@@ -82,20 +96,34 @@ public class Log implements Iterable {
* @param capturedPiece The piece captured with the move
*/
public void add(Move move, Piece piece, Piece capturedPiece) {
- enPassant = piece instanceof Pawn && move.getyDist() == 2 ? new Position(move.getPos().x, move.getPos().y + move.getySign()) : null;
- if (activeColor == Color.BLACK) ++fullmoveNumber;
- if (piece instanceof Pawn || capturedPiece != null) halfmoveClock = 0;
- else++halfmoveClock;
+ enPassant = piece instanceof Pawn && move.getyDist() == 2
+ ? new Position(move.getPos().x, move.getPos().y + move.getySign())
+ : null;
+ if (activeColor == Color.BLACK)
+ ++fullmoveNumber;
+ if (piece instanceof Pawn || capturedPiece != null)
+ halfmoveClock = 0;
+ else
+ ++halfmoveClock;
activeColor = activeColor.opposite();
// Disable castling rights if a king or a rook has been moved
- if (piece instanceof King || piece instanceof Rook) disableCastlingRights(piece, move.getPos());
+ if (piece instanceof King || piece instanceof Rook)
+ disableCastlingRights(piece, move.getPos());
- final MoveNode leaf = new MoveNode(move, capturedPiece, castlingRights.clone(), enPassant, activeColor, fullmoveNumber, halfmoveClock);
+ final MoveNode leaf = new MoveNode(
+ move,
+ capturedPiece,
+ castlingRights.clone(),
+ enPassant,
+ activeColor,
+ fullmoveNumber,
+ halfmoveClock
+ );
if (isEmpty()) {
- root = leaf;
- current = leaf;
+ root = leaf;
+ current = leaf;
} else {
current.addVariation(leaf);
current = leaf;
@@ -111,7 +139,8 @@ public class Log implements Iterable {
current.getParent().getVariations().remove(current);
current = current.getParent();
update();
- } else reset();
+ } else
+ reset();
}
/**
@@ -122,20 +151,24 @@ public class Log implements Iterable {
/**
* @return {@code true} if the current node has a parent node
*/
- public boolean hasParent() { return !isEmpty() && current.hasParent(); }
+ public boolean hasParent() {
+ return !isEmpty() && current.hasParent();
+ }
/**
* Reverts the log to its initial state corresponding to the default board
* position.
*/
public void reset() {
- root = null;
- current = null;
- castlingRights = new boolean[] { true, true, true, true };
- enPassant = null;
- activeColor = Color.WHITE;
- fullmoveNumber = 1;
- halfmoveClock = 0;
+ root = null;
+ current = null;
+ castlingRights = new boolean[] {
+ true, true, true, true
+ };
+ enPassant = null;
+ activeColor = Color.WHITE;
+ fullmoveNumber = 1;
+ halfmoveClock = 0;
}
/**
@@ -144,7 +177,10 @@ public class Log implements Iterable {
* @param index the index of the variation to select
*/
public void selectNextNode(int index) {
- if (!isEmpty() && current.hasVariations() && index < current.getVariations().size()) {
+ if (
+ !isEmpty() && current.hasVariations()
+ && index < current.getVariations().size()
+ ) {
current = current.getVariations().get(index);
update();
}
@@ -171,53 +207,78 @@ public class Log implements Iterable {
}
/**
- * Sets the active color, castling rights, en passant target square, fullmove
+ * Sets the active color, castling rights, en passant target square,
+ * fullmove
* number and halfmove clock to those of the current {@link MoveNode}.
*/
private void update() {
- activeColor = current.activeColor;
- castlingRights = current.castlingRights.clone();
- enPassant = current.enPassant;
- fullmoveNumber = current.fullmoveCounter;
- halfmoveClock = current.halfmoveClock;
+ activeColor = current.activeColor;
+ castlingRights = current.castlingRights.clone();
+ enPassant = current.enPassant;
+ fullmoveNumber = current.fullmoveCounter;
+ halfmoveClock = current.halfmoveClock;
}
/**
- * Removed the castling rights bound to a rook or king for the rest of the game.
+ * Removed the castling rights bound to a rook or king for the rest of the
+ * game.
* This method should be called once the piece has been moved, as a castling
* move involving this piece is forbidden afterwards.
*
- * @param piece the rook or king to disable the castling rights for
- * @param initialPosition the initial position of the piece during the start of
+ * @param piece the rook or king to disable the castling rights
+ * for
+ * @param initialPosition the initial position of the piece during the start
+ * of
* the game
*/
private void disableCastlingRights(Piece piece, Position initialPosition) {
// Kingside
- if (piece instanceof King || piece instanceof Rook && initialPosition.x == 7)
- castlingRights[piece.getColor() == Color.WHITE ? MoveNode.WHITE_KINGSIDE : MoveNode.BLACK_KINGSIDE] = false;
+ if (
+ piece instanceof King
+ || piece instanceof Rook && initialPosition.x == 7
+ )
+ castlingRights[piece.getColor() == Color.WHITE
+ ? MoveNode.WHITE_KINGSIDE
+ : MoveNode.BLACK_KINGSIDE] = false;
// Queenside
- if (piece instanceof King || piece instanceof Rook && initialPosition.x == 0)
- castlingRights[piece.getColor() == Color.WHITE ? MoveNode.WHITE_QUEENSIDE : MoveNode.BLACK_QUEENSIDE] = false;
+ if (
+ piece instanceof King
+ || piece instanceof Rook && initialPosition.x == 0
+ )
+ castlingRights[piece.getColor() == Color.WHITE
+ ? MoveNode.WHITE_QUEENSIDE
+ : MoveNode.BLACK_QUEENSIDE] = false;
}
@Override
public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + Arrays.hashCode(castlingRights);
- result = prime * result + Objects.hash(activeColor, current, enPassant, fullmoveNumber, halfmoveClock);
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(castlingRights);
+ result = prime * result + Objects.hash(
+ activeColor,
+ current,
+ enPassant,
+ fullmoveNumber,
+ halfmoveClock
+ );
return result;
}
@Override
public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
Log other = (Log) obj;
- return activeColor == other.activeColor && Arrays.equals(castlingRights, other.castlingRights) && Objects.equals(current, other.current)
- && Objects.equals(enPassant, other.enPassant) && fullmoveNumber == other.fullmoveNumber && halfmoveClock == other.halfmoveClock;
+ return activeColor == other.activeColor && Arrays
+ .equals(castlingRights, other.castlingRights)
+ && Objects.equals(current, other.current)
+ && Objects.equals(enPassant, other.enPassant) && fullmoveNumber == other.fullmoveNumber && halfmoveClock == other.halfmoveClock;
}
/**
@@ -230,23 +291,75 @@ public class Log implements Iterable {
*/
public MoveNode getLast() { return current; }
+ /**
+ * @return the castling rights present during the current move
+ */
public boolean[] getCastlingRights() { return castlingRights; }
- public void setCastlingRights(boolean[] castlingRights) { this.castlingRights = 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; }
- public void setEnPassant(Position enPassant) { this.enPassant = 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; }
- public void setActiveColor(Color activeColor) { this.activeColor = 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; }
- public void setFullmoveNumber(int fullmoveNumber) { this.fullmoveNumber = 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; }
- public void setHalfmoveClock(int halfmoveClock) { this.halfmoveClock = halfmoveClock; }
-}
\ No newline at end of file
+ /**
+ * 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;
+ }
+}
diff --git a/src/dev/kske/chess/board/Move.java b/src/dev/kske/chess/board/Move.java
index 2ff63e2..e48ef56 100644
--- a/src/dev/kske/chess/board/Move.java
+++ b/src/dev/kske/chess/board/Move.java
@@ -18,46 +18,104 @@ import dev.kske.chess.board.Piece.Color;
*/
public class Move {
- protected final Position pos, dest;
- protected final int xDist, yDist, xSign, ySign;
+ 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;
- xDist = Math.abs(dest.x - pos.x);
- yDist = Math.abs(dest.y - pos.y);
- xSign = (int) Math.signum(dest.x - pos.x);
- ySign = (int) Math.signum(dest.y - pos.y);
+ this.pos = pos;
+ this.dest = dest;
+ xDist = Math.abs(dest.x - pos.x);
+ yDist = Math.abs(dest.y - pos.y);
+ xSign = (int) Math.signum(dest.x - pos.x);
+ ySign = (int) Math.signum(dest.y - pos.y);
}
- public Move(int xPos, int yPos, int xDest, int yDest) { this(new Position(xPos, yPos), new Position(xDest, yDest)); }
+ /**
+ * 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
+ // 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
+ // Move the piece to the move's position square and clean the
+ // destination
board.set(pos, board.get(dest));
board.set(dest, capturedPiece);
}
+ /**
+ * @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));
- if (move.length() == 5) {
+ Position pos = Position.fromLAN(move.substring(0, 2));
+ Position dest = Position.fromLAN(move.substring(2));
+ if (move.length() == 5)
try {
- return new PawnPromotion(pos, dest, Piece.fromFirstChar(move.charAt(4)));
+ return new PawnPromotion(
+ pos,
+ dest,
+ Piece.fromFirstChar(move.charAt(4))
+ );
} catch (Exception e) {
e.printStackTrace();
return null;
}
- } else return new Move(pos, dest);
+ return new Move(pos, dest);
}
- public String toLAN() { return getPos().toLAN() + getDest().toLAN(); }
+ /**
+ * Generates a string representation of this move in Long Algebraic Notation
+ * (LAN).
+ *
+ * @return the LAN string
+ */
+ public String toLAN() {
+ return getPos().toLAN() + getDest().toLAN();
+ }
/**
* Converts a move string from standard algebraic notation to a {@link Move}
@@ -69,75 +127,125 @@ public class Move {
*/
public static Move fromSAN(String sanMove, Board board) {
Map patterns = new HashMap<>();
- patterns.put("pieceMove",
- Pattern.compile(
- "^(?[NBRQK])(?:(?[a-h])|(?[1-8])|(?[a-h][1-8]))?x?(?[a-h][1-8])(?:\\+{0,2}|\\#)$"));
- patterns.put("pawnCapture",
- Pattern.compile("^(?[a-h])(?[1-8])?x(?[a-h][1-8])(?[NBRQ])?(?:\\+{0,2}|\\#)?$"));
- patterns.put("pawnPush", Pattern.compile("^(?[a-h][1-8])(?[NBRQ])?(?:\\+{0,2}|\\#)$"));
- patterns.put("castling", Pattern.compile("^(?O-O-O)|(?O-O)(?:\\+{0,2}|\\#)?$"));
+ patterns.put(
+ "pieceMove",
+ Pattern.compile(
+ "^(?[NBRQK])(?:(?[a-h])|(?[1-8])|(?[a-h][1-8]))?x?(?[a-h][1-8])(?:\\+{0,2}|\\#)$"
+ )
+ );
+ patterns.put(
+ "pawnCapture",
+ Pattern.compile(
+ "^(?[a-h])(?[1-8])?x(?[a-h][1-8])(?[NBRQ])?(?:\\+{0,2}|\\#)?$"
+ )
+ );
+ patterns.put(
+ "pawnPush",
+ Pattern.compile(
+ "^(?[a-h][1-8])(?[NBRQ])?(?:\\+{0,2}|\\#)$"
+ )
+ );
+ patterns.put(
+ "castling",
+ Pattern.compile(
+ "^(?O-O-O)|(?O-O)(?:\\+{0,2}|\\#)?$"
+ )
+ );
for (Map.Entry entry : patterns.entrySet()) {
Matcher m = entry.getValue().matcher(sanMove);
if (m.find()) {
- Position pos = null, dest = null;
- Move move = null;
+ Position pos = null, dest = null;
+ Move move = null;
switch (entry.getKey()) {
case "pieceMove":
dest = Position.fromLAN(m.group("toSquare"));
- if (m.group("fromSquare") != null) pos = Position.fromLAN(m.group("fromSquare"));
+ if (m.group("fromSquare") != null)
+ pos = Position.fromLAN(m.group("fromSquare"));
else {
- Class extends Piece> pieceClass = Piece.fromFirstChar(m.group("pieceType").charAt(0));
- char file;
- int rank;
+ Class extends Piece> pieceClass = Piece
+ .fromFirstChar(m.group("pieceType").charAt(0));
+ char file;
+ int rank;
if (m.group("fromFile") != null) {
- file = m.group("fromFile").charAt(0);
- rank = board.get(pieceClass, file);
- pos = Position.fromLAN(String.format("%c%d", file, rank));
- } else if (m.group("fromRank") != null) {
- rank = Integer.parseInt(m.group("fromRank").substring(0, 1));
- file = board.get(pieceClass, rank);
- pos = Position.fromLAN(String.format("%c%d", file, rank));
- } else pos = board.get(pieceClass, dest);
+ file = m.group("fromFile").charAt(0);
+ rank = board.get(pieceClass, file);
+ pos = Position
+ .fromLAN(String.format("%c%d", file, rank));
+ } else
+ if (m.group("fromRank") != null) {
+ rank = Integer.parseInt(
+ m.group("fromRank").substring(0, 1)
+ );
+ file = board.get(pieceClass, rank);
+ pos = Position.fromLAN(
+ String.format("%c%d", file, rank)
+ );
+ } else
+ pos = board.get(pieceClass, dest);
}
move = new Move(pos, dest);
break;
case "pawnCapture":
char file = m.group("fromFile").charAt(0);
- int rank = m.group("fromRank") == null ? board.get(Pawn.class, file) : Integer.parseInt(m.group("fromRank"));
+ int rank = m.group("fromRank") == null
+ ? board.get(Pawn.class, file)
+ : Integer.parseInt(m.group("fromRank"));
dest = Position.fromLAN(m.group("toSquare"));
- pos = Position.fromLAN(String.format("%c%d", file, rank));
+ pos = Position
+ .fromLAN(String.format("%c%d", file, rank));
- if (m.group("promotedTo") != null) {
+ if (m.group("promotedTo") != null)
try {
- move = new PawnPromotion(pos, dest, Piece.fromFirstChar(m.group("promotedTo").charAt(0)));
+ move = new PawnPromotion(
+ pos,
+ dest,
+ Piece.fromFirstChar(m.group("promotedTo").charAt(0))
+ );
} catch (Exception e) {
e.printStackTrace();
}
- } else move = new Move(pos, dest);
+ else
+ move = new Move(pos, dest);
break;
case "pawnPush":
dest = Position.fromLAN(m.group("toSquare"));
- int step = board.getLog().getActiveColor() == Color.WHITE ? 1 : -1;
+ int step
+ = board.getLog().getActiveColor() == Color.WHITE ? 1
+ : -1;
// One step forward
- if (board.getBoardArr()[dest.x][dest.y + step] != null) pos = new Position(dest.x, dest.y + step);
+ if (board.getBoardArr()[dest.x][dest.y + step] != null)
+ pos = new Position(dest.x, dest.y + step);
// Double step forward
- else pos = new Position(dest.x, dest.y + 2 * step);
+ else
+ pos = new Position(dest.x, dest.y + 2 * step);
- if (m.group("promotedTo") != null) {
+ if (m.group("promotedTo") != null)
try {
- move = new PawnPromotion(pos, dest, Piece.fromFirstChar(m.group("promotedTo").charAt(0)));
+ move = new PawnPromotion(
+ pos,
+ dest,
+ Piece.fromFirstChar(m.group("promotedTo").charAt(0))
+ );
} catch (Exception e) {
e.printStackTrace();
}
- } else move = new Move(pos, dest);
+ else
+ move = new Move(pos, dest);
break;
case "castling":
- pos = new Position(4, board.getLog().getActiveColor() == Color.WHITE ? 7 : 0);
- dest = new Position(m.group("kingside") != null ? 6 : 2, pos.y);
+ pos = new Position(
+ 4,
+ board.getLog().getActiveColor() == Color.WHITE ? 7
+ : 0
+ );
+ dest = new Position(
+ m.group("kingside") != null ? 6 : 2,
+ pos.y
+ );
move = new Castling(pos, dest);
break;
}
@@ -147,21 +255,31 @@ 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);
+ final Piece piece = board.get(pos);
+ StringBuilder sb = new StringBuilder(8);
// Piece symbol
- if(!(piece instanceof Pawn))
+ if (!(piece instanceof Pawn))
sb.append(Character.toUpperCase(piece.firstChar()));
// Position
// TODO: Deconstruct position into optional file or rank
// Omit position if the move is a pawn push
- if (!(piece instanceof Pawn && xDist == 0)) sb.append(pos.toLAN());
+ if (!(piece instanceof Pawn && xDist == 0))
+ sb.append(pos.toLAN());
// Capture indicator
- if (board.get(dest) != null) sb.append('x');
+ if (board.get(dest) != null)
+ sb.append('x');
// Destination
sb.append(dest.toLAN());
@@ -169,37 +287,88 @@ 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
- public String toString() { return toLAN(); }
+ public String toString() {
+ return toLAN();
+ }
@Override
- public int hashCode() { return Objects.hash(getDest(), getPos(), getxDist(), getxSign(), getyDist(), getySign()); }
+ public int hashCode() {
+ return Objects.hash(
+ getDest(),
+ getPos(),
+ getxDist(),
+ getxSign(),
+ getyDist(),
+ getySign()
+ );
+ }
@Override
public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
Move other = (Move) obj;
- return Objects.equals(getDest(), other.getDest()) && Objects.equals(getPos(), other.getPos()) && getxDist() == other.getxDist()
- && getxSign() == other.getxSign() && getyDist() == other.getyDist() && getySign() == other.getySign();
+ return Objects.equals(getDest(), other.getDest()) && Objects
+ .equals(getPos(), other.getPos()) && getxDist() == other.getxDist()
+ && 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; }
- public int getxDist() { return xDist; }
+ /**
+ * @return the x distance
+ */
+ public int getxDist() {
+ return xDist;
+ }
- public int getyDist() { return yDist; }
+ /**
+ * @return the y distance
+ */
+ public int getyDist() {
+ return yDist;
+ }
- public int getxSign() { return xSign; }
+ /**
+ * @return the sign of the x distance
+ */
+ public int getxSign() {
+ return xSign;
+ }
- public int getySign() { return ySign; }
+ /**
+ * @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..51b8f32 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,38 +14,94 @@ 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;
- private MoveNode parent;
- private List variations;
+ /**
+ * 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;
/**
* 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) {
- this.move = move;
- this.capturedPiece = capturedPiece;
- this.castlingRights = castlingRights;
- this.enPassant = enPassant;
- this.activeColor = activeColor;
- this.fullmoveCounter = fullmoveCounter;
- this.halfmoveClock = 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;
+ this.enPassant = enPassant;
+ this.activeColor = activeColor;
+ this.fullmoveCounter = fullmoveCounter;
+ this.halfmoveClock = halfmoveClock;
}
/**
@@ -60,10 +113,18 @@ 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<>();
+ if (variations == null)
+ variations = new ArrayList<>();
for (MoveNode variation : other.variations) {
MoveNode copy = new MoveNode(variation, true);
copy.parent = this;
@@ -78,7 +139,8 @@ public class MoveNode {
* @param variation The {@link MoveNode} to append to this {@link MoveNode}
*/
public void addVariation(MoveNode variation) {
- if (variations == null) variations = new ArrayList<>();
+ if (variations == null)
+ variations = new ArrayList<>();
if (!variations.contains(variation)) {
variations.add(variation);
variation.parent = this;
@@ -90,37 +152,76 @@ public class MoveNode {
*/
public List getVariations() { return variations; }
+ /**
+ * @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; }
+ /**
+ * @return {@code true} if this move node has a parent
+ */
public boolean hasParent() {
return parent != null;
}
+ @Override
+ public String toString() {
+ return String.format(
+ "MoveNode[move=%s,capturedPiece=%s,castlingRights=%s,enPassant=%s,activeColor=%s,fullmoveCounter=%d,halfmoveClock=%d]",
+ move,
+ capturedPiece,
+ Arrays.toString(castlingRights),
+ enPassant,
+ activeColor,
+ fullmoveCounter,
+ halfmoveClock
+ );
+ }
+
@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);
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(castlingRights);
+ result = prime * result + Objects.hash(
+ activeColor,
+ capturedPiece,
+ enPassant,
+ fullmoveCounter,
+ halfmoveClock,
+ move
+ );
return result;
}
@Override
public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
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);
+ 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);
}
-}
\ 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..c5c83e2 100644
--- a/src/dev/kske/chess/board/Pawn.java
+++ b/src/dev/kske/chess/board/Pawn.java
@@ -13,62 +13,107 @@ import java.util.List;
*/
public class Pawn extends Piece {
- public Pawn(Color color, Board board) { super(color, board); }
+ /**
+ * 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
public boolean isValidMove(Move move) {
- boolean step = move.isVertical() && move.getyDist() == 1;
- boolean doubleStep = move.isVertical() && move.getyDist() == 2;
- boolean strafe = move.isDiagonal() && move.getxDist() == 1;
- boolean enPassant = strafe && move.getDest().equals(board.getLog().getEnPassant());
- if (getColor() == Color.WHITE) doubleStep &= move.getPos().y == 6;
- else doubleStep &= move.getPos().y == 1;
+ boolean step = move.isVertical() && move.getyDist() == 1;
+ boolean doubleStep = move.isVertical() && move.getyDist() == 2;
+ boolean strafe = move.isDiagonal() && move.getxDist() == 1;
+ boolean enPassant
+ = strafe && move.getDest().equals(board.getLog().getEnPassant());
+ if (getColor() == Color.WHITE)
+ doubleStep &= move.getPos().y == 6;
+ else
+ doubleStep &= move.getPos().y == 1;
- return enPassant || (step ^ doubleStep ^ strafe) && move.getySign() == (getColor() == Color.WHITE ? -1 : 1) && isFreePath(move);
+ return enPassant || step ^ doubleStep ^ strafe
+ && move.getySign() == (getColor() == Color.WHITE ? -1 : 1)
+ && isFreePath(move);
}
@Override
protected boolean isFreePath(Move move) {
// Two steps forward
if (move.getyDist() == 2)
- return board.getBoardArr()[move.getPos().x][move.getDest().y - move.getySign()] == null && board.getDest(move) == null;
+ return board.getBoardArr()[move.getPos().x][move.getDest().y
+ - move.getySign()] == null && board.getDest(move) == null;
// One step forward
- else if (move.getxDist() == 0) return board.getDest(move) == null;
- // Capture move
- else return board.getDest(move) != null && board.getDest(move).getColor() != getColor();
+ else
+ if (move.getxDist() == 0)
+ return board.getDest(move) == null;
+ // Capture move
+ else
+ return board.getDest(move) != null
+ && board.getDest(move).getColor() != getColor();
}
@Override
protected List getPseudolegalMoves(Position pos) {
- List moves = new ArrayList<>();
- int sign = getColor() == Color.WHITE ? -1 : 1;
- boolean pawnPromotion = sign == 1 && pos.y == 6 || sign == -1 && pos.y == 1;
+ List moves = new ArrayList<>();
+ int sign = getColor() == Color.WHITE ? -1 : 1;
+ boolean pawnPromotion
+ = sign == 1 && pos.y == 6 || sign == -1 && pos.y == 1;
// Strafe left
- if (pos.x > 0) addMoveIfValid(moves, pos, new Position(pos.x - 1, pos.y + sign), pawnPromotion);
+ if (pos.x > 0)
+ addMoveIfValid(
+ moves,
+ pos,
+ new Position(pos.x - 1, pos.y + sign),
+ pawnPromotion
+ );
// Strafe right
- if (pos.x < 7) addMoveIfValid(moves, pos, new Position(pos.x + 1, pos.y + sign), pawnPromotion);
+ if (pos.x < 7)
+ addMoveIfValid(
+ moves,
+ pos,
+ new Position(pos.x + 1, pos.y + sign),
+ pawnPromotion
+ );
// Step forward
- if (sign == 1 && pos.y < 7 || sign == -1 && pos.y > 0) addMoveIfValid(moves, pos, new Position(pos.x, pos.y + sign), pawnPromotion);
+ if (sign == 1 && pos.y < 7 || sign == -1 && pos.y > 0)
+ addMoveIfValid(
+ moves,
+ pos,
+ new Position(pos.x, pos.y + sign),
+ pawnPromotion
+ );
// Double step forward
- if (sign == 1 && pos.y == 1 || sign == -1 && pos.y == 6) addMoveIfValid(moves, pos, new Position(pos.x, pos.y + 2 * sign), pawnPromotion);
+ if (sign == 1 && pos.y == 1 || sign == -1 && pos.y == 6)
+ addMoveIfValid(
+ moves,
+ pos,
+ new Position(pos.x, pos.y + 2 * sign),
+ pawnPromotion
+ );
// Add en passant move if necessary
if (board.getLog().getEnPassant() != null) {
Move move = new EnPassant(pos, board.getLog().getEnPassant());
- if (move.isDiagonal() && move.getxDist() == 1) moves.add(move);
+ if (move.isDiagonal() && move.getxDist() == 1)
+ moves.add(move);
}
-
return moves;
}
- private void addMoveIfValid(List moves, Position pos, Position dest, boolean pawnPromotion) {
+ private void addMoveIfValid(
+ List moves, Position pos, Position dest, boolean pawnPromotion
+ ) {
Move move = new Move(pos, dest);
- if (isFreePath(move)) {
- if (pawnPromotion) {
+ if (isFreePath(move))
+ if (pawnPromotion)
try {
moves.add(new PawnPromotion(pos, dest, Queen.class));
moves.add(new PawnPromotion(pos, dest, Rook.class));
@@ -77,8 +122,8 @@ public class Pawn extends Piece {
} catch (Exception e) {
e.printStackTrace();
}
- } else moves.add(move);
- }
+ else
+ moves.add(move);
}
@Override
diff --git a/src/dev/kske/chess/board/PawnPromotion.java b/src/dev/kske/chess/board/PawnPromotion.java
index 2c78750..0bf1a4c 100644
--- a/src/dev/kske/chess/board/PawnPromotion.java
+++ b/src/dev/kske/chess/board/PawnPromotion.java
@@ -16,32 +16,78 @@ import dev.kske.chess.board.Piece.Color;
*/
public class PawnPromotion extends Move {
- private final Constructor extends Piece> promotionPieceConstructor;
- private final char promotionPieceChar;
+ private final Constructor extends Piece> promotionPieceConstructor;
+ private final char promotionPieceChar;
- public PawnPromotion(Position pos, Position dest, Class extends Piece> promotionPieceClass)
- throws ReflectiveOperationException, RuntimeException {
+ /**
+ * 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 extends Piece> promotionPieceClass
+ )
+ throws ReflectiveOperationException, RuntimeException {
super(pos, dest);
// Cache piece constructor
- promotionPieceConstructor = promotionPieceClass.getDeclaredConstructor(Color.class, Board.class);
+ promotionPieceConstructor = promotionPieceClass
+ .getDeclaredConstructor(Color.class, Board.class);
promotionPieceConstructor.setAccessible(true);
// Get piece char
- promotionPieceChar = (char) promotionPieceClass.getMethod("firstChar").invoke(promotionPieceConstructor.newInstance(null, null));
+ promotionPieceChar = (char) promotionPieceClass.getMethod("firstChar")
+ .invoke(promotionPieceConstructor.newInstance(null, null));
}
- public PawnPromotion(int xPos, int yPos, int xDest, int yDest, Class extends Piece> promotionPiece)
- throws ReflectiveOperationException, RuntimeException {
- this(new Position(xPos, yPos), new Position(xDest, yDest), 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 extends Piece> promotionPieceClass
+ )
+ throws ReflectiveOperationException, RuntimeException {
+ this(
+ new Position(xPos, yPos),
+ new Position(xDest, yDest),
+ promotionPieceClass
+ );
}
@Override
public void execute(Board board) {
try {
- board.set(pos, promotionPieceConstructor.newInstance(board.get(pos).getColor(), board));
+ board.set(
+ pos,
+ promotionPieceConstructor.newInstance(board.get(pos).getColor(), board)
+ );
super.execute(board);
- } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException e) {
+ } catch (
+ InstantiationException
+ | IllegalAccessException
+ | IllegalArgumentException
+ | InvocationTargetException
+ | SecurityException e
+ ) {
e.printStackTrace();
}
}
@@ -53,7 +99,9 @@ public class PawnPromotion extends Move {
}
@Override
- public String toLAN() { return pos.toLAN() + dest.toLAN() + promotionPieceChar; }
+ public String toLAN() {
+ return pos.toLAN() + dest.toLAN() + promotionPieceChar;
+ }
@Override
public String toSAN(Board board) {
@@ -63,18 +111,23 @@ public class PawnPromotion extends Move {
@Override
public int hashCode() {
- final int prime = 31;
- int result = super.hashCode();
- result = prime * result + Objects.hash(promotionPieceChar, promotionPieceConstructor);
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result
+ + Objects.hash(promotionPieceChar, promotionPieceConstructor);
return result;
}
@Override
public boolean equals(Object obj) {
- if (this == obj) return true;
- if (!super.equals(obj)) return false;
- if (!(obj instanceof PawnPromotion)) return false;
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (!(obj instanceof PawnPromotion))
+ return false;
PawnPromotion other = (PawnPromotion) obj;
- return promotionPieceChar == other.promotionPieceChar && Objects.equals(promotionPieceConstructor, other.promotionPieceConstructor);
+ return promotionPieceChar == other.promotionPieceChar && Objects
+ .equals(promotionPieceConstructor, other.promotionPieceConstructor);
}
}
diff --git a/src/dev/kske/chess/board/Piece.java b/src/dev/kske/chess/board/Piece.java
index 848dca5..4873ec7 100644
--- a/src/dev/kske/chess/board/Piece.java
+++ b/src/dev/kske/chess/board/Piece.java
@@ -5,59 +5,97 @@ 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
*/
public abstract class Piece implements Cloneable {
- private final Color color;
- protected Board board;
+ 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;
+ 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();) {
Move move = iterator.next();
board.move(move);
- if (board.checkCheck(getColor())) iterator.remove();
+ if (board.checkCheck(getColor()))
+ iterator.remove();
board.revert();
}
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
+ * 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
- || j != move.getDest().y; i += move.getxSign(), j += move.getySign())
- if (board.getBoardArr()[i][j] != null) return false;
+ for (
+ int i = move.getPos().x + move.getxSign(), j
+ = move.getPos().y + move.getySign();
+ i != move.getDest().x
+ || j != move.getDest().y;
+ i += move.getxSign(), j += move.getySign()
+ )
+ if (board.getBoardArr()[i][j] != null)
+ return false;
return checkDestination(move);
}
/**
* 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
*/
- protected final boolean checkDestination(Move move) { return board.getDest(move) == null || board.getDest(move).getColor() != getColor(); }
+ protected final boolean checkDestination(Move move) {
+ return board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor();
+ }
@Override
public Object clone() {
@@ -71,32 +109,49 @@ public abstract class Piece implements Cloneable {
}
@Override
- public String toString() { return getClass().getSimpleName(); }
+ public String toString() {
+ return String.format("%s[color=%s]", getClass().getSimpleName(), color);
+ }
@Override
- public int hashCode() { return Objects.hash(color); }
+ public int hashCode() {
+ return Objects.hash(color);
+ }
@Override
public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
Piece other = (Piece) obj;
return color == other.color;
}
/**
- * @return the standard value of this {@link Piece} that can be used for board
+ * @return the standard value of this {@link Piece} that can be used for
+ * board
* evaluation
*/
public abstract int getValue();
/**
- * @return The first character of this {@link Piece} in algebraic notation and
+ * @return The first character of this {@link Piece} in algebraic notation
+ * and
* lower case
*/
- public char firstChar() { return Character.toLowerCase(toString().charAt(0)); }
+ public char firstChar() {
+ return Character.toLowerCase(getClass().getSimpleName().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 extends Piece> fromFirstChar(char firstChar) {
switch (Character.toLowerCase(firstChar)) {
case 'k':
@@ -121,14 +176,48 @@ 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,
- public static Color fromFirstChar(char c) { return Character.toLowerCase(c) == 'w' ? WHITE : BLACK; }
+ /**
+ * Represents the color of the black pieces on a board.
+ */
+ BLACK;
- public char firstChar() { return this == WHITE ? 'w' : 'b'; }
+ /**
+ * @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;
+ }
- public Color opposite() { return this == WHITE ? BLACK : WHITE; }
+ /**
+ * @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..ecd74cf 100644
--- a/src/dev/kske/chess/board/Position.java
+++ b/src/dev/kske/chess/board/Position.java
@@ -4,23 +4,51 @@ 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;
+ this.x = x;
+ this.y = y;
}
+ /**
+ * 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)));
+ return new Position(
+ pos.charAt(0) - 97,
+ 8 - Character.getNumericValue(pos.charAt(1))
+ );
}
+ /**
+ * 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);
}
@@ -32,21 +60,26 @@ public class Position {
@Override
public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + x;
- result = prime * result + y;
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + x;
+ result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
Position other = (Position) obj;
- if (x != other.x) return false;
- if (y != other.y) return false;
+ if (x != other.x)
+ return false;
+ if (y != other.y)
+ return false;
return true;
}
}
diff --git a/src/dev/kske/chess/board/Queen.java b/src/dev/kske/chess/board/Queen.java
index b2482ce..42b85c4 100644
--- a/src/dev/kske/chess/board/Queen.java
+++ b/src/dev/kske/chess/board/Queen.java
@@ -7,19 +7,26 @@ 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);
}
@Override
public boolean isValidMove(Move move) {
- return ((move.isHorizontal() || move.isVertical()) || move.isDiagonal()) && isFreePath(move);
+ return (move.isHorizontal() || move.isVertical() || move.isDiagonal())
+ && isFreePath(move);
}
@Override
@@ -29,73 +36,106 @@ public class Queen extends Piece {
// Horizontal moves to the right
for (int i = pos.x + 1; i < 8; i++) {
Move move = new Move(pos, new Position(i, pos.y));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
-
// Horizontal moves to the left
for (int i = pos.x - 1; i >= 0; i--) {
Move move = new Move(pos, new Position(i, pos.y));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
-
// Vertical moves to the top
for (int i = pos.y - 1; i >= 0; i--) {
Move move = new Move(pos, new Position(pos.x, i));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
-
// Vertical moves to the bottom
for (int i = pos.y + 1; i < 8; i++) {
Move move = new Move(pos, new Position(pos.x, i));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
-
// Diagonal moves to the lower right
for (int i = pos.x + 1, j = pos.y + 1; i < 8 && j < 8; i++, j++) {
Move move = new Move(pos, new Position(i, j));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
-
// Diagonal moves to the lower left
for (int i = pos.x - 1, j = pos.y + 1; i >= 0 && j < 8; i--, j++) {
Move move = new Move(pos, new Position(i, j));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
-
// Diagonal moves to the upper right
for (int i = pos.x + 1, j = pos.y - 1; i < 8 && j >= 0; i++, j--) {
Move move = new Move(pos, new Position(i, j));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
-
// Diagonal moves to the upper left
for (int i = pos.x - 1, j = pos.y - 1; i >= 0 && j >= 0; i--, j--) {
Move move = new Move(pos, new Position(i, j));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
return moves;
}
diff --git a/src/dev/kske/chess/board/Rook.java b/src/dev/kske/chess/board/Rook.java
index b2efd5c..c14512e 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);
}
@@ -29,37 +35,54 @@ public class Rook extends Piece {
// Horizontal moves to the right
for (int i = pos.x + 1; i < 8; i++) {
Move move = new Move(pos, new Position(i, pos.y));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
-
// Horizontal moves to the left
for (int i = pos.x - 1; i >= 0; i--) {
Move move = new Move(pos, new Position(i, pos.y));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
-
// Vertical moves to the top
for (int i = pos.y - 1; i >= 0; i--) {
Move move = new Move(pos, new Position(pos.x, i));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
-
// Vertical moves to the bottom
for (int i = pos.y + 1; i < 8; i++) {
Move move = new Move(pos, new Position(pos.x, i));
- if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) {
+ if (
+ board.getDest(move) == null
+ || board.getDest(move).getColor() != getColor()
+ ) {
moves.add(move);
- if (board.getDest(move) != null) break;
- } else break;
+ if (board.getDest(move) != null)
+ break;
+ } else
+ break;
}
return moves;
}
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..8ae27ae 100644
--- a/src/dev/kske/chess/event/EventBus.java
+++ b/src/dev/kske/chess/event/EventBus.java
@@ -4,21 +4,27 @@ 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
- *
+ *
* @since Chess v0.4-alpha
* @author Kai S. K. Engelbart
*/
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();
+ if (instance == null)
+ instance = new EventBus();
return instance;
}
@@ -26,13 +32,28 @@ public class EventBus {
subscribers = new ArrayList<>();
}
- public void register(Subscribable 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));
+ 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..f2dbd47 100644
--- a/src/dev/kske/chess/event/GameStartEvent.java
+++ b/src/dev/kske/chess/event/GameStartEvent.java
@@ -6,7 +6,7 @@ import dev.kske.chess.game.Game;
* Project: Chess
* File: GameStartEvent.java
* Created: 30 Oct 2019
- *
+ *
* @since Chess v0.5-alpha
* @author Kai S. K. Engelbart
*/
@@ -14,7 +14,14 @@ public class GameStartEvent implements Event {
private final Game game;
- public GameStartEvent(Game source) { game = source; }
+ /**
+ * Creates an instance of {@link GameStartEvent}.
+ *
+ * @param source the game started
+ */
+ public GameStartEvent(Game source) {
+ game = source;
+ }
@Override
public Game getData() { return game; }
diff --git a/src/dev/kske/chess/event/MoveEvent.java b/src/dev/kske/chess/event/MoveEvent.java
index a60a785..87a91b5 100644
--- a/src/dev/kske/chess/event/MoveEvent.java
+++ b/src/dev/kske/chess/event/MoveEvent.java
@@ -7,22 +7,31 @@ import dev.kske.chess.board.Move;
* Project: Chess
* File: MoveEvent.java
* Created: 7 Aug 2019
- *
+ *
* @since Chess v0.4-alpha
* @author Kai S. K. Engelbart
*/
public class MoveEvent implements Event {
private final Move move;
- private final BoardState boardState;
+ 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;
+ this.boardState = boardState;
}
@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 65%
rename from src/dev/kske/chess/event/Subscribable.java
rename to src/dev/kske/chess/event/Subscriber.java
index 4de8380..b967996 100644
--- a/src/dev/kske/chess/event/Subscribable.java
+++ b/src/dev/kske/chess/event/Subscriber.java
@@ -3,19 +3,24 @@ 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
- *
+ *
* @since Chess v0.4-alpha
* @author Kai S. K. Engelbart
*/
-public interface Subscribable {
+public interface Subscriber {
/**
* Consumes an event dispatched by an event bus.
- *
- * @param event The event dispatched by the event bus, only of supported type
+ *
+ * @param event The event dispatched by the event bus, only of supported
+ * type
*/
void handle(Event> event);
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 2005cd0..d4bb36c 100644
--- a/src/dev/kske/chess/game/Game.java
+++ b/src/dev/kske/chess/game/Game.java
@@ -1,6 +1,6 @@
package dev.kske.chess.game;
-import java.util.HashMap;
+import java.util.EnumMap;
import java.util.Map;
import javax.swing.JOptionPane;
@@ -23,23 +23,42 @@ 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
*/
public class Game {
- private Map players = new HashMap<>();
- private Board board;
- private OverlayComponent overlayComponent;
- private BoardComponent boardComponent;
+ private Map players = new EnumMap<>(Color.class);
+ private Board board;
+ 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);
}
- public Game(BoardPane boardPane, String whiteName, String blackName, Board board) {
+ /**
+ * 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);
}
@@ -47,34 +66,57 @@ public class Game {
private void init(BoardPane boardPane, String whiteName, String blackName) {
// Initialize / synchronize UI
- overlayComponent = boardPane.getOverlayComponent();
- boardComponent = boardPane.getBoardComponent();
+ overlayComponent = boardPane.getOverlayComponent();
+ boardComponent = boardPane.getBoardComponent();
boardComponent.setBoard(board);
// Initialize players
players.put(Color.WHITE, getPlayer(whiteName, Color.WHITE));
players.put(Color.BLACK, getPlayer(blackName, Color.BLACK));
-
- // Initialize the game variable in each player
- 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":
- return new NaturalPlayer(color, overlayComponent);
+ return new NaturalPlayer(this, color, overlayComponent);
case "AI Player":
- return new AIPlayer(color, 4, -10);
+ return new AIPlayer(this, color, 4, -10);
default:
for (EngineInfo info : EngineUtil.getEngineInfos())
- if (info.name.equals(name)) return new UCIPlayer(color, info.path);
+ if (info.name.equals(name))
+ return new UCIPlayer(this, color, info.path);
System.err.println("Invalid player name: " + name);
return null;
}
}
- public void onMove(Player player, Move move) {
- if (board.getPos(move).getColor() == player.color && board.attemptMove(move)) {
+ /**
+ * 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 synchronized void onMove(Player player, Move move) {
+ if (
+ board.getPos(move).getColor() == player.color
+ && board.attemptMove(move)
+ ) {
// Redraw
boardComponent.repaint();
@@ -83,29 +125,46 @@ 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:
case STALEMATE:
- String result = String.format("%s in %s!%n", player.color.opposite(), boardState);
+ String result = String.format(
+ "%s in %s!%n",
+ player.color.opposite(),
+ boardState
+ );
System.out.print(result);
JOptionPane.showMessageDialog(boardComponent, result);
break;
case CHECK:
- System.out.printf("%s in check!%n", player.color.opposite());
+ System.out
+ .printf("%s in check!%n", player.color.opposite());
default:
players.get(board.getLog().getActiveColor()).requestMove();
}
- } else player.requestMove();
+ } else
+ player.requestMove();
}
- public void start() {
+ /**
+ * Starts the game by requesting a move from the player of the currently
+ * active
+ * color.
+ */
+ public synchronized void start() {
EventBus.getInstance().dispatch(new GameStartEvent(this));
players.get(board.getLog().getActiveColor()).requestMove();
}
- public void reset() {
+ /**
+ * Cancels move calculations, initializes the default position and clears
+ * the
+ * {@link OverlayComponent}.
+ */
+ public synchronized void reset() {
players.values().forEach(Player::cancelMove);
board.initDefaultPositions();
boardComponent.repaint();
@@ -114,19 +173,19 @@ 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() {
+ public synchronized void stop() {
players.values().forEach(Player::disconnect);
}
/**
* Assigns the players their opposite colors.
*/
- public void swapColors() {
+ public synchronized void swapColors() {
players.values().forEach(Player::cancelMove);
- Player white = players.get(Color.WHITE);
- Player black = players.get(Color.BLACK);
+ Player white = players.get(Color.WHITE);
+ Player black = players.get(Color.BLACK);
white.setColor(Color.BLACK);
black.setColor(Color.WHITE);
players.put(Color.WHITE, black);
diff --git a/src/dev/kske/chess/game/NaturalPlayer.java b/src/dev/kske/chess/game/NaturalPlayer.java
index 864c4e5..b9def9d 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
*/
@@ -26,55 +30,83 @@ public class NaturalPlayer extends Player implements MouseListener {
private final OverlayComponent overlayComponent;
- private boolean moveRequested;
- private Piece selectedPiece;
- private List possibleMoves;
+ private boolean moveRequested;
+ private Piece selectedPiece;
+ private List possibleMoves;
- public NaturalPlayer(Color color, OverlayComponent overlayComponent) {
- super(color);
- this.overlayComponent = overlayComponent;
- name = "Player";
- moveRequested = false;
+ /**
+ * Creates an instance of {@link NaturalPlayer}.
+ *
+ * @param game the game in which this player will be used
+ * @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(
+ Game game, Color color, OverlayComponent overlayComponent
+ ) {
+ super(game, color);
+ this.overlayComponent = overlayComponent;
+ name = "Player";
+ moveRequested = false;
overlayComponent.addMouseListener(this);
}
@Override
- public void requestMove() { moveRequested = true; }
+ public void requestMove() {
+ moveRequested = true;
+ }
@Override
- public void cancelMove() { moveRequested = false; }
+ public void cancelMove() {
+ moveRequested = false;
+ }
@Override
- public void disconnect() { overlayComponent.removeMouseListener(this); }
+ public void disconnect() {
+ overlayComponent.removeMouseListener(this);
+ }
@Override
public void mousePressed(MouseEvent evt) {
- if (!moveRequested) return;
+ if (!moveRequested)
+ return;
if (selectedPiece == null) {
// Get selected Piece
- final Position pos = new Position(evt.getPoint().x / overlayComponent.getTileSize(), evt.getPoint().y / overlayComponent.getTileSize());
+ final Position pos = new Position(
+ evt.getPoint().x / overlayComponent.getTileSize(),
+ evt.getPoint().y / overlayComponent.getTileSize()
+ );
selectedPiece = board.get(pos);
// Check if a piece was selected
- if (selectedPiece != null) {
-
+ if (selectedPiece != null)
// Discard selection if the piece has the wrong color
- if (selectedPiece.getColor() == color.opposite()) selectedPiece = null;
+ if (selectedPiece.getColor() == color.opposite())
+ selectedPiece = null;
else {
- // Generate all moves possible with the selected piece and display their
+ // Generate all moves possible with the selected piece and
+ // display their
// destinations
possibleMoves = selectedPiece.getMoves(pos);
- overlayComponent.displayDots(possibleMoves.stream().map(move -> move.getDest()).collect(Collectors.toList()));
+ overlayComponent.displayDots(
+ possibleMoves.stream().map(Move::getDest).collect(Collectors.toList())
+ );
}
- }
} else {
- Position dest = new Position(evt.getPoint().x / overlayComponent.getTileSize(), evt.getPoint().y / overlayComponent.getTileSize());
+ Position dest = new Position(
+ evt.getPoint().x / overlayComponent.getTileSize(),
+ evt.getPoint().y / overlayComponent.getTileSize()
+ );
// Get all moves leading to the specified destination
- List selectedMoves = possibleMoves.stream().filter(m -> m.getDest().equals(dest)).collect(Collectors.toList());
+ List selectedMoves = possibleMoves.stream()
+ .filter(m -> m.getDest().equals(dest))
+ .collect(Collectors.toList());
if (!selectedMoves.isEmpty()) {
Move move;
@@ -82,21 +114,26 @@ 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]));
- JOptionPane.showMessageDialog(overlayComponent, comboBox, "Select a promotion", JOptionPane.QUESTION_MESSAGE);
+ JComboBox comboBox
+ = new JComboBox<>(selectedMoves.toArray(new Move[0]));
+ JOptionPane.showMessageDialog(
+ overlayComponent,
+ comboBox,
+ "Select a promotion",
+ JOptionPane.QUESTION_MESSAGE
+ );
move = selectedMoves.get(comboBox.getSelectedIndex());
- } else move = selectedMoves.get(0);
-
+ } else
+ move = selectedMoves.get(0);
// Tell the game to execute the move
moveRequested = false;
game.onMove(NaturalPlayer.this, move);
}
-
// Discard the selection
overlayComponent.clearDots();
- selectedPiece = null;
- possibleMoves = null;
+ selectedPiece = null;
+ possibleMoves = null;
}
}
diff --git a/src/dev/kske/chess/game/Player.java b/src/dev/kske/chess/game/Player.java
index 0b88f9d..c2035ae 100644
--- a/src/dev/kske/chess/game/Player.java
+++ b/src/dev/kske/chess/game/Player.java
@@ -1,49 +1,87 @@
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
*/
public abstract class Player {
- protected Game game;
- protected Board board;
- protected Color color;
- protected String name;
+ protected final Game game;
+ protected final Board board;
- public Player(Color color) {
+ protected String name;
+ protected Color color;
+
+ /**
+ * Initializes the color of this player.
+ *
+ * @param game the game in which this player will be used
+ * @param color the piece color that this player will control
+ */
+ public Player(Game game, Color color) {
+ this.game = game;
+ board = game.getBoard();
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; }
- 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..4331e3e 100644
--- a/src/dev/kske/chess/game/UCIPlayer.java
+++ b/src/dev/kske/chess/game/UCIPlayer.java
@@ -9,22 +9,33 @@ 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;
- public UCIPlayer(Color color, String enginePath) {
- super(color);
+ /**
+ * Creates an instance of {@link UCIPlayer}.
+ *
+ * @param game the game in which this player will be used
+ * @param color the piece color that this player will control
+ * @param enginePath the path to the engine executable
+ */
+ public UCIPlayer(Game game, Color color, String enginePath) {
+ super(game, color);
try {
handle = new UCIHandle(enginePath);
- handle.setListener(this);
+ handle.registerListener(this);
handle.start();
} catch (IOException ex) {
ex.printStackTrace();
diff --git a/src/dev/kske/chess/game/ai/AIPlayer.java b/src/dev/kske/chess/game/ai/AIPlayer.java
index 9e53326..e737e00 100644
--- a/src/dev/kske/chess/game/ai/AIPlayer.java
+++ b/src/dev/kske/chess/game/ai/AIPlayer.java
@@ -2,90 +2,99 @@ 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;
import dev.kske.chess.board.Board;
import dev.kske.chess.board.Move;
import dev.kske.chess.board.Piece.Color;
+import dev.kske.chess.game.Game;
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
*/
public class AIPlayer extends Player {
- private int availableProcessors;
- private int maxDepth;
- private int alphaBetaThreshold;
+ private int availableProcessors;
+ private int maxDepth;
+ private int alphaBetaThreshold;
- private volatile boolean exitRequested;
- private volatile ExecutorService executor;
+ private volatile boolean exitRequested;
+ private volatile ExecutorService executor;
- public AIPlayer(Color color, int maxDepth, int alphaBetaThreshold) {
- super(color);
- name = "AIPlayer";
- availableProcessors = Runtime.getRuntime().availableProcessors();
- this.maxDepth = maxDepth;
- this.alphaBetaThreshold = alphaBetaThreshold;
- exitRequested = false;
+ /**
+ * Creates an instance of {@link AIPlayer}.
+ *
+ * @param game the game in which this player will be used
+ * @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(
+ Game game, Color color, int maxDepth, int alphaBetaThreshold
+ ) {
+ super(game, color);
+ name = "AIPlayer";
+ availableProcessors = Runtime.getRuntime().availableProcessors();
+ this.maxDepth = maxDepth;
+ this.alphaBetaThreshold = alphaBetaThreshold;
}
@Override
public void requestMove() {
exitRequested = false;
- /*
- * Define some processing threads, split the available moves between them and
- * retrieve the result after their execution.
- */
- new Thread(() -> {
- /*
- * Get a copy of the board and the available moves.
- */
- Board board = new Board(this.board, false);
- List moves = board.getMoves(color);
- /*
- * Define move processors and split the available moves between them.
- */
- int numThreads = Math.min(moves.size(), availableProcessors);
- List processors = new ArrayList<>(numThreads);
- final int step = moves.size() / numThreads;
- int rem = moves.size() % numThreads;
- int beginIndex = 0, endIndex = 0;
+ // Define some processing threads, split the available moves between
+ // them and
+ // retrieve the result after their execution.
+ new Thread(() -> {
+
+ // Get a copy of the board and the available moves.
+ Board board = new Board(this.board, false);
+ List moves = board.getMoves(color);
+
+ // Define move processors and split the available moves between
+ // them.
+ int numThreads = Math.min(moves.size(), availableProcessors);
+ List processors = new ArrayList<>(numThreads);
+ final int step = moves.size() / numThreads;
+ int rem = moves.size() % numThreads;
+ int beginIndex = 0, endIndex = 0;
for (int i = 0; i < numThreads; i++) {
- if (rem-- > 0) ++endIndex;
+ if (rem-- > 0)
+ ++endIndex;
endIndex += step;
- processors.add(new MoveProcessor(new Board(board, false), moves.subList(beginIndex, endIndex), color,
- maxDepth, alphaBetaThreshold));
+ processors
+ .add(new MoveProcessor(new Board(board, false), moves.subList(beginIndex, endIndex), color, maxDepth, alphaBetaThreshold));
beginIndex = endIndex;
}
-
- /*
- * Execute processors, get the best result and pass it back to the Game class
- */
+ // Execute processors, get the best result and pass it back to the
+ // Game class
executor = Executors.newFixedThreadPool(numThreads);
List results = new ArrayList<>(numThreads);
try {
- List> futures = executor.invokeAll(processors);
+ List> futures
+ = executor.invokeAll(processors);
for (Future f : futures)
results.add(f.get());
- executor.shutdown();
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
+ } finally {
+ executor.shutdown();
}
results.sort((r1, r2) -> Integer.compare(r2.score, r1.score));
- if (!exitRequested) SwingUtilities.invokeLater(() -> game.onMove(this, results.get(0).move));
+ if (!exitRequested)
+ SwingUtilities
+ .invokeLater(() -> game.onMove(this, results.get(0).move));
}, "AIPlayer calculation setup").start();
}
diff --git a/src/dev/kske/chess/game/ai/MoveProcessor.java b/src/dev/kske/chess/game/ai/MoveProcessor.java
index 112bc30..ef933d6 100644
--- a/src/dev/kske/chess/game/ai/MoveProcessor.java
+++ b/src/dev/kske/chess/game/ai/MoveProcessor.java
@@ -5,32 +5,26 @@ 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
*/
public class MoveProcessor implements Callable {
- private final Board board;
- private final List rootMoves;
- private final Color color;
- private final int maxDepth;
- private final int alphaBetaThreshold;
+ private final Board board;
+ private final List rootMoves;
+ private final Color color;
+ private final int maxDepth;
+ private final int alphaBetaThreshold;
private Move bestMove;
@@ -38,42 +32,194 @@ public class MoveProcessor implements Callable {
static {
positionScores = new HashMap<>();
- positionScores.put(King.class,
- new int[][] { new int[] { -3, -4, -4, -5, -5, -4, -4, -3 }, new int[] { -3, -4, -4, -5, -4, -4, -4, -3 },
- new int[] { -3, -4, -4, -5, -4, -4, -4, -3 }, new int[] { -3, -4, -4, -5, -4, -4, -4, -3 },
- new int[] { -2, -3, -3, -2, -2, -2, -2, -1 }, new int[] { -1, -2, -2, -2, -2, -2, -2, -1 },
- new int[] { 2, 2, 0, 0, 0, 0, 2, 2 }, new int[] { 2, 3, 1, 0, 0, 1, 3, 2 } });
- positionScores.put(Queen.class,
- new int[][] { new int[] { -2, -1, -1, -1, -1, -1, -1, -2 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 },
- new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { 0, 0, 1, 1, 1, 1, 0, -1 },
- new int[] { -1, 1, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 0, 1, 0, 0, 0, 0, -1 },
- new int[] { -2, -1, -1, -1, -1, -1, -1, -2 } });
- positionScores.put(Rook.class,
- new int[][] { new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 1, 1, 1, 1, 1, 1, 1, 1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 },
- new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 },
- new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { 0, 0, 0, 1, 1, 0, 0, 0 } });
- positionScores.put(Knight.class,
- new int[][] { new int[] { -5, -4, -3, -3, -3, -3, -4, -5 }, new int[] { -4, -2, 0, 0, 0, 0, -2, -4 },
- new int[] { -3, 0, 1, 2, 2, 1, 0, -3 }, new int[] { -3, 1, 2, 2, 2, 2, 1, -3 }, new int[] { -3, 0, 2, 2, 2, 2, 0, -1 },
- new int[] { -3, 1, 1, 2, 2, 1, 1, -3 }, new int[] { -4, -2, 0, 1, 1, 0, -2, -4 },
- new int[] { -5, -4, -3, -3, -3, -3, -4, -5 } });
- positionScores.put(Bishop.class,
- new int[][] { new int[] { -2, -1, -1, -1, -1, -1, -1, 2 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 },
- new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 1, 1, 1, 1, 1, 1, -1 }, new int[] { -1, 0, 1, 1, 1, 1, 0, -1 },
- new int[] { -1, 1, 1, 1, 1, 1, 1, -1 }, new int[] { -1, 1, 0, 0, 0, 0, 1, -1 },
- new int[] { -2, -1, -1, -1, -1, -1, -1, -2 } });
- positionScores.put(Pawn.class,
- new int[][] { new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 5, 5, 5, 5, 5, 5, 5, 5 }, new int[] { 1, 1, 2, 3, 3, 2, 1, 1 },
- new int[] { 0, 0, 1, 3, 3, 1, 0, 0 }, new int[] { 0, 0, 0, 2, 2, 0, 0, 0 }, new int[] { 0, 0, -1, 0, 0, -1, 0, 0 },
- new int[] { 0, 1, 1, -2, -2, 1, 1, 0 }, new int[] { 0, 0, 0, 0, 0, 0, 0, 0 } });
+ positionScores.put(
+ King.class,
+ new int[][] {
+ new int[] {
+ -3, -4, -4, -5, -5, -4, -4, -3
+ }, new int[] {
+ -3, -4, -4, -5, -4, -4, -4, -3
+ },
+ new int[] {
+ -3, -4, -4, -5, -4, -4, -4, -3
+ },
+ new int[] {
+ -3, -4, -4, -5, -4, -4, -4, -3
+ },
+ new int[] {
+ -2, -3, -3, -2, -2, -2, -2, -1
+ },
+ new int[] {
+ -1, -2, -2, -2, -2, -2, -2, -1
+ },
+ new int[] {
+ 2, 2, 0, 0, 0, 0, 2, 2
+ },
+ new int[] {
+ 2, 3, 1, 0, 0, 1, 3, 2
+ }
+ }
+ );
+ positionScores.put(
+ Queen.class,
+ new int[][] {
+ new int[] {
+ -2, -1, -1, -1, -1, -1, -1, -2
+ }, new int[] {
+ -1, 0, 0, 0, 0, 0, 0, -1
+ },
+ new int[] {
+ -1, 0, 1, 1, 1, 1, 0, -1
+ },
+ new int[] {
+ -1, 0, 1, 1, 1, 1, 0, -1
+ },
+ new int[] {
+ 0, 0, 1, 1, 1, 1, 0, -1
+ },
+ new int[] {
+ -1, 1, 1, 1, 1, 1, 0, -1
+ },
+ new int[] {
+ -1, 0, 1, 0, 0, 0, 0, -1
+ },
+ new int[] {
+ -2, -1, -1, -1, -1, -1, -1, -2
+ }
+ }
+ );
+ positionScores.put(
+ Rook.class,
+ new int[][] {
+ new int[] {
+ 0, 0, 0, 0, 0, 0, 0, 0
+ }, new int[] {
+ 1, 1, 1, 1, 1, 1, 1, 1
+ }, new int[] {
+ -1, 0, 0, 0, 0, 0, 0, -1
+ },
+ new int[] {
+ -1, 0, 0, 0, 0, 0, 0, -1
+ },
+ new int[] {
+ -1, 0, 0, 0, 0, 0, 0, -1
+ },
+ new int[] {
+ -1, 0, 0, 0, 0, 0, 0, -1
+ },
+ new int[] {
+ -1, 0, 0, 0, 0, 0, 0, -1
+ },
+ new int[] {
+ 0, 0, 0, 1, 1, 0, 0, 0
+ }
+ }
+ );
+ positionScores.put(
+ Knight.class,
+ new int[][] {
+ new int[] {
+ -5, -4, -3, -3, -3, -3, -4, -5
+ }, new int[] {
+ -4, -2, 0, 0, 0, 0, -2, -4
+ },
+ new int[] {
+ -3, 0, 1, 2, 2, 1, 0, -3
+ },
+ new int[] {
+ -3, 1, 2, 2, 2, 2, 1, -3
+ },
+ new int[] {
+ -3, 0, 2, 2, 2, 2, 0, -1
+ },
+ new int[] {
+ -3, 1, 1, 2, 2, 1, 1, -3
+ },
+ new int[] {
+ -4, -2, 0, 1, 1, 0, -2, -4
+ },
+ new int[] {
+ -5, -4, -3, -3, -3, -3, -4, -5
+ }
+ }
+ );
+ positionScores.put(
+ Bishop.class,
+ new int[][] {
+ new int[] {
+ -2, -1, -1, -1, -1, -1, -1, 2
+ }, new int[] {
+ -1, 0, 0, 0, 0, 0, 0, -1
+ },
+ new int[] {
+ -1, 0, 1, 1, 1, 1, 0, -1
+ },
+ new int[] {
+ -1, 1, 1, 1, 1, 1, 1, -1
+ },
+ new int[] {
+ -1, 0, 1, 1, 1, 1, 0, -1
+ },
+ new int[] {
+ -1, 1, 1, 1, 1, 1, 1, -1
+ },
+ new int[] {
+ -1, 1, 0, 0, 0, 0, 1, -1
+ },
+ new int[] {
+ -2, -1, -1, -1, -1, -1, -1, -2
+ }
+ }
+ );
+ positionScores.put(
+ Pawn.class,
+ new int[][] {
+ new int[] {
+ 0, 0, 0, 0, 0, 0, 0, 0
+ }, new int[] {
+ 5, 5, 5, 5, 5, 5, 5, 5
+ }, new int[] {
+ 1, 1, 2, 3, 3, 2, 1, 1
+ },
+ new int[] {
+ 0, 0, 1, 3, 3, 1, 0, 0
+ },
+ new int[] {
+ 0, 0, 0, 2, 2, 0, 0, 0
+ },
+ new int[] {
+ 0, 0, -1, 0, 0, -1, 0, 0
+ },
+ new int[] {
+ 0, 1, 1, -2, -2, 1, 1, 0
+ },
+ new int[] {
+ 0, 0, 0, 0, 0, 0, 0, 0
+ }
+ }
+ );
}
- public MoveProcessor(Board board, List rootMoves, Color color, int maxDepth, int alphaBetaThreshold) {
- this.board = board;
- this.rootMoves = rootMoves;
- this.color = color;
- this.maxDepth = maxDepth;
- this.alphaBetaThreshold = alphaBetaThreshold;
+ /**
+ * 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;
+ this.color = color;
+ this.maxDepth = maxDepth;
+ this.alphaBetaThreshold = alphaBetaThreshold;
}
@Override
@@ -86,18 +232,23 @@ public class MoveProcessor implements Callable {
int bestValue = Integer.MIN_VALUE;
for (Move move : moves) {
board.move(move);
- int teamValue = evaluate(board, color);
- int enemyValue = evaluate(board, color.opposite());
- int valueChange = teamValue - enemyValue;
+ int teamValue = evaluate(board, color);
+ int enemyValue = evaluate(board, color.opposite());
+ int valueChange = teamValue - enemyValue;
if (depth < maxDepth && valueChange >= alphaBetaThreshold)
- valueChange -= miniMax(board, board.getMoves(color.opposite()), color.opposite(), depth + 1);
+ valueChange -= miniMax(
+ board,
+ board.getMoves(color.opposite()),
+ color.opposite(),
+ depth + 1
+ );
if (valueChange > bestValue) {
bestValue = valueChange;
- if (depth == 0) bestMove = move;
+ if (depth == 0)
+ bestMove = move;
}
-
board.revert();
}
return bestValue;
@@ -105,18 +256,27 @@ 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;
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
- if (board.getBoardArr()[i][j] != null && board.getBoardArr()[i][j].getColor() == color) {
+ if (
+ board.getBoardArr()[i][j] != null
+ && board.getBoardArr()[i][j].getColor() == color
+ ) {
score += board.getBoardArr()[i][j].getValue();
- if (positionScores.containsKey(board.getBoardArr()[i][j].getClass()))
- score += positionScores.get(board.getBoardArr()[i][j].getClass())[i][color == Color.WHITE ? j : 7 - j];
+ if (
+ positionScores
+ .containsKey(board.getBoardArr()[i][j].getClass())
+ )
+ score += positionScores.get(
+ board.getBoardArr()[i][j].getClass()
+ )[i][color == Color.WHITE ? j : 7 - j];
}
return score;
}
diff --git a/src/dev/kske/chess/game/ai/ProcessingResult.java b/src/dev/kske/chess/game/ai/ProcessingResult.java
index 951ac71..0a13612 100644
--- a/src/dev/kske/chess/game/ai/ProcessingResult.java
+++ b/src/dev/kske/chess/game/ai/ProcessingResult.java
@@ -3,25 +3,41 @@ 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;
+ this.move = move;
+ this.score = score;
}
@Override
public String toString() {
- return String.format("ProcessingResult[Move = %s, Score = %d]", move, score);
+ 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..90216ab 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,11 +28,16 @@ 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() {
+ EngineInfo info = new EngineInfo(enginePath);
+ UCIHandle handle = new UCIHandle(enginePath);
+ handle.registerListener(new UCIListener() {
@Override
public void onIdName(String name) {
@@ -64,29 +64,66 @@ public class EngineUtil {
@SuppressWarnings("unchecked")
private static void loadEngineInfos() {
- try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(engineInfoFile))) {
+ try (
+ ObjectInputStream in
+ = new ObjectInputStream(new FileInputStream(engineInfoFile))
+ ) {
Object obj = in.readObject();
- if (obj instanceof ArrayList>) engineInfos = (ArrayList) obj;
- else throw new IOException("Serialized object has the wrong class.");
+ if (obj instanceof ArrayList>)
+ engineInfos = (ArrayList) obj;
+ else
+ throw new IOException("Serialized object has the wrong class.");
} catch (ClassNotFoundException | IOException ex) {
engineInfos = new ArrayList<>();
}
}
private static void saveEngineInfos() {
- try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(engineInfoFile))) {
+ try (
+ ObjectOutputStream out
+ = new ObjectOutputStream(new FileOutputStream(engineInfoFile))
+ ) {
out.writeObject(engineInfos);
} catch (IOException ex) {
ex.printStackTrace();
}
}
+ /**
+ * 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;
+ /**
+ * 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;
}
@@ -97,5 +134,8 @@ public class EngineUtil {
}
}
+ /**
+ * @return a list of all stored engine infos
+ */
public static List getEngineInfos() { return engineInfos; }
}
diff --git a/src/dev/kske/chess/io/TextureUtil.java b/src/dev/kske/chess/io/TextureUtil.java
index 79d251b..7bffddc 100644
--- a/src/dev/kske/chess/io/TextureUtil.java
+++ b/src/dev/kske/chess/io/TextureUtil.java
@@ -15,13 +15,14 @@ import dev.kske.chess.board.Piece;
* Project: Chess
* File: TextureUtil.java
* Created: 01.07.2019
- *
+ *
* @since Chess v0.1-alpha
* @author Kai S. K. Engelbart
*/
public class TextureUtil {
- private static Map textures = new HashMap<>(), scaledTextures = new HashMap<>();
+ private static Map textures = new HashMap<>(),
+ scaledTextures = new HashMap<>();
static {
loadPieceTextures();
@@ -32,28 +33,31 @@ public class TextureUtil {
/**
* Loads a piece texture fitting to a piece object.
- *
+ *
* @param piece The piece from which the texture properties are taken
* @return The fitting texture
*/
public static Image getPieceTexture(Piece piece) {
- String key = piece.toString().toLowerCase() + "_" + piece.getColor().toString().toLowerCase();
+ String key = piece.getClass().getSimpleName().toLowerCase() + "_"
+ + piece.getColor().toString().toLowerCase();
return scaledTextures.get(key);
}
/**
* Scales all piece textures to fit the current tile size.
- *
+ *
* @param tileSize the new width and height of the piece textures
*/
public static void scalePieceTextures(int tileSize) {
scaledTextures.clear();
- textures.forEach((key, img) -> scaledTextures.put(key, img.getScaledInstance(tileSize, tileSize, Image.SCALE_SMOOTH)));
+ textures.forEach(
+ (key, img) -> scaledTextures.put(key, img.getScaledInstance(tileSize, tileSize, Image.SCALE_SMOOTH))
+ );
}
/**
* Loads an image from a file in the resource folder.
- *
+ *
* @param fileName The name of the image resource
* @return The loaded image
*/
@@ -73,18 +77,22 @@ public class TextureUtil {
*/
private static void loadPieceTextures() {
Arrays
- .asList("king_white",
- "king_black",
- "queen_white",
- "queen_black",
- "rook_white",
- "rook_black",
- "knight_white",
- "knight_black",
- "bishop_white",
- "bishop_black",
- "pawn_white",
- "pawn_black")
- .forEach(name -> textures.put(name, loadImage("/pieces/" + name + ".png")));
+ .asList(
+ "king_white",
+ "king_black",
+ "queen_white",
+ "queen_black",
+ "rook_white",
+ "rook_black",
+ "knight_white",
+ "knight_black",
+ "bishop_white",
+ "bishop_black",
+ "pawn_white",
+ "pawn_black"
+ )
+ .forEach(
+ name -> textures.put(name, loadImage("/pieces/" + name + ".png"))
+ );
}
}
diff --git a/src/dev/kske/chess/pgn/PGNDatabase.java b/src/dev/kske/chess/pgn/PGNDatabase.java
index 127f9a6..c4a6607 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,34 @@ 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();
+ public void load(File pgnFile)
+ throws FileNotFoundException, ChessException {
+ 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..456f4d1 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
@@ -26,29 +20,53 @@ import dev.kske.chess.exception.ChessException;
*/
public class PGNGame {
- private final Map tagPairs = new HashMap<>(7);
- private final Board board;
+ private final Map tagPairs = new HashMap<>(7);
+ private final Board board;
- public PGNGame() { board = new Board(); }
+ /**
+ * Creates an instance of {@link PGNGame}. A new default {@link Board} will
+ * be
+ * created.
+ */
+ public PGNGame() {
+ board = new Board();
+ }
- public PGNGame(Board board) { this.board = 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;
- Pattern tagPairPattern = Pattern.compile("\\[(\\w+) \"(.*)\"]"),
- movePattern = Pattern.compile("\\d+\\.\\s+(?:(?:(\\S+)\\s+(\\S+))|(?:O-O-O)|(?:O-O))(?:\\+{0,2}|\\#)"),
- nagPattern = Pattern.compile("(\\$\\d{1,3})*"), terminationMarkerPattern = Pattern.compile("1-0|0-1|1\\/2-1\\/2|\\*");
+ MatchResult matchResult;
+ Pattern tagPairPattern = Pattern.compile("\\[(\\w+) \"(.*)\"]"),
+ movePattern = Pattern.compile(
+ "\\d+\\.\\s+(?:(?:(\\S+)\\s+(\\S+))|(?:O-O-O)|(?:O-O))(?:\\+{0,2}|\\#)"
+ ),
+ nagPattern = Pattern.compile("(\\$\\d{1,3})*"), terminationMarkerPattern = Pattern.compile("1-0|0-1|1\\/2-1\\/2|\\*");
// Parse tag pairs
while (sc.findInLine(tagPairPattern) != null) {
matchResult = sc.match();
- if (matchResult.groupCount() == 2) game.setTag(matchResult.group(1), matchResult.group(2));
- else break;
+ if (matchResult.groupCount() == 2)
+ game.setTag(matchResult.group(1), matchResult.group(2));
+ else
+ break;
sc.nextLine();
}
-
// Parse movetext
while (true) {
// Skip NAG (Numeric Annotation Glyph)
@@ -58,20 +76,31 @@ public class PGNGame {
if (sc.findWithinHorizon(movePattern, 20) != null) {
matchResult = sc.match();
- if (matchResult.groupCount() > 0) for (int i = 1; i < matchResult.groupCount() + 1; i++) {
- game.board.move(matchResult.group(i));
- System.out.println(game.getBoard().getLog().getLast().move.toLAN() + ": " + new FENString(game.board).toString());
- }
- else break;
- } else break;
+ if (matchResult.groupCount() > 0)
+ for (int i = 1; i < matchResult.groupCount() + 1; i++) {
+ game.board.move(matchResult.group(i));
+ System.out.println(
+ game.getBoard().getLog().getLast().move.toLAN()
+ + ": " + new FENString(game.board).toString()
+ );
+ }
+ else
+ break;
+ } else
+ break;
}
-
// Parse game termination marker
- if (sc.findWithinHorizon(terminationMarkerPattern, 20) == null) System.err.println("Termination marker expected");
+ if (sc.findWithinHorizon(terminationMarkerPattern, 20) == null)
+ System.err.println("Termination marker expected");
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", "*");
@@ -80,18 +109,21 @@ public class PGNGame {
tagPairs.forEach((k, v) -> pw.printf("[%s \"%s\"]%n", k, v));
// Insert newline if tags were printed
- if (!tagPairs.isEmpty()) pw.println();
+ if (!tagPairs.isEmpty())
+ pw.println();
if (!board.getLog().isEmpty()) {
// Collect SAN moves
- Board clone = new Board(board, true);
- List chunks = new ArrayList<>();
- boolean flag = true;
+ Board clone = new Board(board, true);
+ List chunks = new ArrayList<>();
+ boolean flag = true;
while (flag) {
Move move = clone.getLog().getLast().move;
flag = clone.getLog().hasParent();
clone.revert();
- String chunk = clone.getLog().getActiveColor() == Color.WHITE ? String.format(" %d. ", clone.getLog().getFullmoveNumber()) : " ";
+ String chunk = clone.getLog().getActiveColor() == Color.WHITE
+ ? String.format(" %d. ", clone.getLog().getFullmoveNumber())
+ : " ";
chunk += move.toSAN(clone);
chunks.add(chunk);
}
@@ -100,22 +132,47 @@ public class PGNGame {
// Write movetext
String line = "";
for (String chunk : chunks)
- if (line.length() + chunk.length() <= 80) line += chunk;
+ if (line.length() + chunk.length() <= 80)
+ line += chunk;
else {
pw.println(line);
line = chunk;
}
- if (!line.isEmpty()) pw.println(line);
+ if (!line.isEmpty())
+ pw.println(line);
}
// Write game termination marker
pw.print(tagPairs.get("Result"));
}
- public String getTag(String tagName) { return tagPairs.get(tagName); }
+ /**
+ * @param tagName the name of a game tag
+ * @return the value of the game tag
+ */
+ public String getTag(String tagName) {
+ return tagPairs.get(tagName);
+ }
- public boolean hasTag(String tagName) { return tagPairs.containsKey(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);
+ }
- public void setTag(String tagName, String tagValue) { tagPairs.put(tagName, tagValue); }
+ /**
+ * 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..da8d9d4 100644
--- a/src/dev/kske/chess/uci/UCIHandle.java
+++ b/src/dev/kske/chess/uci/UCIHandle.java
@@ -11,22 +11,33 @@ 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
*/
public class UCIHandle {
- private final Process process;
- private final PrintWriter out;
- private final UCIReceiver receiver;
+ private final Process process;
+ 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());
+ 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();
@@ -35,68 +46,89 @@ public class UCIHandle {
/**
* Tells the engine to use UCI.
*/
- public void uci() { out.println("uci"); }
+ public void uci() {
+ out.println("uci");
+ }
/**
* Switches the debug mode of the engine on or off.
- *
- * @param debug Enables debugging if set to {@code true}, disables it otherwise
+ *
+ * @param debug Enables debugging if set to {@code true}, disables it
+ * otherwise
*/
- public void debug(boolean debug) { out.println("debug " + (debug ? "on" : "off")); }
+ public void debug(boolean debug) {
+ out.println("debug " + (debug ? "on" : "off"));
+ }
/**
* Synchronized the engine with the GUI
*/
- public void isready() { out.println("isready"); }
+ public void isready() {
+ out.println("isready");
+ }
/**
* Signifies a button press to the engine.
- *
+ *
* @param name The name of the button
*/
- public void setOption(String name) { out.println("setoption name " + name); }
+ 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
*/
- public void setOption(String name, String value) { out.printf("setoption name %s value %s%n", name, value); }
+ public void setOption(String name, String value) {
+ out.printf("setoption name %s value %s%n", name, value);
+ }
/**
* Registers the engine
- *
+ *
* @param name The name the engine should be registered with
* @param code The code the engine should be registered with
*/
- public void register(String name, String code) { out.printf("register %s %s%n", name, code); }
+ public void register(String name, String code) {
+ out.printf("register %s %s%n", name, code);
+ }
/**
* Tells the engine to postpone the registration.
*/
- public void registerLater() { out.println("register later"); }
+ public void registerLater() {
+ out.println("register later");
+ }
/**
* Tells the engine that the next search will be from a different game.
*/
- public void uciNewGame() { out.println("ucinewgame"); }
+ public void uciNewGame() {
+ out.println("ucinewgame");
+ }
/**
* Sets up the position in its initial state.
*/
- public void positionStartpos() { out.println("position startpos"); }
+ public void positionStartpos() {
+ out.println("position startpos");
+ }
/**
* 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); }
+ 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
*/
@@ -109,15 +141,19 @@ public class UCIHandle {
/**
* Starts calculating on the current position.
*/
- public void go() { out.println("go"); }
+ public void go() {
+ out.println("go");
+ }
/**
* Starts calculating on the current position.
- * This command has multiple optional parameters which will only be included in
- * the call if they are not {@code null}, greater than zero or {@code true} for
+ * This command has multiple optional parameters which will only be included
+ * in
+ * 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
@@ -131,8 +167,11 @@ public class UCIHandle {
* @param moveTime the exact search time
* @param infinite search until the {@code stop} command
*/
- public void go(List searchMoves, boolean ponder, int wTime, int bTime, int wInc, int bInc, int movesToGo, int depth, int nodes, int mate,
- int moveTime, boolean infinite) {
+ public void go(
+ List searchMoves, boolean ponder, int wTime, int bTime, int wInc,
+ int bInc, int movesToGo, int depth, int nodes, int mate,
+ int moveTime, boolean infinite
+ ) {
StringJoiner joiner = new StringJoiner(" ");
joiner.add("go");
@@ -140,7 +179,8 @@ public class UCIHandle {
joiner.add("searchmoves");
searchMoves.forEach(m -> joiner.add(m.toLAN()));
}
- if (ponder) joiner.add("ponder");
+ if (ponder)
+ joiner.add("ponder");
if (wTime > 0) {
joiner.add("wtime");
joiner.add(String.valueOf(wTime));
@@ -177,24 +217,38 @@ public class UCIHandle {
joiner.add("movetime");
joiner.add(String.valueOf(moveTime));
}
- if (infinite) joiner.add("infinite");
+ if (infinite)
+ joiner.add("infinite");
out.println(joiner);
}
/**
* Stops calculation as soon as possible.
*/
- public void stop() { out.println("stop"); }
+ public void stop() {
+ out.println("stop");
+ }
/**
* Tells the engine that the user has played the expected move.
*/
- public void ponderHit() { out.println("ponderhit"); }
+ public void ponderHit() {
+ out.println("ponderhit");
+ }
/**
* Quits the engine process as soon as possible.
*/
- public void quit() { out.println("quit"); }
+ 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..d06eed3 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,41 +8,51 @@ 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
*/
public class UCIInfo {
- private int depth, seldepth, time, nodes, multipv, currmovenumber, hashfull, nps, tbhits, sbhits, cpuload, cpunr;
- private List pv = new ArrayList<>(), refutation = new ArrayList<>();
- private Map> currline = new HashMap<>();
- private Move currmove;
- private Score score;
- private String displayString;
+ private int depth, seldepth, time, nodes, multipv, currmovenumber, hashfull,
+ nps, tbhits, sbhits, cpuload, cpunr;
+ private List pv = new ArrayList<>(), refutation = new ArrayList<>();
+ private Map> currline = new HashMap<>();
+ private Move currmove;
+ private Score score;
+ private String displayString;
/**
* Contains every parameter for the UCI info command. Helpful for parsing
* multi-value parameters.
*/
- private static final List params = Arrays.asList("depth",
- "seldepth",
- "time",
- "nodes",
- "multipv",
- "currmove",
- "currmovenumber",
- "hashfull",
- "nps",
- "tbhits",
- "sbhits",
- "cpuload",
- "string",
- "score",
- "pv",
- "refutation",
- "currline");
+ private static final List params = Arrays.asList(
+ "depth",
+ "seldepth",
+ "time",
+ "nodes",
+ "multipv",
+ "currmove",
+ "currmovenumber",
+ "hashfull",
+ "nps",
+ "tbhits",
+ "sbhits",
+ "cpuload",
+ "string",
+ "score",
+ "pv",
+ "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(" ");
@@ -93,7 +99,8 @@ public class UCIInfo {
displayString = tokens[++i];
break;
case "score":
- score = new Score(line.substring(line.indexOf("score") + tokens[i].length() + 1));
+ score
+ = new Score(line.substring(line.indexOf("score") + tokens[i].length() + 1));
i += score.getLength() + 1;
break;
case "pv":
@@ -106,15 +113,22 @@ 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++]));
currline.put(cpu, moves);
- System.err.println("The parameter 'currline' for command 'info' is not yet implemented");
+ System.err.println(
+ "The parameter 'currline' for command 'info' is not yet implemented"
+ );
break;
default:
- System.err.printf("Unknown parameter '%s' for command 'info' found!%n", tokens[i]);
+ System.err.printf(
+ "Unknown parameter '%s' for command 'info' found!%n",
+ tokens[i]
+ );
}
}
@@ -156,15 +170,16 @@ public class UCIInfo {
public static class Score {
- private int cp, mate;
- private boolean lowerbound, upperbound;
- private int length;
+ private int cp, mate;
+ private boolean lowerbound, upperbound;
+ private int length;
public Score(String line) {
- String[] tokens = line.split(" ");
- int i = 0;
+ String[] tokens = line.split(" ");
+ int i = 0;
for (; i < tokens.length; i++) {
- if (params.contains(tokens[i])) break;
+ if (params.contains(tokens[i]))
+ break;
switch (tokens[i]) {
case "cp":
cp = Integer.parseInt(tokens[++i]);
@@ -179,7 +194,10 @@ public class UCIInfo {
upperbound = true;
break;
default:
- System.err.printf("Unknown parameter '%s' for command 'score' found!%n", tokens[i]);
+ System.err.printf(
+ "Unknown parameter '%s' for command 'score' found!%n",
+ tokens[i]
+ );
}
}
length = i + 1;
diff --git a/src/dev/kske/chess/uci/UCIListener.java b/src/dev/kske/chess/uci/UCIListener.java
index 9d8b727..18b8442 100644
--- a/src/dev/kske/chess/uci/UCIListener.java
+++ b/src/dev/kske/chess/uci/UCIListener.java
@@ -6,7 +6,7 @@ import dev.kske.chess.board.Move;
* Project: Chess
* File: UCIListener.java
* Created: 19.07.2019
- *
+ *
* @since Chess v0.3-alpha
* @author Kai S. K. Engelbart
*/
@@ -14,14 +14,14 @@ public interface UCIListener {
/**
* Identifies the name of the engine.
- *
+ *
* @param name The name of the engine
*/
default void onIdName(String name) {}
/**
* Identifies the author of the engine.
- *
+ *
* @param author The name of the engine's author
*/
default void onIdAuthor(String author) {}
@@ -38,14 +38,14 @@ public interface UCIListener {
/**
* The engine has stopped searching and has found the best move.
- *
+ *
* @param move The best moves the engine has found
*/
default void onBestMove(String move) {}
/**
* The engine has stopped searching and has found the best move.
- *
+ *
* @param move The best move the engine has found
* @param ponderMove The move the engine likes to ponder on
*/
@@ -83,14 +83,14 @@ public interface UCIListener {
/**
* The engine sends information to the GUI.
- *
+ *
* @param info Contains all pieces of information to be sent
*/
default void onInfo(UCIInfo info) {}
/**
* Tells the GUI which parameters can be changed in the engine.
- *
+ *
* @param option Option object describing the parameter
*/
default void onOption(UCIOption option) {}
diff --git a/src/dev/kske/chess/uci/UCIOption.java b/src/dev/kske/chess/uci/UCIOption.java
index ed38017..9e208ed 100644
--- a/src/dev/kske/chess/uci/UCIOption.java
+++ b/src/dev/kske/chess/uci/UCIOption.java
@@ -1,23 +1,20 @@
package dev.kske.chess.uci;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.StringJoiner;
+import java.util.*;
/**
* Project: Chess
* File: UCIOption.java
* Created: 22.07.2019
- *
+ *
* @since Chess v0.3-alpha
* @author Kai S. K. Engelbart
*/
public class UCIOption {
- private String name, defaultVal, minVal, maxVal;
- private GUIType type;
- private List varList;
+ private String name, defaultVal, minVal, maxVal;
+ private GUIType type;
+ private List varList;
public UCIOption(String line) {
varList = new ArrayList<>();
@@ -27,7 +24,10 @@ public class UCIOption {
switch (tokens[i]) {
case "name":
StringJoiner nameJoiner = new StringJoiner(" ");
- while (!Arrays.asList("type", "default", "min", "max", "var").contains(tokens[i + 1]))
+ while (
+ !Arrays.asList("type", "default", "min", "max", "var")
+ .contains(tokens[i + 1])
+ )
nameJoiner.add(tokens[++i]);
name = nameJoiner.toString();
break;
@@ -48,7 +48,10 @@ public class UCIOption {
varList.add(tokens[++i]);
break;
default:
- System.err.printf("Unknown parameter '%s' for command 'option' found!%n", tokens[i]);
+ System.err.printf(
+ "Unknown parameter '%s' for command 'option' found!%n",
+ tokens[i]
+ );
}
}
@@ -64,7 +67,7 @@ public class UCIOption {
public List getVarList() { return varList; }
- public static enum GUIType {
+ public enum GUIType {
CHECK, SPIN, COMBO, BUTTON, STRING
}
}
diff --git a/src/dev/kske/chess/uci/UCIReceiver.java b/src/dev/kske/chess/uci/UCIReceiver.java
index 1d80b6b..2c1ac98 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,17 +20,26 @@ 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<>();
+ 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;
while (!Thread.currentThread().isInterrupted())
try {
- if ((line = in.readLine()) != null && !line.isEmpty()) parse(line);
+ if ((line = in.readLine()) != null && !line.isEmpty())
+ parse(line);
} catch (IndexOutOfBoundsException ex) {
System.err.println("Too few arguments were provided!");
ex.printStackTrace();
@@ -43,8 +49,9 @@ public class UCIReceiver implements Runnable {
}
private void parse(String line) {
- int spaceIndex = line.indexOf(' ');
- String command = spaceIndex == -1 ? line : line.substring(0, spaceIndex);
+ int spaceIndex = line.indexOf(' ');
+ String command
+ = spaceIndex == -1 ? line : line.substring(0, spaceIndex);
switch (command) {
case "id":
parseId(line.substring(command.length() + 1));
@@ -76,8 +83,8 @@ public class UCIReceiver implements Runnable {
}
private void parseId(String line) {
- String param = line.substring(0, line.indexOf(' '));
- String arg = line.substring(param.length() + 1);
+ String param = line.substring(0, line.indexOf(' '));
+ String arg = line.substring(param.length() + 1);
switch (param) {
case "name":
listeners.forEach(l -> l.onIdName(arg));
@@ -86,17 +93,22 @@ public class UCIReceiver implements Runnable {
listeners.forEach(l -> l.onIdAuthor(arg));
break;
default:
- System.err.printf("Unknown parameter '%s' for command 'id' found!%n", param);
+ System.err.printf(
+ "Unknown parameter '%s' for command 'id' found!%n",
+ param
+ );
}
}
private void parseBestMove(String line) {
- String[] tokens = line.split(" ");
- String move = tokens[0];
+ String[] tokens = line.split(" ");
+ String move = tokens[0];
// Ponder move
- if (tokens.length == 3) listeners.forEach(l -> l.onBestMove(move, Move.fromLAN(tokens[2])));
- else listeners.forEach(l -> l.onBestMove(move));
+ if (tokens.length == 3)
+ listeners.forEach(l -> l.onBestMove(move, Move.fromLAN(tokens[2])));
+ else
+ listeners.forEach(l -> l.onBestMove(move));
}
private void parseCopyProtection(String line) {
@@ -111,7 +123,10 @@ public class UCIReceiver implements Runnable {
listeners.forEach(UCIListener::onCopyProtectionError);
break;
default:
- System.err.printf("Unknown parameter '%s' for command 'copyprotection' found!%n", line);
+ System.err.printf(
+ "Unknown parameter '%s' for command 'copyprotection' found!%n",
+ line
+ );
}
}
@@ -127,13 +142,27 @@ public class UCIReceiver implements Runnable {
listeners.forEach(UCIListener::onRegistrationError);
break;
default:
- System.err.printf("Unknown parameter '%s' for command 'registration' found!%n", line);
+ System.err.printf(
+ "Unknown parameter '%s' for command 'registration' found!%n",
+ line
+ );
}
}
- private void parseInfo(String line) { listeners.forEach(l -> l.onInfo(new UCIInfo(line))); }
+ private void parseInfo(String line) {
+ listeners.forEach(l -> l.onInfo(new UCIInfo(line)));
+ }
- private void parseOption(String line) { listeners.forEach(l -> l.onOption(new UCIOption((line)))); }
+ 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
deleted file mode 100644
index 64515cb..0000000
--- a/src/dev/kske/chess/ui/AIConfigDialog.java
+++ /dev/null
@@ -1,84 +0,0 @@
-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;
-
-/**
- * Project: Chess
- * File: AIConfigDialog.java
- * Created: 16.07.2019
- *
- * @since Chess v0.1-alpha
- * @author Kai S. K. Engelbart
- */
-@Deprecated
-public class AIConfigDialog extends JDialog {
-
- private static final long serialVersionUID = -8047984368152479992L;
-
- private int maxDepth;
- private int alphaBetaThreshold;
- private boolean startGame = false;
-
- public AIConfigDialog() {
- setSize(new Dimension(337, 212));
- setModal(true);
- setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
- setTitle("AI Configuration");
- getContentPane().setLayout(null);
-
- JSpinner spAlphaBetaThreshold = new JSpinner();
- spAlphaBetaThreshold.setBounds(222, 68, 95, 28);
- getContentPane().add(spAlphaBetaThreshold);
- spAlphaBetaThreshold.setModel(new SpinnerNumberModel(-10, -100, 100, 5));
-
- JSpinner spMaxDepth = new JSpinner();
- spMaxDepth.setBounds(222, 6, 95, 28);
- getContentPane().add(spMaxDepth);
- spMaxDepth.setModel(new SpinnerNumberModel(4, 1, 10, 1));
-
- JLabel lblAlphabetaThreshold = new JLabel("Alpha-Beta Threshold:");
- lblAlphabetaThreshold.setBounds(16, 68, 194, 28);
- getContentPane().add(lblAlphabetaThreshold);
-
- JButton btnOk = new JButton("OK");
- btnOk.setBounds(16, 137, 84, 28);
- getContentPane().add(btnOk);
- btnOk.addActionListener((evt) -> {
- maxDepth = ((Integer) spMaxDepth.getValue()).intValue();
- alphaBetaThreshold = ((Integer) spAlphaBetaThreshold.getValue()).intValue();
- startGame = true;
- dispose();
- });
- btnOk.setToolTipText("Start the game");
-
- JButton btnCancel = new JButton("Cancel");
- btnCancel.setBounds(222, 137, 95, 28);
- getContentPane().add(btnCancel);
- btnCancel.addActionListener((evt) -> dispose());
- btnCancel.setToolTipText("Cancel the game start");
-
- JLabel lblMaximalRecursionDepth = new JLabel("Maximal Recursion Depth:");
- lblMaximalRecursionDepth.setBounds(16, 12, 194, 16);
- getContentPane().add(lblMaximalRecursionDepth);
-
- setLocationRelativeTo(null);
- }
-
- public int getMaxDepth() { return maxDepth; }
-
- public void setMaxDepth(int maxDepth) { this.maxDepth = maxDepth; }
-
- public int getAlphaBetaThreshold() { return alphaBetaThreshold; }
-
- public void setAlphaBetaThreshold(int alphaBetaThreshold) { this.alphaBetaThreshold = alphaBetaThreshold; }
-
- public boolean isStartGame() { return startGame; }
-
- public void setStartGame(boolean startGame) { this.startGame = startGame; }
-}
diff --git a/src/dev/kske/chess/ui/BoardComponent.java b/src/dev/kske/chess/ui/BoardComponent.java
index b4120c8..b232bc2 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,27 +43,44 @@ 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);
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
- if (j > 0) g.setColor(g.getColor().equals(Color.white) ? Color.lightGray : Color.white);
+ if (j > 0)
+ g.setColor(
+ g.getColor().equals(Color.white) ? Color.lightGray : Color.white
+ );
g.fillRect(tileSize * i, tileSize * j, tileSize, tileSize);
}
// Draw the pieces if a board is present
- if (board != null) for (int i = 0; i < 8; i++)
- for (int j = 0; j < 8; j++)
- if (board.getBoardArr()[i][j] != null)
- g.drawImage(TextureUtil.getPieceTexture(board.getBoardArr()[i][j]), i * tileSize, j * tileSize, this);
+ if (board != null)
+ for (int i = 0; i < 8; i++)
+ for (int j = 0; j < 8; j++)
+ if (board.getBoardArr()[i][j] != null)
+ 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..8032e5c 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
*/
@@ -16,14 +19,17 @@ public class BoardPane extends JLayeredPane {
private static final long serialVersionUID = -5415058382478806092L;
- private final BoardComponent boardComponent;
- private final OverlayComponent overlayComponent;
+ private final BoardComponent boardComponent;
+ private final OverlayComponent overlayComponent;
private int tileSize;
+ /**
+ * Creates an instance of {@link BoardPane}.
+ */
public BoardPane() {
- boardComponent = new BoardComponent(this);
- overlayComponent = new OverlayComponent(this);
+ boardComponent = new BoardComponent(this);
+ overlayComponent = new OverlayComponent(this);
setLayer(overlayComponent, 1);
setLayout(null);
@@ -37,9 +43,20 @@ 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; }
- public OverlayComponent getOverlayComponent() { return overlayComponent; }
+ /**
+ * @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..31753b4 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;
@@ -36,9 +28,20 @@ public class DialogUtil {
/**
* Saves the last accessed folder for loading and saving game files.
*/
- private static Preferences preferences = Preferences.userNodeForPackage(DialogUtil.class);
+ private static Preferences preferences
+ = Preferences.userNodeForPackage(DialogUtil.class);
- public static void showFileSelectionDialog(Component parent, Consumer> action, Collection filters) {
+ /**
+ * 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) {
action.accept(Arrays.asList(fileChooser.getSelectedFile()));
@@ -46,27 +49,60 @@ public class DialogUtil {
}
}
- public static void showFileSaveDialog(Component parent, Consumer action, Collection filters) {
+ /**
+ * 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) {
- action.accept(new File(fileChooser.getSelectedFile().getAbsolutePath() + "."
- + ((FileNameExtensionFilter) fileChooser.getFileFilter()).getExtensions()[0]));
+ action.accept(
+ new File(
+ fileChooser.getSelectedFile().getAbsolutePath() + "."
+ + ((FileNameExtensionFilter) fileChooser
+ .getFileFilter()).getExtensions()[0]
+ )
+ );
preferences.put("path", fileChooser.getSelectedFile().getParent());
}
}
- private static JFileChooser createFileChooser(Collection filters) {
+ private static JFileChooser
+ createFileChooser(Collection filters) {
JFileChooser fileChooser = new JFileChooser();
- fileChooser.setCurrentDirectory(new File(preferences.get("path", System.getProperty("user.home"))));
+ fileChooser.setCurrentDirectory(
+ new File(preferences.get("path", System.getProperty("user.home")))
+ );
fileChooser.setAcceptAllFileFilterUsed(false);
filters.forEach(fileChooser::addChoosableFileFilter);
return fileChooser;
}
- public static void showGameConfigurationDialog(Component parent, BiConsumer action) {
+ /**
+ * 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();
- List options = new ArrayList<>(Arrays.asList("Natural Player", "AI Player"));
+ List options
+ = new ArrayList<>(Arrays.asList("Natural Player", "AI Player"));
EngineUtil.getEngineInfos().forEach(info -> options.add(info.name));
JLabel lblWhite = new JLabel("White:");
@@ -89,7 +125,15 @@ public class DialogUtil {
cbBlack.setBounds(98, 36, 159, 22);
dialogPanel.add(cbBlack);
- JOptionPane.showMessageDialog(parent, dialogPanel, "Game configuration", JOptionPane.QUESTION_MESSAGE);
- action.accept(options.get(cbWhite.getSelectedIndex()), options.get(cbBlack.getSelectedIndex()));
+ JOptionPane.showMessageDialog(
+ parent,
+ dialogPanel,
+ "Game configuration",
+ JOptionPane.QUESTION_MESSAGE
+ );
+ action.accept(
+ options.get(cbWhite.getSelectedIndex()),
+ options.get(cbBlack.getSelectedIndex())
+ );
}
}
diff --git a/src/dev/kske/chess/ui/GameDropTarget.java b/src/dev/kske/chess/ui/GameDropTarget.java
index 4419495..58f1c17 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,14 +24,25 @@ public class GameDropTarget extends DropTargetAdapter {
private MainWindow mainWindow;
- public GameDropTarget(MainWindow mainWindow) { this.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")
@Override
public void drop(DropTargetDropEvent evt) {
try {
evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
- mainWindow.loadFiles((List) evt.getTransferable().getTransferData(DataFlavor.javaFileListFlavor));
+ mainWindow.loadFiles(
+ (List) evt.getTransferable()
+ .getTransferData(DataFlavor.javaFileListFlavor)
+ );
} catch (UnsupportedFlavorException | IOException ex) {
ex.printStackTrace();
evt.rejectDrop();
diff --git a/src/dev/kske/chess/ui/GamePane.java b/src/dev/kske/chess/ui/GamePane.java
index 7f891d2..44f36de 100644
--- a/src/dev/kske/chess/ui/GamePane.java
+++ b/src/dev/kske/chess/ui/GamePane.java
@@ -7,31 +7,23 @@ 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.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
- *
+ *
* @since Chess v0.4-alpha
* @author Kai S. K. Engelbart
*/
@@ -39,28 +31,37 @@ public class GamePane extends JComponent {
private static final long serialVersionUID = 4349772338239617477L;
- private JButton btnRestart, btnSwapColors;
- private BoardPane boardPane;
- private Game game;
- private Color activeColor;
- private JPanel moveSelectionPanel;
- private JButton btnNext;
- private JButton btnFirst;
- private JButton btnLast;
+ private JButton btnRestart, btnSwapColors;
+ private BoardPane boardPane;
+ private Game game;
+ private Color activeColor;
+ private JPanel moveSelectionPanel;
+ private JButton btnFirst, btnPrevious, btnNext, btnLast;
+ /**
+ * Creates an instance of {@link GamePane}.
+ */
public GamePane() {
activeColor = Color.WHITE;
GridBagLayout gridBagLayout = new GridBagLayout();
- gridBagLayout.columnWidths = new int[] { 450, 1, 0 };
- gridBagLayout.rowHeights = new int[] { 33, 267, 1, 0 };
- gridBagLayout.columnWeights = new double[] { 0.0, 1.0, 1.0 };
- gridBagLayout.rowWeights = new double[] { 1.0, 1.0, 1.0, Double.MIN_VALUE };
+ gridBagLayout.columnWidths = new int[] {
+ 450, 1, 0
+ };
+ gridBagLayout.rowHeights = new int[] {
+ 33, 267, 1, 0
+ };
+ gridBagLayout.columnWeights = new double[] {
+ 0.0, 1.0, 1.0
+ };
+ gridBagLayout.rowWeights = new double[] {
+ 1.0, 1.0, 1.0, Double.MIN_VALUE
+ };
setLayout(gridBagLayout);
JPanel toolPanel = new JPanel();
btnRestart = new JButton("Restart");
- btnRestart.addActionListener((evt) -> {
+ btnRestart.addActionListener(evt -> {
if (game != null) {
game.reset();
game.start();
@@ -68,9 +69,10 @@ public class GamePane extends JComponent {
});
btnSwapColors = new JButton("Play as black");
- btnSwapColors.addActionListener((evt) -> {
+ btnSwapColors.addActionListener(evt -> {
game.swapColors();
- btnSwapColors.setText("Play as " + activeColor.toString().toLowerCase());
+ btnSwapColors
+ .setText("Play as " + activeColor.toString().toLowerCase());
activeColor = activeColor.opposite();
});
@@ -78,71 +80,94 @@ public class GamePane extends JComponent {
toolPanel.add(btnSwapColors);
GridBagConstraints gbc_toolPanel = new GridBagConstraints();
- gbc_toolPanel.anchor = GridBagConstraints.NORTH;
- gbc_toolPanel.fill = GridBagConstraints.HORIZONTAL;
- gbc_toolPanel.gridx = 0;
- gbc_toolPanel.gridy = 0;
- gbc_toolPanel.gridwidth = 2;
+ gbc_toolPanel.anchor = GridBagConstraints.NORTH;
+ gbc_toolPanel.fill = GridBagConstraints.HORIZONTAL;
+ gbc_toolPanel.gridx = 0;
+ gbc_toolPanel.gridy = 0;
+ gbc_toolPanel.gridwidth = 2;
add(toolPanel, gbc_toolPanel);
moveSelectionPanel = new JPanel();
GridBagConstraints gbc_moveSelectionPanel = new GridBagConstraints();
- gbc_moveSelectionPanel.fill = GridBagConstraints.BOTH;
- gbc_moveSelectionPanel.gridx = 2;
- gbc_moveSelectionPanel.gridy = 0;
+ gbc_moveSelectionPanel.fill = GridBagConstraints.BOTH;
+ gbc_moveSelectionPanel.gridx = 2;
+ gbc_moveSelectionPanel.gridy = 0;
add(moveSelectionPanel, gbc_moveSelectionPanel);
btnFirst = new JButton("First");
btnFirst.setEnabled(false);
moveSelectionPanel.add(btnFirst);
- JButton btnPreviousMove = new JButton("Previous");
- btnPreviousMove.setEnabled(false);
- moveSelectionPanel.add(btnPreviousMove);
+ btnPrevious = new JButton("Previous");
+ btnPrevious.addActionListener(evt -> {
+ if (game != null) {
+ game.getBoard().selectPreviousNode();
+ getBoardPane().getOverlayComponent().clearArrow();
+ repaint();
+ }
+ });
+ moveSelectionPanel.add(btnPrevious);
btnNext = new JButton("Next");
- btnNext.setEnabled(false);
+ 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.")
+ );
+ game.getBoard().selectNextNode(index);
+ getBoardPane().getOverlayComponent().clearArrow();
+ repaint();
+ }
+ });
moveSelectionPanel.add(btnNext);
btnLast = new JButton("Last");
btnLast.setEnabled(false);
moveSelectionPanel.add(btnLast);
+
boardPane = new BoardPane();
GridBagConstraints gbc_boardPane = new GridBagConstraints();
- gbc_boardPane.fill = GridBagConstraints.BOTH;
- gbc_boardPane.gridx = 0;
- gbc_boardPane.gridy = 1;
+ gbc_boardPane.fill = GridBagConstraints.BOTH;
+ gbc_boardPane.gridx = 0;
+ gbc_boardPane.gridy = 1;
add(boardPane, gbc_boardPane);
- JPanel numberPanel = new JPanel(new GridLayout(8, 1));
- GridBagConstraints gbc_numberPanel = new GridBagConstraints();
- gbc_numberPanel.anchor = GridBagConstraints.WEST;
- gbc_numberPanel.fill = GridBagConstraints.VERTICAL;
- gbc_numberPanel.gridx = 1;
- gbc_numberPanel.gridy = 1;
+ JPanel numberPanel = new JPanel(new GridLayout(8, 1));
+ GridBagConstraints gbc_numberPanel = new GridBagConstraints();
+ gbc_numberPanel.anchor = GridBagConstraints.WEST;
+ gbc_numberPanel.fill = GridBagConstraints.VERTICAL;
+ gbc_numberPanel.gridx = 1;
+ gbc_numberPanel.gridy = 1;
add(numberPanel, gbc_numberPanel);
- JPanel letterPanel = new JPanel(new GridLayout(1, 8));
- GridBagConstraints gbc_letterPanel = new GridBagConstraints();
- gbc_letterPanel.anchor = GridBagConstraints.NORTH;
- gbc_letterPanel.fill = GridBagConstraints.HORIZONTAL;
- gbc_letterPanel.gridx = 0;
- gbc_letterPanel.gridy = 2;
+ JPanel letterPanel = new JPanel(new GridLayout(1, 8));
+ GridBagConstraints gbc_letterPanel = new GridBagConstraints();
+ gbc_letterPanel.anchor = GridBagConstraints.NORTH;
+ gbc_letterPanel.fill = GridBagConstraints.HORIZONTAL;
+ gbc_letterPanel.gridx = 0;
+ gbc_letterPanel.gridy = 2;
add(letterPanel, gbc_letterPanel);
// Initialize board coordinates
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);
}
-
- JScrollPane scrollPane = new JScrollPane();
- GridBagConstraints gbc_scrollPane = new GridBagConstraints();
- gbc_scrollPane.fill = GridBagConstraints.BOTH;
- gbc_scrollPane.gridx = 2;
- gbc_scrollPane.gridy = 1;
+ JScrollPane scrollPane = new JScrollPane();
+ GridBagConstraints gbc_scrollPane = new GridBagConstraints();
+ gbc_scrollPane.fill = GridBagConstraints.BOTH;
+ gbc_scrollPane.gridx = 2;
+ gbc_scrollPane.gridy = 1;
add(scrollPane, gbc_scrollPane);
JList pgnList = new JList<>();
@@ -152,27 +177,40 @@ public class GamePane extends JComponent {
pgnList.setCellRenderer(new MoveNodeRenderer());
scrollPane.setViewportView(pgnList);
- // Listen to moves and game (re-)starts and update the move list or disable the
+ // 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) {
- if (event instanceof MoveEvent && (((MoveEvent) event).getBoardState() == BoardState.CHECKMATE
- || ((MoveEvent) event).getBoardState() == BoardState.STALEMATE))
+ if (
+ event instanceof MoveEvent && (((MoveEvent) event)
+ .getBoardState() == BoardState.CHECKMATE
+ || ((MoveEvent) event)
+ .getBoardState() == BoardState.STALEMATE)
+ )
btnSwapColors.setEnabled(false);
- else if (event instanceof GameStartEvent) btnSwapColors.setEnabled(
- game.getPlayers().get(Color.WHITE) instanceof NaturalPlayer ^ game.getPlayers().get(Color.BLACK) instanceof NaturalPlayer);
+ else
+ if (event instanceof GameStartEvent)
+ btnSwapColors.setEnabled(
+ game.getPlayers().get(Color.WHITE) instanceof NaturalPlayer ^ game.getPlayers().get(Color.BLACK) instanceof NaturalPlayer
+ );
- if (game.getBoard().getLog() == null) return;
+ if (game.getBoard().getLog() == null)
+ return;
DefaultListModel model = new DefaultListModel<>();
- game.getBoard().getLog().forEach(node -> model.addElement(node));
+ game.getBoard().getLog().forEach(model::addElement);
pgnList.setModel(model);
}
@Override
- public Set> supports() { return new HashSet<>(Arrays.asList(MoveEvent.class, GameStartEvent.class)); }
+ public Set> supports() {
+ return new HashSet<>(
+ Arrays.asList(MoveEvent.class, GameStartEvent.class)
+ );
+ }
});
}
@@ -187,13 +225,15 @@ public class GamePane extends JComponent {
public Game getGame() { return game; }
/**
- * Assigns a new {@link Game} instance to this game pane. If exactly one of the
+ * Assigns a new {@link Game} instance to this game pane. If exactly one of
+ * the
* players is natural, color swapping functionality is enabled.
- *
+ *
* @param game The {@link Game} to assign to this game pane.
*/
public void setGame(Game game) {
- if (this.game != null) this.game.stop();
+ if (this.game != null)
+ this.game.stop();
this.game = game;
}
}
diff --git a/src/dev/kske/chess/ui/GameTabComponent.java b/src/dev/kske/chess/ui/GameTabComponent.java
index ff492b4..039d91b 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,9 +22,16 @@ 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");
+ if (tabbedPane == null)
+ throw new NullPointerException("TabbedPane is null");
this.tabbedPane = tabbedPane;
// Create title JLabel
@@ -69,13 +69,21 @@ public class GameTabComponent extends JPanel {
addMouseListener(new MouseAdapter() {
@Override
- public void mouseEntered(MouseEvent evt) { setBorderPainted(true); }
+ public void mouseEntered(MouseEvent evt) {
+ setBorderPainted(true);
+ }
@Override
- public void mouseExited(MouseEvent evt) { setBorderPainted(false); }
+ public void mouseExited(MouseEvent evt) {
+ setBorderPainted(false);
+ }
});
setRolloverEnabled(true);
- addActionListener((evt) -> { int i = tabbedPane.indexOfTabComponent(GameTabComponent.this); if (i != -1) tabbedPane.remove(i); });
+ addActionListener(evt -> {
+ int i = tabbedPane.indexOfTabComponent(GameTabComponent.this);
+ if (i != -1)
+ tabbedPane.remove(i);
+ });
}
@Override
@@ -86,13 +94,25 @@ public class GameTabComponent extends JPanel {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
// shift the image for pressed buttons
- if (getModel().isPressed()) { g2.translate(1, 1); }
+ if (getModel().isPressed())
+ g2.translate(1, 1);
g2.setStroke(new BasicStroke(2));
g2.setColor(Color.BLACK);
- if (getModel().isRollover()) { g2.setColor(Color.MAGENTA); }
+ if (getModel().isRollover())
+ g2.setColor(Color.MAGENTA);
final int delta = 6;
- g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1);
- g2.drawLine(getWidth() - delta - 1, delta, delta, getHeight() - delta - 1);
+ g2.drawLine(
+ delta,
+ delta,
+ getWidth() - delta - 1,
+ getHeight() - delta - 1
+ );
+ g2.drawLine(
+ getWidth() - delta - 1,
+ delta,
+ delta,
+ getHeight() - delta - 1
+ );
g2.dispose();
}
}
diff --git a/src/dev/kske/chess/ui/MainWindow.java b/src/dev/kske/chess/ui/MainWindow.java
index 22ccbc3..3e691bd 100644
--- a/src/dev/kske/chess/ui/MainWindow.java
+++ b/src/dev/kske/chess/ui/MainWindow.java
@@ -1,7 +1,6 @@
package dev.kske.chess.ui;
import java.awt.Desktop;
-import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.dnd.DropTarget;
import java.io.File;
@@ -10,10 +9,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;
@@ -32,45 +28,36 @@ import dev.kske.chess.pgn.PGNGame;
*/
public class MainWindow extends JFrame {
- private static final long serialVersionUID = -3100939302567978977L;
+ private JTabbedPane tabbedPane = new JTabbedPane();
- private JTabbedPane tabbedPane;
+ private static final long serialVersionUID = -3100939302567978977L;
/**
* Launch the application.
+ *
+ * @param args command line arguments are ignored
*/
public static void main(String[] args) {
- EventQueue.invokeLater(() -> {
- try {
- new MainWindow();
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- });
+ SwingUtilities.invokeLater(MainWindow::new);
}
/**
* Create the application.
*/
- public MainWindow() {
+ private MainWindow() {
super("Chess by Kai S. K. Engelbart");
- initialize();
- }
- /**
- * Initialize the contents of the frame.
- */
- private void initialize() {
// Configure frame
setResizable(false);
setBounds(100, 100, 494, 565);
- setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("/pieces/queen_white.png")));
+ setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ setIconImage(
+ Toolkit.getDefaultToolkit()
+ .getImage(getClass().getResource("/pieces/queen_white.png"))
+ );
- // Add frame content
- tabbedPane = new JTabbedPane();
+ // Add tabbed pane, menu bar and drop target
getContentPane().add(tabbedPane);
-
setJMenuBar(new MenuBar(this));
new DropTarget(this, new GameDropTarget(this));
@@ -81,40 +68,64 @@ 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(); }
+ public GamePane getSelectedGamePane() {
+ return (GamePane) tabbedPane.getSelectedComponent();
+ }
/**
* 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)); }
+ public GamePane addGamePane() {
+ return addGamePane("Game " + (tabbedPane.getTabCount() + 1));
+ }
/**
* 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();
tabbedPane.add(title, gamePane);
- tabbedPane.setTabComponentAt(tabbedPane.getTabCount() - 1, new GameTabComponent(tabbedPane));
+ tabbedPane.setTabComponentAt(
+ tabbedPane.getTabCount() - 1,
+ new GameTabComponent(tabbedPane)
+ );
tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
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,
- (whiteName, blackName) -> {
- Game game = new Game(gamePane.getBoardPane(), whiteName, blackName, board);
- gamePane.setGame(game);
- game.start();
- });
+ DialogUtil.showGameConfigurationDialog(
+ this,
+ (whiteName, blackName) -> {
+ Game game = new Game(
+ gamePane.getBoardPane(),
+ whiteName,
+ blackName,
+ board
+ );
+ gamePane.setGame(game);
+ game.start();
+ }
+ );
return gamePane;
}
@@ -123,7 +134,9 @@ public class MainWindow extends JFrame {
*
* @param index The index of the {@link GamePane} to remove
*/
- public void removeGamePane(int index) { tabbedPane.remove(index); }
+ public void removeGamePane(int index) {
+ tabbedPane.remove(index);
+ }
/**
* Loads a game file (FEN or PGN) and adds it to a new {@link GamePane}.
@@ -132,65 +145,111 @@ public class MainWindow extends JFrame {
*/
public void loadFiles(List files) {
files.forEach(file -> {
- final String name = file.getName().substring(0, file.getName().lastIndexOf('.'));
- final String extension = file.getName().substring(file.getName().lastIndexOf('.')).toLowerCase();
+ final String name
+ = file.getName().substring(0, file.getName().lastIndexOf('.'));
+ final String extension = file.getName()
+ .substring(file.getName().lastIndexOf('.'))
+ .toLowerCase();
try {
Board board;
switch (extension) {
case ".fen":
- board = new FENString(new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)).getBoard();
+ board = new FENString(
+ new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)
+ ).getBoard();
break;
case ".pgn":
PGNDatabase pgnDB = new PGNDatabase();
pgnDB.load(file);
if (pgnDB.getGames().size() > 0) {
- String[] gameNames = new String[pgnDB.getGames().size()];
+ String[] gameNames
+ = new String[pgnDB.getGames().size()];
for (int i = 0; i < gameNames.length; i++) {
final PGNGame game = pgnDB.getGames().get(i);
- gameNames[i] = String.format("%s vs %s: %s", game.getTag("White"), game.getTag("Black"), game.getTag("Result"));
+ gameNames[i] = String.format(
+ "%s vs %s: %s",
+ game.getTag("White"),
+ game.getTag("Black"),
+ game.getTag("Result")
+ );
}
- JComboBox comboBox = new JComboBox<>(gameNames);
- JOptionPane.showMessageDialog(this, comboBox, "Select a game", JOptionPane.QUESTION_MESSAGE);
- board = pgnDB.getGames().get(comboBox.getSelectedIndex()).getBoard();
- } else throw new ChessException("The PGN database '" + name + "' is empty!");
+ JComboBox comboBox
+ = new JComboBox<>(gameNames);
+ JOptionPane.showMessageDialog(
+ this,
+ comboBox,
+ "Select a game",
+ JOptionPane.QUESTION_MESSAGE
+ );
+ board = pgnDB.getGames()
+ .get(comboBox.getSelectedIndex())
+ .getBoard();
+ } else
+ throw new ChessException(
+ "The PGN database '" + name + "' is empty!"
+ );
break;
default:
- throw new ChessException("The file extension '" + extension + "' is not supported!");
+ throw new ChessException(
+ "The file extension '" + extension
+ + "' is not supported!"
+ );
}
addGamePane(name, board);
} catch (Exception e) {
e.printStackTrace();
- JOptionPane.showMessageDialog(this,
- "Failed to load the file " + file.getName() + ": " + e.toString(),
- "File loading error",
- JOptionPane.ERROR_MESSAGE);
+ JOptionPane.showMessageDialog(
+ this,
+ "Failed to load the file " + file.getName() + ": "
+ + e.toString(),
+ "File loading error",
+ JOptionPane.ERROR_MESSAGE
+ );
}
});
}
+ /**
+ * 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();
+ final int dotIndex = file.getName().lastIndexOf('.');
+ final String extension
+ = file.getName().substring(dotIndex).toLowerCase();
- if (extension.equals(".pgn")) try {
- PGNGame pgnGame = new PGNGame(getSelectedGamePane().getGame().getBoard());
- pgnGame.setTag("Event", tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()));
- pgnGame.setTag("Result", "*");
- PGNDatabase pgnDB = new PGNDatabase();
- pgnDB.getGames().add(pgnGame);
- pgnDB.save(file);
+ if (extension.equals(".pgn"))
+ try {
+ PGNGame pgnGame
+ = new PGNGame(getSelectedGamePane().getGame().getBoard());
+ pgnGame.setTag(
+ "Event",
+ tabbedPane.getTitleAt(tabbedPane.getSelectedIndex())
+ );
+ pgnGame.setTag("Result", "*");
+ PGNDatabase pgnDB = new PGNDatabase();
+ pgnDB.getGames().add(pgnGame);
+ pgnDB.save(file);
- if (JOptionPane.showConfirmDialog(this,
- "Game export finished. Do you want to view the created file?",
- "Game export finished",
- JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
- Desktop.getDesktop().open(file);
- } catch (IOException e) {
- e.printStackTrace();
- JOptionPane.showMessageDialog(this,
- "Failed to save the file " + file.getName() + ": " + e.toString(),
+ if (
+ JOptionPane.showConfirmDialog(
+ this,
+ "Game export finished. Do you want to view the created file?",
+ "Game export finished",
+ JOptionPane.YES_NO_OPTION
+ ) == JOptionPane.YES_OPTION
+ )
+ Desktop.getDesktop().open(file);
+ } catch (IOException e) {
+ e.printStackTrace();
+ JOptionPane.showMessageDialog(
+ this,
+ "Failed to save the file " + file.getName() + ": " + e,
"File saving error",
- JOptionPane.ERROR_MESSAGE);
- }
+ JOptionPane.ERROR_MESSAGE
+ );
+ }
}
}
diff --git a/src/dev/kske/chess/ui/MenuBar.java b/src/dev/kske/chess/ui/MenuBar.java
index 3ea86cc..ac478e0 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;
@@ -41,25 +43,52 @@ public class MenuBar extends JMenuBar {
JMenu gameMenu = new JMenu("Game");
JMenuItem newGameMenuItem = new JMenuItem("New Game");
- newGameMenuItem.addActionListener((evt) -> DialogUtil.showGameConfigurationDialog(mainWindow, (whiteName, blackName) -> {
- GamePane gamePane = mainWindow.addGamePane();
- Game game = new Game(gamePane.getBoardPane(), whiteName, blackName);
- gamePane.setGame(game);
- game.start();
- }));
+ newGameMenuItem.addActionListener(
+ evt -> DialogUtil.showGameConfigurationDialog(
+ mainWindow,
+ (whiteName, blackName) -> {
+ GamePane gamePane = mainWindow.addGamePane();
+ Game game = new Game(
+ gamePane.getBoardPane(),
+ whiteName,
+ blackName
+ );
+ gamePane.setGame(game);
+ game.start();
+ }
+ )
+ );
gameMenu.add(newGameMenuItem);
JMenuItem loadFileMenu = new JMenuItem("Load game file");
- loadFileMenu.addActionListener((evt) -> DialogUtil
- .showFileSelectionDialog(mainWindow,
+ loadFileMenu.addActionListener(
+ evt -> DialogUtil
+ .showFileSelectionDialog(
+ mainWindow,
mainWindow::loadFiles,
- Arrays.asList(new FileNameExtensionFilter("FEN and PGN files", "fen", "pgn"))));
+ Arrays.asList(
+ new FileNameExtensionFilter(
+ "FEN and PGN files",
+ "fen",
+ "pgn"
+ )
+ )
+ )
+ );
gameMenu.add(loadFileMenu);
JMenuItem saveFileMenu = new JMenuItem("Save game file");
saveFileMenu
- .addActionListener((evt) -> DialogUtil
- .showFileSaveDialog(mainWindow, mainWindow::saveFile, Arrays.asList(new FileNameExtensionFilter("PGN file", "pgn"))));
+ .addActionListener(
+ evt -> DialogUtil
+ .showFileSaveDialog(
+ mainWindow,
+ mainWindow::saveFile,
+ Arrays.asList(
+ new FileNameExtensionFilter("PGN file", "pgn")
+ )
+ )
+ );
gameMenu.add(saveFileMenu);
add(gameMenu);
@@ -70,10 +99,16 @@ public class MenuBar extends JMenuBar {
JMenu engineMenu = new JMenu("Engine");
JMenuItem addEngineMenuItem = new JMenuItem("Add engine");
- addEngineMenuItem.addActionListener((evt) -> {
+ addEngineMenuItem.addActionListener(evt -> {
String enginePath = JOptionPane
- .showInputDialog(getParent(), "Enter the path to a UCI-compatible chess engine:", "Engine selection", JOptionPane.QUESTION_MESSAGE);
- if (enginePath != null) EngineUtil.addEngine(enginePath);
+ .showInputDialog(
+ getParent(),
+ "Enter the path to a UCI-compatible chess engine:",
+ "Engine selection",
+ JOptionPane.QUESTION_MESSAGE
+ );
+ if (enginePath != null)
+ EngineUtil.addEngine(enginePath);
});
JMenuItem showInfoMenuItem = new JMenuItem("Show engine info");
@@ -87,29 +122,50 @@ public class MenuBar extends JMenuBar {
JMenu toolsMenu = new JMenu("Tools");
JMenuItem exportFENMenuItem = new JMenuItem("Export board to FEN");
- exportFENMenuItem.addActionListener((evt) -> {
- final String fen = new FENString(mainWindow.getSelectedGamePane().getGame().getBoard()).toString();
- Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(fen), null);
- JOptionPane.showMessageDialog(mainWindow, String.format("FEN-string copied to clipboard!%n%s", fen));
+ exportFENMenuItem.addActionListener(evt -> {
+ final String fen = new FENString(
+ mainWindow.getSelectedGamePane().getGame().getBoard()
+ ).toString();
+ Toolkit.getDefaultToolkit()
+ .getSystemClipboard()
+ .setContents(new StringSelection(fen), null);
+ JOptionPane.showMessageDialog(
+ mainWindow,
+ String.format("FEN-string copied to clipboard!%n%s", fen)
+ );
});
toolsMenu.add(exportFENMenuItem);
JMenuItem loadFromFENMenuItem = new JMenuItem("Load board from FEN");
- loadFromFENMenuItem.addActionListener((evt) -> {
- final GamePane gamePane = mainWindow.addGamePane();
- final String fen = JOptionPane.showInputDialog("Enter a FEN string: ");
- DialogUtil.showGameConfigurationDialog(mainWindow, (whiteName, blackName) -> {
- Game game;
- try {
- game = new Game(gamePane.getBoardPane(), whiteName, blackName, new FENString(fen).getBoard());
- gamePane.setGame(game);
- game.start();
- } catch (ChessException e) {
- e.printStackTrace();
- JOptionPane
- .showMessageDialog(mainWindow, "Failed to load FEN string: " + e.toString(), "FEN loading error", JOptionPane.ERROR_MESSAGE);
+ loadFromFENMenuItem.addActionListener(evt -> {
+ final GamePane gamePane = mainWindow.addGamePane();
+ final String fen
+ = JOptionPane.showInputDialog("Enter a FEN string: ");
+ DialogUtil.showGameConfigurationDialog(
+ mainWindow,
+ (whiteName, blackName) -> {
+ Game game;
+ try {
+ game = new Game(
+ gamePane.getBoardPane(),
+ whiteName,
+ blackName,
+ new FENString(fen).getBoard()
+ );
+ gamePane.setGame(game);
+ game.start();
+ } catch (ChessException e) {
+ e.printStackTrace();
+ JOptionPane
+ .showMessageDialog(
+ mainWindow,
+ "Failed to load FEN string: " + e.toString(),
+ "FEN loading error",
+ JOptionPane.ERROR_MESSAGE
+ );
+ }
}
- });
+ );
});
toolsMenu.add(loadFromFENMenuItem);
diff --git a/src/dev/kske/chess/ui/MoveNodeRenderer.java b/src/dev/kske/chess/ui/MoveNodeRenderer.java
index f78bea5..7775e00 100644
--- a/src/dev/kske/chess/ui/MoveNodeRenderer.java
+++ b/src/dev/kske/chess/ui/MoveNodeRenderer.java
@@ -14,19 +14,26 @@ import dev.kske.chess.board.MoveNode;
* Project: Chess
* File: MoveNodeRenderer.java
* Created: 9 Oct 2019
- *
+ *
* @since Chess v0.5-alpha
* @author Kai S. K. Engelbart
*/
-public class MoveNodeRenderer extends JLabel implements ListCellRenderer {
+public class MoveNodeRenderer extends JLabel
+ implements ListCellRenderer {
private static final long serialVersionUID = 5242015788752442446L;
@Override
- public Component getListCellRendererComponent(JList extends MoveNode> list, MoveNode node, int index,
- boolean isSelected, boolean cellHasFocus) {
+ public Component getListCellRendererComponent(
+ JList extends MoveNode> list, MoveNode node, int index,
+ boolean isSelected, boolean cellHasFocus
+ ) {
setBorder(new EmptyBorder(5, 5, 5, 5));
- setText(node.move.toLAN());
+
+ int numVariations
+ = node.hasVariations() ? node.getVariations().size() : 0;
+ setText(String.format("%s (%d)", node.move.toLAN(), numVariations));
+
setBackground(isSelected ? Color.red : Color.white);
setOpaque(true);
return this;
diff --git a/src/dev/kske/chess/ui/OverlayComponent.java b/src/dev/kske/chess/ui/OverlayComponent.java
index cee5449..a933198 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
*/
@@ -30,9 +24,15 @@ public class OverlayComponent extends JComponent {
private final BoardPane boardPane;
- private List dots;
- private Move arrow;
+ 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,20 +43,36 @@ 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
if (arrow != null) {
- Point pos = new Point(arrow.getPos().x * tileSize + tileSize / 2, arrow.getPos().y * tileSize + tileSize / 2);
- Point dest = new Point(arrow.getDest().x * tileSize + tileSize / 2, arrow.getDest().y * tileSize + tileSize / 2);
+ Point pos = new Point(
+ arrow.getPos().x * tileSize + tileSize / 2,
+ arrow.getPos().y * tileSize + tileSize / 2
+ );
+ Point dest = new Point(
+ arrow.getDest().x * tileSize + tileSize / 2,
+ arrow.getDest().y * tileSize + tileSize / 2
+ );
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.yellow);
- g2d.drawRect(arrow.getPos().x * tileSize, arrow.getPos().y * tileSize, tileSize, tileSize);
- g2d.drawRect(arrow.getDest().x * tileSize, arrow.getDest().y * tileSize, tileSize, tileSize);
+ g2d.drawRect(
+ arrow.getPos().x * tileSize,
+ arrow.getPos().y * tileSize,
+ tileSize,
+ tileSize
+ );
+ g2d.drawRect(
+ arrow.getDest().x * tileSize,
+ arrow.getDest().y * tileSize,
+ tileSize,
+ tileSize
+ );
Shape arrowShape = createArrowShape(pos, dest);
g.setColor(new Color(255, 0, 0, 127));
@@ -64,16 +80,17 @@ public class OverlayComponent extends JComponent {
g2d.setColor(Color.black);
g2d.draw(arrowShape);
}
-
// Draw possible moves if a piece was selected
if (!dots.isEmpty()) {
g.setColor(Color.green);
int radius = tileSize / 4;
for (Position dot : dots)
- g.fillOval(dot.x * tileSize + tileSize / 2 - radius / 2,
- dot.y * tileSize + tileSize / 2 - radius / 2,
- radius,
- radius);
+ g.fillOval(
+ dot.x * tileSize + tileSize / 2 - radius / 2,
+ dot.y * tileSize + tileSize / 2 - radius / 2,
+ radius,
+ radius
+ );
}
}
@@ -89,10 +106,11 @@ public class OverlayComponent extends JComponent {
Point midPoint = midpoint(pos, dest);
- double rotate = Math.atan2(dest.y - pos.y, dest.x - pos.x);
- double ptDistance = pos.distance(dest);
- double scale = ptDistance / 12.0; // 12 because it's the length of the arrow
- // polygon.
+ double rotate = Math.atan2(dest.y - pos.y, dest.x - pos.x);
+ double ptDistance = pos.distance(dest);
+ double scale = ptDistance / 12.0; // 12 because it's the length of the
+ // arrow
+ // polygon.
AffineTransform transform = new AffineTransform();
@@ -104,29 +122,52 @@ public class OverlayComponent extends JComponent {
}
private Point midpoint(Point p1, Point p2) {
- return new Point((int) ((p1.x + p2.x) / 2.0), (int) ((p1.y + p2.y) / 2.0));
+ 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..795db7b 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() {
@@ -38,6 +37,9 @@ class BoardTest {
clone.getBoardArr()[0][0] = new Queen(Color.BLACK, clone);
clone.move(new Move(1, 1, 1, 2));
assertNotEquals(clone.getBoardArr()[0][0], board.getBoardArr()[0][0]);
- assertNotEquals(clone.getLog().getActiveColor(), board.getLog().getActiveColor());
+ assertNotEquals(
+ clone.getLog().getActiveColor(),
+ board.getLog().getActiveColor()
+ );
}
}
diff --git a/test/dev/kske/chess/board/FENStringTest.java b/test/dev/kske/chess/board/FENStringTest.java
index b2c1e43..70be9b7 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++)
@@ -35,7 +40,11 @@ class FENStringTest {
*/
@BeforeEach
void setUp() throws Exception {
- fenStrings.addAll(Arrays.asList("rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2"));
+ fenStrings.addAll(
+ Arrays.asList(
+ "rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2"
+ )
+ );
Board board = new Board();
board.set(Position.fromLAN("c7"), null);
board.set(Position.fromLAN("c5"), new Pawn(Color.BLACK, board));
@@ -51,22 +60,28 @@ class FENStringTest {
}
/**
- * Test method for {@link dev.kske.chess.board.FENString#toString()}.
+ * Test method for {@link FENString#toString()}.
*/
@Test
void testToString() {
for (int i = 0; i < fenStrings.size(); i++)
- assertEquals(fenStrings.get(i), new FENString(boards.get(i)).toString());
+ assertEquals(
+ fenStrings.get(i),
+ new FENString(boards.get(i)).toString()
+ );
}
/**
- * Test method for {@link dev.kske.chess.board.FENString#getBoard()}.
- *
+ * Test method for {@link FENString#getBoard()}.
+ *
* @throws ChessException
*/
@Test
void testGetBoard() throws ChessException {
for (int i = 0; i < boards.size(); i++)
- assertEquals(boards.get(i), new FENString(fenStrings.get(i)).getBoard());
+ assertEquals(
+ boards.get(i),
+ new FENString(fenStrings.get(i)).getBoard()
+ );
}
}
diff --git a/test/dev/kske/chess/board/LogTest.java b/test/dev/kske/chess/board/LogTest.java
index fafc6dc..83592e4 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() {
@@ -48,11 +44,14 @@ class LogTest {
other.add(Move.fromLAN("a2a4"), new Pawn(Color.WHITE, null), null);
other.add(Move.fromLAN("a4a5"), new Pawn(Color.WHITE, null), null);
assertNotEquals(log.getRoot(), other.getRoot());
- assertNotEquals(log.getRoot().getVariations(), other.getRoot().getVariations());
+ assertNotEquals(
+ log.getRoot().getVariations(),
+ other.getRoot().getVariations()
+ );
}
/**
- * 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 +59,7 @@ class LogTest {
}
/**
- * Test method for {@link dev.kske.chess.board.Log#removeLast()}.
+ * Test method for {@link Log#removeLast()}.
*/
@Test
void testRemoveLast() {
@@ -68,7 +67,7 @@ class LogTest {
}
/**
- * Test method for {@link dev.kske.chess.board.Log#isEmpty()}.
+ * Test method for {@link Log#isEmpty()}.
*/
@Test
void testIsEmpty() {
@@ -76,7 +75,7 @@ class LogTest {
}
/**
- * Test method for {@link dev.kske.chess.board.Log#reset()}.
+ * Test method for {@link Log#reset()}.
*/
@Test
void testReset() {
@@ -84,7 +83,7 @@ class LogTest {
}
/**
- * Test method for {@link dev.kske.chess.board.Log#getRoot()}.
+ * Test method for {@link Log#getRoot()}.
*/
@Test
void testGetRoot() {
@@ -92,7 +91,7 @@ class LogTest {
}
/**
- * Test method for {@link dev.kske.chess.board.Log#getLast()}.
+ * Test method for {@link Log#getLast()}.
*/
@Test
void testGetLast() {
@@ -100,7 +99,7 @@ class LogTest {
}
/**
- * Test method for {@link dev.kske.chess.board.Log#getEnPassant()}.
+ * Test method for {@link Log#getEnPassant()}.
*/
@Test
void testGetEnPassant() {
@@ -108,7 +107,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 +115,7 @@ class LogTest {
}
/**
- * Test method for {@link dev.kske.chess.board.Log#getActiveColor()}.
+ * Test method for {@link Log#getActiveColor()}.
*/
@Test
void testGetActiveColor() {
@@ -124,7 +123,8 @@ 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 +132,7 @@ class LogTest {
}
/**
- * Test method for {@link dev.kske.chess.board.Log#getFullmoveNumber()}.
+ * Test method for {@link Log#getFullmoveNumber()}.
*/
@Test
void testGetFullmoveCounter() {
@@ -140,7 +140,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 +148,7 @@ class LogTest {
}
/**
- * Test method for {@link dev.kske.chess.board.Log#getHalfmoveClock()}.
+ * Test method for {@link Log#getHalfmoveClock()}.
*/
@Test
void testGetHalfmoveClock() {
@@ -156,10 +156,10 @@ class LogTest {
}
/**
- * Test method for {@link dev.kske.chess.board.Log#setHalfmoveClock(int)}.
+ * Test method for {@link Log#setHalfmoveClock(int)}.
*/
@Test
void testSetHalfmoveClock() {
fail("Not yet implemented");
}
-}
\ No newline at end of file
+}
diff --git a/test/dev/kske/chess/board/PositionTest.java b/test/dev/kske/chess/board/PositionTest.java
index 5cb4a1b..3b63b0b 100644
--- a/test/dev/kske/chess/board/PositionTest.java
+++ b/test/dev/kske/chess/board/PositionTest.java
@@ -12,14 +12,23 @@ 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 +37,7 @@ class PositionTest {
}
/**
- * Test method for {@link dev.kske.chess.board.Position#toLAN()}.
+ * Test method for {@link Position#toLAN()}.
*/
@Test
void testToSAN() {
@@ -37,7 +46,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..bb8f0b5 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;
@@ -18,19 +16,18 @@ import dev.kske.chess.exception.ChessException;
class PGNDatabaseTest {
/**
- * Test method for {@link dev.kske.chess.pgn.PGNDatabase#load(java.io.File)}.
- *
- * @throws ChessException
- * @throws FileNotFoundException
+ * Test method for
+ * {@link dev.kske.chess.pgn.PGNDatabase#load(java.io.File)}.
+ *
+ * @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())
+ );
}
}