Add missing Javadoc
This commit is contained in:
		| @@ -7,12 +7,18 @@ import java.util.List; | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>Bishop.java</strong><br> | ||||
|  * Created: <strong>01.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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); | ||||
| 	} | ||||
|   | ||||
| @@ -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<Move> 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; | ||||
| 	} | ||||
|   | ||||
| @@ -4,10 +4,11 @@ package dev.kske.chess.board; | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>BoardState.java</strong><br> | ||||
|  * Created: <strong>07.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @since Chess v0.1-alpha | ||||
|  * @author Kai S. K. Engelbart | ||||
|  */ | ||||
| @SuppressWarnings("javadoc") | ||||
| public enum BoardState { | ||||
| 	CHECK, CHECKMATE, STALEMATE, NORMAL; | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -4,7 +4,7 @@ package dev.kske.chess.board; | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>EnPassant.java</strong><br> | ||||
|  * Created: <strong>2 Nov 2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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; } | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -7,12 +7,18 @@ import java.util.List; | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>King.java</strong><br> | ||||
|  * Created: <strong>01.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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) { | ||||
|   | ||||
| @@ -7,12 +7,18 @@ import java.util.List; | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>Knight.java</strong><br> | ||||
|  * Created: <strong>01.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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); | ||||
| 	} | ||||
|   | ||||
| @@ -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}.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>Log.java</strong><br> | ||||
|  * Created: <strong>09.07.2019</strong><br> | ||||
| @@ -23,6 +26,9 @@ public class Log implements Iterable<MoveNode> { | ||||
| 	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<MoveNode> { | ||||
| 	 */ | ||||
| 	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; } | ||||
| } | ||||
| @@ -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; } | ||||
| } | ||||
|   | ||||
| @@ -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<MoveNode>	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<MoveNode> 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); | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
|   | ||||
| @@ -19,6 +19,19 @@ public class PawnPromotion extends Move { | ||||
| 	private final Constructor<? extends Piece>	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<? extends Piece> 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<? extends Piece> promotionPiece) | ||||
| 	/** | ||||
| 	 * | ||||
| 	 * Creates an instance of {@link PawnPromotion}. | ||||
| 	 * | ||||
| 	 * @param xPos                the horizontal position of this move | ||||
| 	 * @param yPos                the vertical position of this move | ||||
| 	 * @param xDest               the horizontal destination of this move | ||||
| 	 * @param yDest               the vertical destination of this move | ||||
| 	 * @param promotionPieceClass the class of the piece to which the pawn is | ||||
| 	 *                            promoted | ||||
| 	 * @throws ReflectiveOperationException if the promotion piece could not be | ||||
| 	 *                                      instantiated | ||||
| 	 * @throws RuntimeException             if the promotion piece could not be | ||||
| 	 *                                      instantiated | ||||
| 	 */ | ||||
| 	public PawnPromotion(int xPos, int yPos, int xDest, int yDest, Class<? extends Piece> promotionPieceClass) | ||||
| 			throws ReflectiveOperationException, RuntimeException { | ||||
| 		this(new Position(xPos, yPos), new Position(xDest, yDest), promotionPiece); | ||||
| 		this(new Position(xPos, yPos), new Position(xDest, yDest), promotionPieceClass); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
|   | ||||
| @@ -5,10 +5,12 @@ import java.util.List; | ||||
| import java.util.Objects; | ||||
|  | ||||
| /** | ||||
|  * Represents a piece on a board with a color.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>Piece.java</strong><br> | ||||
|  * Created: <strong>01.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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<Move> getMoves(Position pos) { | ||||
| 		List<Move> moves = getPseudolegalMoves(pos); | ||||
| 		for (Iterator<Move> 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<Move> 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<? extends Piece> 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: <strong>Chess</strong><br> | ||||
| 	 * File: <strong>Piece.java</strong><br> | ||||
| 	 * Created: <strong>01.07.2019</strong><br> | ||||
| 	 * | ||||
| 	 * @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; } | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -4,31 +4,50 @@ package dev.kske.chess.board; | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>Position.java</strong><br> | ||||
|  * Created: <strong>02.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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() { | ||||
|   | ||||
| @@ -7,12 +7,18 @@ import java.util.List; | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>Queen.java</strong><br> | ||||
|  * Created: <strong>01.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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); | ||||
| 	} | ||||
|   | ||||
| @@ -7,12 +7,18 @@ import java.util.List; | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>Rook.java</strong><br> | ||||
|  * Created: <strong>01.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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); | ||||
| 	} | ||||
|   | ||||
| @@ -4,9 +4,10 @@ package dev.kske.chess.event; | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>Event.java</strong><br> | ||||
|  * Created: <strong>7 Aug 2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @since Chess v0.4-alpha | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @param <T> the type of the event's value | ||||
|  */ | ||||
| public interface Event<T> { | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,8 @@ import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * Dispatches {@link Event}s to various {@link Subscriber}s.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>EventBus.java</strong><br> | ||||
|  * Created: <strong>7 Aug 2019</strong><br> | ||||
| @@ -13,26 +15,38 @@ import java.util.List; | ||||
|  */ | ||||
| public class EventBus { | ||||
|  | ||||
| 	private List<Subscribable> subscribers; | ||||
| 	private List<Subscriber> 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<Subscribable> getSubscribers() { return subscribers; } | ||||
| 	/** | ||||
| 	 * @return a list of all registered subscribers | ||||
| 	 */ | ||||
| 	public List<Subscriber> getSubscribers() { return subscribers; } | ||||
| } | ||||
|   | ||||
| @@ -14,6 +14,11 @@ public class GameStartEvent implements Event<Game> { | ||||
|  | ||||
| 	private final Game game; | ||||
|  | ||||
| 	/** | ||||
| 	 * Creates an instance of {@link GameStartEvent}. | ||||
| 	 * | ||||
| 	 * @param source the game started | ||||
| 	 */ | ||||
| 	public GameStartEvent(Game source) { game = source; } | ||||
|  | ||||
| 	@Override | ||||
|   | ||||
| @@ -16,6 +16,12 @@ public class MoveEvent implements Event<Move> { | ||||
| 	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<Move> { | ||||
| 	@Override | ||||
| 	public Move getData() { return move; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the state of the board after the move | ||||
| 	 */ | ||||
| 	public BoardState getBoardState() { return boardState; } | ||||
| } | ||||
|   | ||||
| @@ -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.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>Subscribable.java</strong><br> | ||||
|  * Created: <strong>7 Aug 2019</strong><br> | ||||
| @@ -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. | ||||
| @@ -4,7 +4,7 @@ package dev.kske.chess.exception; | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>ChessException.java</strong><br> | ||||
|  * Created: <strong>22 Sep 2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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); | ||||
| 	} | ||||
|   | ||||
| @@ -23,7 +23,7 @@ import dev.kske.chess.ui.OverlayComponent; | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>Game.java</strong><br> | ||||
|  * Created: <strong>06.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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. | ||||
|   | ||||
| @@ -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.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>NaturalPlayer.java</strong><br> | ||||
|  * Created: <strong>06.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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<Move>	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<Move> comboBox = new JComboBox<Move>(selectedMoves.toArray(new Move[0])); | ||||
| 					JComboBox<Move> comboBox = new JComboBox<>(selectedMoves.toArray(new Move[0])); | ||||
| 					JOptionPane.showMessageDialog(overlayComponent, comboBox, "Select a promotion", JOptionPane.QUESTION_MESSAGE); | ||||
|  | ||||
| 					move = selectedMoves.get(comboBox.getSelectedIndex()); | ||||
|   | ||||
| @@ -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.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>Player.java</strong><br> | ||||
|  * Created: <strong>06.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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; } | ||||
| } | ||||
|   | ||||
| @@ -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.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>UCIPlayer.java</strong><br> | ||||
|  * Created: <strong>18.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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!"); } | ||||
| } | ||||
|   | ||||
| @@ -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: <strong>Chess</strong><br> | ||||
|  * File: <strong>AIPlayer.java</strong><br> | ||||
|  * Created: <strong>06.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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"; | ||||
|   | ||||
| @@ -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.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>MoveProcessor.java</strong><br> | ||||
|  * Created: <strong>08.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @since Chess v0.1-alpha | ||||
|  * @author Kai S. K. Engelbart | ||||
|  */ | ||||
| @@ -68,6 +62,16 @@ public class MoveProcessor implements Callable<ProcessingResult> { | ||||
| 						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<Move> rootMoves, Color color, int maxDepth, int alphaBetaThreshold) { | ||||
| 		this.board				= board; | ||||
| 		this.rootMoves			= rootMoves; | ||||
| @@ -105,9 +109,10 @@ public class MoveProcessor implements Callable<ProcessingResult> { | ||||
|  | ||||
| 	/** | ||||
| 	 * 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; | ||||
|   | ||||
| @@ -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. | ||||
|  * <br> | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>ProcessingResult.java</strong><br> | ||||
|  * Created: <strong>08.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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); } | ||||
| } | ||||
|   | ||||
| @@ -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: <strong>Chess</strong><br> | ||||
|  * File: <strong>MenuBar.java</strong><br> | ||||
|  * File: <strong>EngineUtil.java</strong><br> | ||||
|  * Created: <strong>23.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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.<br> | ||||
| 	 * <br> | ||||
| 	 * Project: <strong>Chess</strong><br> | ||||
| 	 * File: <strong>EngineUtil.java</strong><br> | ||||
| 	 * Created: <strong>23.07.2019</strong><br> | ||||
| 	 * | ||||
| 	 * @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<EngineInfo> getEngineInfos() { return engineInfos; } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|  * <br> | ||||
|  * 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<PGNGame> getGames() { return games; } | ||||
| } | ||||
|   | ||||
| @@ -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: <strong>Chess</strong><br> | ||||
| @@ -29,11 +23,27 @@ public class PGNGame { | ||||
| 	private final Map<String, String>	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; } | ||||
| } | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import dev.kske.chess.board.Move; | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>UCIHandle.java</strong><br> | ||||
|  * Created: <strong>18.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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); } | ||||
| } | ||||
|   | ||||
| @@ -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: <strong>Chess</strong><br> | ||||
|  * File: <strong>UCIInfo.java</strong><br> | ||||
|  * Created: <strong>28.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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<Move> moves = new ArrayList<>(); | ||||
| 					while (i < tokens.length && !params.contains(tokens[i])) | ||||
| 						moves.add(Move.fromLAN(tokens[i++])); | ||||
|   | ||||
| @@ -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: <strong>Chess</strong><br> | ||||
|  * File: <strong>UCIReceiver.java</strong><br> | ||||
|  * Created: <strong>19.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @since Chess v0.3-alpha | ||||
|  * @author Kai S. K. Engelbart | ||||
|  */ | ||||
| @@ -23,11 +20,19 @@ public class UCIReceiver implements Runnable { | ||||
|  | ||||
| 	private List<UCIListener> 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); } | ||||
| } | ||||
|   | ||||
| @@ -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: <strong>Chess</strong><br> | ||||
|  * File: <strong>AIConfigDialog.java</strong><br> | ||||
|  * Created: <strong>16.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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(); | ||||
| 		}); | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -5,10 +5,13 @@ import java.awt.Dimension; | ||||
| import javax.swing.JLayeredPane; | ||||
|  | ||||
| /** | ||||
|  * Combines a {@link BoardComponent} and an {@link OverlayComponent} into a | ||||
|  * layered pane. | ||||
|  * <br> | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>BoardPane.java</strong><br> | ||||
|  * Created: <strong>08.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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; } | ||||
| } | ||||
|   | ||||
| @@ -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<List<File>> action, Collection<FileNameExtensionFilter> 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<File> action, Collection<FileNameExtensionFilter> 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.<br> | ||||
| 	 * <br> | ||||
| 	 * 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<String, String> action) { | ||||
| 		JPanel dialogPanel = new JPanel(); | ||||
|  | ||||
|   | ||||
| @@ -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}.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>GameDropTarget.java</strong><br> | ||||
|  * Created: <strong>13 Aug 2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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") | ||||
|   | ||||
| @@ -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.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>GamePane.java</strong><br> | ||||
|  * Created: <strong>23.08.2019</strong><br> | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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}.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>Chess</strong><br> | ||||
|  * File: <strong>GameTabComponent.java</strong><br> | ||||
|  * Created: <strong>11 Dec 2019</strong><br> | ||||
| @@ -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"); | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -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: <strong>Chess</strong><br> | ||||
|  * File: <strong>MenuBar.java</strong><br> | ||||
|  * Created: <strong>16.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @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; | ||||
|  | ||||
|   | ||||
| @@ -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: <strong>Chess</strong><br> | ||||
|  * File: <strong>OverlayComponent.java</strong><br> | ||||
|  * Created: <strong>08.07.2019</strong><br> | ||||
|  *  | ||||
|  * | ||||
|  * @since Chess v0.1-alpha | ||||
|  * @author Kai S. K. Engelbart | ||||
|  */ | ||||
| @@ -33,6 +27,12 @@ public class OverlayComponent extends JComponent { | ||||
| 	private List<Position>	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<Position> 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(); } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user