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