Compare commits
	
		
			23 Commits
		
	
	
		
			v0.3-alpha
			...
			v0.4-alpha
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 54e4a0e2e4 | |||
| 1ecafa5485 | |||
| c987bfcebb | |||
| 3941a75c91 | |||
| 249480724a | |||
| 216877b76b | |||
| 19712a2bb7 | |||
| 08ac0ac114 | |||
| de9cd05602 | |||
| 3b73cd1efb | |||
| 964de02e24 | |||
| 76fa3859ef | |||
| c1a8589a04 | |||
| 358654b1ed | |||
| 8e2af63c35 | |||
| 642a0bf4d1 | |||
| 3ea48ef21b | |||
| d121e85897 | |||
| 14c7167ce4 | |||
| 90c100e0e1 | |||
| e7af9f40c2 | |||
| 3d8877ddbd | |||
| 83c6e48f03 | 
@@ -7,11 +7,7 @@
 | 
			
		||||
			<attribute name="test" value="true"/>
 | 
			
		||||
		</attributes>
 | 
			
		||||
	</classpathentry>
 | 
			
		||||
	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
 | 
			
		||||
		<attributes>
 | 
			
		||||
			<attribute name="module" value="true"/>
 | 
			
		||||
		</attributes>
 | 
			
		||||
	</classpathentry>
 | 
			
		||||
	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
 | 
			
		||||
	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
 | 
			
		||||
	<classpathentry kind="output" path="bin"/>
 | 
			
		||||
</classpath>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import dev.kske.chess.board.Log.LoggedMove;
 | 
			
		||||
import dev.kske.chess.board.Log.MoveNode;
 | 
			
		||||
import dev.kske.chess.board.Piece.Color;
 | 
			
		||||
import dev.kske.chess.board.Piece.Type;
 | 
			
		||||
 | 
			
		||||
@@ -15,12 +15,12 @@ import dev.kske.chess.board.Piece.Type;
 | 
			
		||||
 * Created: <strong>01.07.2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
public class Board implements Cloneable {
 | 
			
		||||
public class Board {
 | 
			
		||||
 | 
			
		||||
	private Piece[][]						boardArr;
 | 
			
		||||
	private Map<Color, Position>			kingPos;
 | 
			
		||||
	private Map<Color, Map<Type, Boolean>>	castlingRights;
 | 
			
		||||
	private Log								log;
 | 
			
		||||
	private Piece[][]						boardArr		= new Piece[8][8];
 | 
			
		||||
	private Map<Color, Position>			kingPos			= new HashMap<>();
 | 
			
		||||
	private Map<Color, Map<Type, Boolean>>	castlingRights	= new HashMap<>();
 | 
			
		||||
	private Log								log				= new Log();
 | 
			
		||||
 | 
			
		||||
	private static final Map<Type, int[][]> positionScores;
 | 
			
		||||
 | 
			
		||||
@@ -59,12 +59,44 @@ public class Board implements Cloneable {
 | 
			
		||||
						new int[] { 0, 1, 1, -2, -2, 1, 1, 0 }, new int[] { 0, 0, 0, 0, 0, 0, 0, 0 } });
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Initializes the board with the default chess starting position.
 | 
			
		||||
	 */
 | 
			
		||||
	public Board() {
 | 
			
		||||
		initDefaultPositions();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Initializes the board with data from a FEN-string.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param fen The FEN-string to initialize the board from
 | 
			
		||||
	 */
 | 
			
		||||
	public Board(String fen) {
 | 
			
		||||
		initFromFEN(fen);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates a copy of another {@link Board} instance.<br>
 | 
			
		||||
	 * The created object is a deep copy, but does not contain any move history
 | 
			
		||||
	 * apart from the current {@link MoveNode}.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param other The {@link Board} instance to copy
 | 
			
		||||
	 */
 | 
			
		||||
	public Board(Board other) {
 | 
			
		||||
		boardArr = new Piece[8][8];
 | 
			
		||||
		kingPos			= new HashMap<>();
 | 
			
		||||
		castlingRights	= new HashMap<>();
 | 
			
		||||
		log				= new Log();
 | 
			
		||||
		initializeDefaultPositions();
 | 
			
		||||
		for (int i = 0; i < 8; i++)
 | 
			
		||||
			for (int j = 0; j < 8; j++) {
 | 
			
		||||
				if (other.boardArr[i][j] == null) continue;
 | 
			
		||||
				boardArr[i][j]			= (Piece) other.boardArr[i][j].clone();
 | 
			
		||||
				boardArr[i][j].board	= this;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		kingPos.putAll(other.kingPos);
 | 
			
		||||
		Map<Type, Boolean> whiteCastling = new HashMap<>(other.castlingRights.get(Color.WHITE)),
 | 
			
		||||
				blackCastling = new HashMap<>(other.castlingRights.get(Color.BLACK));
 | 
			
		||||
		castlingRights.put(Color.WHITE, whiteCastling);
 | 
			
		||||
		castlingRights.put(Color.BLACK, blackCastling);
 | 
			
		||||
		log = new Log(other.log, false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
@@ -97,7 +129,6 @@ public class Board implements Cloneable {
 | 
			
		||||
	 * Moves a piece across the board without checking if the move is legal.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param move The move to execute
 | 
			
		||||
	 * @return The captures piece, or null if the move's destination was empty
 | 
			
		||||
	 */
 | 
			
		||||
	public void move(Move move) {
 | 
			
		||||
		Piece	piece			= getPos(move);
 | 
			
		||||
@@ -156,9 +187,9 @@ public class Board implements Cloneable {
 | 
			
		||||
	 * Reverts the last move.
 | 
			
		||||
	 */
 | 
			
		||||
	public void revert() {
 | 
			
		||||
		LoggedMove	loggedMove		= log.getLast();
 | 
			
		||||
		Move		move			= loggedMove.move;
 | 
			
		||||
		Piece		capturedPiece	= loggedMove.capturedPiece;
 | 
			
		||||
		MoveNode	moveNode		= log.getLast();
 | 
			
		||||
		Move		move			= moveNode.move;
 | 
			
		||||
		Piece		capturedPiece	= moveNode.capturedPiece;
 | 
			
		||||
 | 
			
		||||
		switch (move.type) {
 | 
			
		||||
			case PAWN_PROMOTION:
 | 
			
		||||
@@ -327,7 +358,7 @@ public class Board implements Cloneable {
 | 
			
		||||
	/**
 | 
			
		||||
	 * Initialized the board array with the default chess pieces and positions.
 | 
			
		||||
	 */
 | 
			
		||||
	public void initializeDefaultPositions() {
 | 
			
		||||
	public void initDefaultPositions() {
 | 
			
		||||
		// Initialize pawns
 | 
			
		||||
		for (int i = 0; i < 8; i++) {
 | 
			
		||||
			boardArr[i][1]	= new Pawn(Color.BLACK, this);
 | 
			
		||||
@@ -370,7 +401,6 @@ public class Board implements Cloneable {
 | 
			
		||||
				boardArr[i][j] = null;
 | 
			
		||||
 | 
			
		||||
		// Initialize castling rights
 | 
			
		||||
		castlingRights.clear();
 | 
			
		||||
		Map<Type, Boolean> whiteCastling = new HashMap<>(), blackCastling = new HashMap<>();
 | 
			
		||||
		whiteCastling.put(Type.KING, true);
 | 
			
		||||
		whiteCastling.put(Type.QUEEN, true);
 | 
			
		||||
@@ -383,34 +413,90 @@ public class Board implements Cloneable {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return A new instance of this class with a shallow copy of both
 | 
			
		||||
	 *         {@code kingPos} and {code boardArr}
 | 
			
		||||
	 * Initialized the board with a position specified in a FEN-encoded string.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param fen The FEN-encoded string representing target state of the board
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public Object clone() {
 | 
			
		||||
		Board board = null;
 | 
			
		||||
		try {
 | 
			
		||||
			board = (Board) super.clone();
 | 
			
		||||
		} catch (CloneNotSupportedException ex) {
 | 
			
		||||
			ex.printStackTrace();
 | 
			
		||||
	public void initFromFEN(String fen) {
 | 
			
		||||
		String[] parts = fen.split(" ");
 | 
			
		||||
		log.reset();
 | 
			
		||||
 | 
			
		||||
		// Piece placement (from white's perspective)
 | 
			
		||||
		String[] rows = parts[0].split("/");
 | 
			
		||||
		for (int i = 0; i < 8; i++) {
 | 
			
		||||
			char[] places = rows[i].toCharArray();
 | 
			
		||||
			for (int j = 0, k = 0; k < places.length; j++, k++) {
 | 
			
		||||
				if (Character.isDigit(places[k])) {
 | 
			
		||||
					for (int l = j; l < Character.digit(places[k], 10); l++, j++)
 | 
			
		||||
						boardArr[j][i] = null;
 | 
			
		||||
					--j;
 | 
			
		||||
				} else {
 | 
			
		||||
					Color color = Character.isUpperCase(places[k]) ? Color.WHITE : Color.BLACK;
 | 
			
		||||
					switch (Character.toLowerCase(places[k])) {
 | 
			
		||||
						case 'k':
 | 
			
		||||
							boardArr[j][i] = new King(color, this);
 | 
			
		||||
							kingPos.put(color, new Position(j, i));
 | 
			
		||||
							break;
 | 
			
		||||
						case 'q':
 | 
			
		||||
							boardArr[j][i] = new Queen(color, this);
 | 
			
		||||
							break;
 | 
			
		||||
						case 'r':
 | 
			
		||||
							boardArr[j][i] = new Rook(color, this);
 | 
			
		||||
							break;
 | 
			
		||||
						case 'n':
 | 
			
		||||
							boardArr[j][i] = new Knight(color, this);
 | 
			
		||||
							break;
 | 
			
		||||
						case 'b':
 | 
			
		||||
							boardArr[j][i] = new Bishop(color, this);
 | 
			
		||||
							break;
 | 
			
		||||
						case 'p':
 | 
			
		||||
							boardArr[j][i] = new Pawn(color, this);
 | 
			
		||||
							break;
 | 
			
		||||
						default:
 | 
			
		||||
							System.err.printf("Unknown character '%c' in board declaration of FEN string '%s'%n",
 | 
			
		||||
									places[k],
 | 
			
		||||
									fen);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		board.boardArr = new Piece[8][8];
 | 
			
		||||
		for (int i = 0; i < 8; i++)
 | 
			
		||||
			for (int j = 0; j < 8; j++) {
 | 
			
		||||
				if (boardArr[i][j] == null) continue;
 | 
			
		||||
				board.boardArr[i][j]		= (Piece) boardArr[i][j].clone();
 | 
			
		||||
				board.boardArr[i][j].board	= board;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		board.kingPos = new HashMap<>();
 | 
			
		||||
		board.kingPos.putAll(kingPos);
 | 
			
		||||
		board.log = (Log) log.clone();
 | 
			
		||||
		// Active color
 | 
			
		||||
		log.setActiveColor(Color.fromFirstChar(parts[1].charAt(0)));
 | 
			
		||||
 | 
			
		||||
		return board;
 | 
			
		||||
		// Castling rights
 | 
			
		||||
		Map<Type, Boolean> whiteCastling = new HashMap<>(), blackCastling = new HashMap<>();
 | 
			
		||||
		for (char c : parts[2].toCharArray())
 | 
			
		||||
			switch (c) {
 | 
			
		||||
				case 'K':
 | 
			
		||||
					whiteCastling.put(Type.KING, true);
 | 
			
		||||
				case 'Q':
 | 
			
		||||
					whiteCastling.put(Type.QUEEN, true);
 | 
			
		||||
				case 'k':
 | 
			
		||||
					blackCastling.put(Type.KING, true);
 | 
			
		||||
				case 'q':
 | 
			
		||||
					blackCastling.put(Type.QUEEN, true);
 | 
			
		||||
				case '-':
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					System.err
 | 
			
		||||
						.printf("Unknown character '%c' in castling rights declaration of FEN string '%s'", c, fen);
 | 
			
		||||
			}
 | 
			
		||||
		castlingRights.put(Color.WHITE, whiteCastling);
 | 
			
		||||
		castlingRights.put(Color.BLACK, blackCastling);
 | 
			
		||||
 | 
			
		||||
		// En passant availability
 | 
			
		||||
		if (!parts[3].equals("-")) log.setEnPassant(Position.fromSAN(parts[3]));
 | 
			
		||||
 | 
			
		||||
		// Halfmove clock
 | 
			
		||||
		log.setHalfmoveClock(Integer.parseInt(parts[4]));
 | 
			
		||||
 | 
			
		||||
		// Fullmove counter
 | 
			
		||||
		log.setFullmoveCounter(Integer.parseInt(parts[5]));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return A FEN string representing the board
 | 
			
		||||
	 * @return a FEN-encoded string representing the board
 | 
			
		||||
	 */
 | 
			
		||||
	public String toFEN() {
 | 
			
		||||
		StringBuilder sb = new StringBuilder();
 | 
			
		||||
@@ -418,38 +504,17 @@ public class Board implements Cloneable {
 | 
			
		||||
		// Piece placement (from white's perspective)
 | 
			
		||||
		for (int i = 0; i < 8; i++) {
 | 
			
		||||
			int emptyCount = 0;
 | 
			
		||||
			for (int j = 0; j < 8; j++)
 | 
			
		||||
				if (boardArr[j][i] == null) ++emptyCount;
 | 
			
		||||
			for (int j = 0; j < 8; j++) {
 | 
			
		||||
				final Piece piece = boardArr[j][i];
 | 
			
		||||
				if (piece == null) ++emptyCount;
 | 
			
		||||
				else {
 | 
			
		||||
					if (emptyCount != 0) {
 | 
			
		||||
						sb.append(emptyCount);
 | 
			
		||||
						emptyCount = 0;
 | 
			
		||||
					}
 | 
			
		||||
					char piece;
 | 
			
		||||
					switch (boardArr[j][i].getType()) {
 | 
			
		||||
						case KING:
 | 
			
		||||
							piece = 'K';
 | 
			
		||||
							break;
 | 
			
		||||
						case QUEEN:
 | 
			
		||||
							piece = 'Q';
 | 
			
		||||
							break;
 | 
			
		||||
						case ROOK:
 | 
			
		||||
							piece = 'R';
 | 
			
		||||
							break;
 | 
			
		||||
						case KNIGHT:
 | 
			
		||||
							piece = 'N';
 | 
			
		||||
							break;
 | 
			
		||||
						case BISHOP:
 | 
			
		||||
							piece = 'B';
 | 
			
		||||
							break;
 | 
			
		||||
						case PAWN:
 | 
			
		||||
							piece = 'P';
 | 
			
		||||
							break;
 | 
			
		||||
						default:
 | 
			
		||||
							piece = '-';
 | 
			
		||||
					char p = boardArr[j][i].getType().firstChar();
 | 
			
		||||
					sb.append(piece.getColor() == Color.WHITE ? Character.toUpperCase(p) : p);
 | 
			
		||||
				}
 | 
			
		||||
					if (boardArr[j][i].getColor() == Color.BLACK) piece = Character.toLowerCase(piece);
 | 
			
		||||
					sb.append(piece);
 | 
			
		||||
			}
 | 
			
		||||
			if (emptyCount != 0) sb.append(emptyCount);
 | 
			
		||||
			if (i < 7) sb.append('/');
 | 
			
		||||
@@ -458,6 +523,7 @@ public class Board implements Cloneable {
 | 
			
		||||
		// Active color
 | 
			
		||||
		sb.append(" " + log.getActiveColor().firstChar());
 | 
			
		||||
 | 
			
		||||
		// Castling Rights
 | 
			
		||||
		sb.append(' ');
 | 
			
		||||
		StringBuilder castlingSb = new StringBuilder();
 | 
			
		||||
		if (castlingRights.get(Color.WHITE).get(Type.KING)) castlingSb.append('K');
 | 
			
		||||
@@ -467,9 +533,9 @@ public class Board implements Cloneable {
 | 
			
		||||
		if (castlingSb.length() == 0) sb.append("-");
 | 
			
		||||
		sb.append(castlingSb);
 | 
			
		||||
 | 
			
		||||
		final LoggedMove lastMove = log.getLast();
 | 
			
		||||
		final MoveNode lastMove = log.getLast();
 | 
			
		||||
 | 
			
		||||
		// En passant availabillity
 | 
			
		||||
		// En passant availability
 | 
			
		||||
		sb.append(" " + (lastMove == null || lastMove.enPassant == null ? "-" : lastMove.enPassant.toSAN()));
 | 
			
		||||
 | 
			
		||||
		// Halfmove clock
 | 
			
		||||
@@ -481,26 +547,56 @@ public class Board implements Cloneable {
 | 
			
		||||
		return sb.toString();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param pos The position from which to return a piece
 | 
			
		||||
	 * @return The piece at the position
 | 
			
		||||
	 */
 | 
			
		||||
	public Piece get(Position pos) {
 | 
			
		||||
		return boardArr[pos.x][pos.y];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Places a piece at a position.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param pos   The position to place the piece at
 | 
			
		||||
	 * @param piece The piece to place
 | 
			
		||||
	 */
 | 
			
		||||
	public void set(Position pos, Piece piece) {
 | 
			
		||||
		boardArr[pos.x][pos.y] = piece;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param move The move from which position to return a piece
 | 
			
		||||
	 * @return The piece at the position of the move
 | 
			
		||||
	 */
 | 
			
		||||
	public Piece getPos(Move move) {
 | 
			
		||||
		return get(move.pos);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param move The move from which destination to return a piece
 | 
			
		||||
	 * @return The piece at the destination of the move
 | 
			
		||||
	 */
 | 
			
		||||
	public Piece getDest(Move move) {
 | 
			
		||||
		return get(move.dest);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Places a piece at the position of a move.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param move  The move at which position to place the piece
 | 
			
		||||
	 * @param piece The piece to place
 | 
			
		||||
	 */
 | 
			
		||||
	public void setPos(Move move, Piece piece) {
 | 
			
		||||
		set(move.pos, piece);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Places a piece at the destination of a move.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param move  The move at which destination to place the piece
 | 
			
		||||
	 * @param piece The piece to place
 | 
			
		||||
	 */
 | 
			
		||||
	public void setDest(Move move, Piece piece) {
 | 
			
		||||
		set(move.dest, piece);
 | 
			
		||||
	}
 | 
			
		||||
@@ -511,12 +607,7 @@ public class Board implements Cloneable {
 | 
			
		||||
	public Piece[][] getBoardArr() { return boardArr; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return The active color for the next move
 | 
			
		||||
	 * @return The move log
 | 
			
		||||
	 */
 | 
			
		||||
	public Color getActiveColor() { return log.getActiveColor(); }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return The current en passant square, or null if there isn't any
 | 
			
		||||
	 */
 | 
			
		||||
	public Position getEnPassantSquare() { return log.getLast() == null ? null : log.getLast().enPassant; }
 | 
			
		||||
	public Log getLog() { return log; }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,77 +11,190 @@ import dev.kske.chess.board.Piece.Color;
 | 
			
		||||
 * Created: <strong>09.07.2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
public class Log implements Cloneable {
 | 
			
		||||
public class Log {
 | 
			
		||||
 | 
			
		||||
	private List<LoggedMove>	moves;
 | 
			
		||||
	private MoveNode root, current;
 | 
			
		||||
 | 
			
		||||
	private Position	enPassant;
 | 
			
		||||
	private Color		activeColor;
 | 
			
		||||
	private int			fullmoveCounter, halfmoveClock;
 | 
			
		||||
 | 
			
		||||
	public Log() {
 | 
			
		||||
		moves		= new ArrayList<>();
 | 
			
		||||
		activeColor	= Color.WHITE;
 | 
			
		||||
		reset();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void add(Move move, Piece capturedPiece, boolean pawnMove) {
 | 
			
		||||
		// En passant availability
 | 
			
		||||
		Position enPassant = null;
 | 
			
		||||
		if (pawnMove && move.yDist == 2) enPassant = new Position(move.pos.x, move.pos.y + move.ySign);
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates a (partially deep) copy of another {@link Log} instance which begins
 | 
			
		||||
	 * with the current {@link MoveNode}.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param other          The {@link Log} instance to copy
 | 
			
		||||
	 * @param copyVariations If set to {@code true}, subsequent variations of the
 | 
			
		||||
	 *                       current {@link MoveNode} are copied with the
 | 
			
		||||
	 *                       {@link Log}
 | 
			
		||||
	 */
 | 
			
		||||
	public Log(Log other, boolean copyVariations) {
 | 
			
		||||
		enPassant		= other.enPassant;
 | 
			
		||||
		activeColor		= other.activeColor;
 | 
			
		||||
		fullmoveCounter	= other.fullmoveCounter;
 | 
			
		||||
		halfmoveClock	= other.halfmoveClock;
 | 
			
		||||
 | 
			
		||||
		// Fullmove counter and halfmove clock
 | 
			
		||||
		int fullmoveCounter, halfmoveClock;
 | 
			
		||||
		if (moves.isEmpty()) {
 | 
			
		||||
		// The new root is the current node of the copied instance
 | 
			
		||||
		if (!other.isEmpty()) {
 | 
			
		||||
			root		= new MoveNode(other.current, copyVariations);
 | 
			
		||||
			root.parent	= null;
 | 
			
		||||
			current		= root;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Adds a move to the move history and adjusts the log to the new position.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param move          The move to log
 | 
			
		||||
	 * @param capturedPiece The piece captured with the move
 | 
			
		||||
	 * @param pawnMove      {@code true} if the move was made by a pawn
 | 
			
		||||
	 */
 | 
			
		||||
	public void add(Move move, Piece capturedPiece, boolean pawnMove) {
 | 
			
		||||
		enPassant = pawnMove && move.yDist == 2 ? new Position(move.pos.x, move.pos.y + move.ySign) : null;
 | 
			
		||||
		if (activeColor == Color.BLACK) ++fullmoveCounter;
 | 
			
		||||
		if (pawnMove || capturedPiece != null) halfmoveClock = 0;
 | 
			
		||||
		else++halfmoveClock;
 | 
			
		||||
		activeColor = activeColor.opposite();
 | 
			
		||||
		final MoveNode leaf = new MoveNode(move, capturedPiece, enPassant, activeColor, fullmoveCounter, halfmoveClock);
 | 
			
		||||
 | 
			
		||||
		if (isEmpty()) {
 | 
			
		||||
			root	= leaf;
 | 
			
		||||
			current	= leaf;
 | 
			
		||||
		} else {
 | 
			
		||||
			current.addVariation(leaf);
 | 
			
		||||
			current = leaf;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Removed the last move from the log and adjusts its state to the previous
 | 
			
		||||
	 * move.
 | 
			
		||||
	 */
 | 
			
		||||
	public void removeLast() {
 | 
			
		||||
		if (!isEmpty() && current.parent != null) {
 | 
			
		||||
			current.parent.variations.remove(current);
 | 
			
		||||
			current			= current.parent;
 | 
			
		||||
			activeColor		= current.activeColor;
 | 
			
		||||
			enPassant		= current.enPassant;
 | 
			
		||||
			fullmoveCounter	= current.fullmoveCounter;
 | 
			
		||||
			halfmoveClock	= current.halfmoveClock;
 | 
			
		||||
		} else reset();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public boolean isEmpty() { return root == null; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Reverts the log to its initial state corresponding to the default board
 | 
			
		||||
	 * position.
 | 
			
		||||
	 */
 | 
			
		||||
	public void reset() {
 | 
			
		||||
		root			= null;
 | 
			
		||||
		current			= null;
 | 
			
		||||
		enPassant		= null;
 | 
			
		||||
		activeColor		= Color.WHITE;
 | 
			
		||||
		fullmoveCounter	= 1;
 | 
			
		||||
		halfmoveClock	= 0;
 | 
			
		||||
		} else {
 | 
			
		||||
			fullmoveCounter = getLast().fullmoveCounter;
 | 
			
		||||
			if (activeColor == Color.BLACK) ++fullmoveCounter;
 | 
			
		||||
			halfmoveClock = capturedPiece != null || pawnMove ? 0 : getLast().halfmoveClock + 1;
 | 
			
		||||
		}
 | 
			
		||||
		activeColor = activeColor.opposite();
 | 
			
		||||
		moves.add(new LoggedMove(move, capturedPiece, enPassant, fullmoveCounter, halfmoveClock));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public LoggedMove getLast() { return moves.isEmpty() ? null : moves.get(moves.size() - 1); }
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return The first logged move, or {@code null} if there is none
 | 
			
		||||
	 */
 | 
			
		||||
	public MoveNode getRoot() { return root; }
 | 
			
		||||
 | 
			
		||||
	public void removeLast() {
 | 
			
		||||
		if (!moves.isEmpty()) {
 | 
			
		||||
			activeColor = activeColor.opposite();
 | 
			
		||||
			moves.remove(moves.size() - 1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return the last logged move, or {@code null} if there is none
 | 
			
		||||
	 */
 | 
			
		||||
	public MoveNode getLast() { return current; }
 | 
			
		||||
 | 
			
		||||
	public void reset() {
 | 
			
		||||
		moves.clear();
 | 
			
		||||
		activeColor = Color.WHITE;
 | 
			
		||||
	}
 | 
			
		||||
	public Position getEnPassant() { return enPassant; }
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Object clone() {
 | 
			
		||||
		Log log = null;
 | 
			
		||||
		try {
 | 
			
		||||
			log = (Log) super.clone();
 | 
			
		||||
		} catch (CloneNotSupportedException e) {
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
		}
 | 
			
		||||
		log.moves = new ArrayList<>();
 | 
			
		||||
		log.moves.addAll(this.moves);
 | 
			
		||||
		return log;
 | 
			
		||||
	}
 | 
			
		||||
	public void setEnPassant(Position enPassant) { this.enPassant = enPassant; }
 | 
			
		||||
 | 
			
		||||
	public Color getActiveColor() { return activeColor; }
 | 
			
		||||
 | 
			
		||||
	public static class LoggedMove {
 | 
			
		||||
	public void setActiveColor(Color activeColor) { this.activeColor = activeColor; }
 | 
			
		||||
 | 
			
		||||
	public int getFullmoveCounter() { return fullmoveCounter; }
 | 
			
		||||
 | 
			
		||||
	public void setFullmoveCounter(int fullmoveCounter) { this.fullmoveCounter = fullmoveCounter; }
 | 
			
		||||
 | 
			
		||||
	public int getHalfmoveClock() { return halfmoveClock; }
 | 
			
		||||
 | 
			
		||||
	public void setHalfmoveClock(int halfmoveClock) { this.halfmoveClock = halfmoveClock; }
 | 
			
		||||
 | 
			
		||||
	public static class MoveNode {
 | 
			
		||||
 | 
			
		||||
		public final Move		move;
 | 
			
		||||
		public final Piece		capturedPiece;
 | 
			
		||||
		public final Position	enPassant;
 | 
			
		||||
		public final Color		activeColor;
 | 
			
		||||
		public final int		fullmoveCounter, halfmoveClock;
 | 
			
		||||
 | 
			
		||||
		public LoggedMove(Move move, Piece capturedPiece, Position enPassant, int fullmoveCounter, int halfmoveClock) {
 | 
			
		||||
		private MoveNode		parent;
 | 
			
		||||
		private List<MoveNode>	variations;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Creates a new {@link MoveNode}.
 | 
			
		||||
		 * 
 | 
			
		||||
		 * @param move            The logged {@link Move}
 | 
			
		||||
		 * @param capturedPiece   The {@link Piece} captures by the logged {@link Move}
 | 
			
		||||
		 * @param enPassant       The en passant {@link Position} valid after the logged
 | 
			
		||||
		 *                        {@link Move}, or {@code null} if there is none
 | 
			
		||||
		 * @param activeColor     The {@link Color} active after the logged {@link Move}
 | 
			
		||||
		 * @param fullmoveCounter
 | 
			
		||||
		 * @param halfmoveClock
 | 
			
		||||
		 */
 | 
			
		||||
		public MoveNode(Move move, Piece capturedPiece, Position enPassant, Color activeColor, int fullmoveCounter,
 | 
			
		||||
				int halfmoveClock) {
 | 
			
		||||
			this.move				= move;
 | 
			
		||||
			this.capturedPiece		= capturedPiece;
 | 
			
		||||
			this.enPassant			= enPassant;
 | 
			
		||||
			this.activeColor		= activeColor;
 | 
			
		||||
			this.fullmoveCounter	= fullmoveCounter;
 | 
			
		||||
			this.halfmoveClock		= halfmoveClock;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Creates a (deep) copy of another {@link MoveNode}.
 | 
			
		||||
		 * 
 | 
			
		||||
		 * @param other          The {@link MoveNode} to copy
 | 
			
		||||
		 * @param copyVariations When this is set to {@code true} a deep copy is
 | 
			
		||||
		 *                       created, which
 | 
			
		||||
		 *                       considers subsequent variations
 | 
			
		||||
		 */
 | 
			
		||||
		public MoveNode(MoveNode other, boolean copyVariations) {
 | 
			
		||||
			this(other.move, other.capturedPiece, other.enPassant, other.activeColor, other.fullmoveCounter,
 | 
			
		||||
					other.halfmoveClock);
 | 
			
		||||
			if (copyVariations && other.variations != null) {
 | 
			
		||||
				if (variations == null) variations = new ArrayList<>();
 | 
			
		||||
				other.variations.forEach(variation -> {
 | 
			
		||||
					MoveNode copy = new MoveNode(variation, true);
 | 
			
		||||
					copy.parent = this;
 | 
			
		||||
					variations.add(copy);
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Adds another {@link MoveNode} as a child node.
 | 
			
		||||
		 * 
 | 
			
		||||
		 * @param variation The {@link MoveNode} to append to this {@link MoveNode}
 | 
			
		||||
		 */
 | 
			
		||||
		public void addVariation(MoveNode variation) {
 | 
			
		||||
			if (variations == null) variations = new ArrayList<>();
 | 
			
		||||
			if (!variations.contains(variation)) {
 | 
			
		||||
				variations.add(variation);
 | 
			
		||||
				variation.parent = this;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @return A list of all variations associated with this {@link MoveNode}
 | 
			
		||||
		 */
 | 
			
		||||
		public List<MoveNode> getVariations() { return variations; }
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -35,6 +35,10 @@ public class Move {
 | 
			
		||||
				Position.fromSAN(move.substring(2)));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public String toSAN() {
 | 
			
		||||
		return pos.toSAN() + dest.toSAN();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public boolean isHorizontal() { return yDist == 0; }
 | 
			
		||||
 | 
			
		||||
	public boolean isVertical() { return xDist == 0; }
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ public class Pawn extends Piece {
 | 
			
		||||
			move.type = Move.Type.PAWN_PROMOTION;
 | 
			
		||||
 | 
			
		||||
		// Mark the move as en passant if necessary
 | 
			
		||||
		if (strafe && move.dest.equals(board.getEnPassantSquare())) {
 | 
			
		||||
		if (strafe && move.dest.equals(board.getLog().getEnPassant())) {
 | 
			
		||||
			enPassant	= true;
 | 
			
		||||
			move.type	= Move.Type.EN_PASSANT;
 | 
			
		||||
		}
 | 
			
		||||
@@ -84,8 +84,8 @@ public class Pawn extends Piece {
 | 
			
		||||
			moves.parallelStream().forEach(m -> m.type = Move.Type.PAWN_PROMOTION);
 | 
			
		||||
 | 
			
		||||
		// Add en passant move if necessary
 | 
			
		||||
		if (board.getEnPassantSquare() != null) {
 | 
			
		||||
			Move move = new Move(pos, board.getEnPassantSquare(), Move.Type.EN_PASSANT);
 | 
			
		||||
		if (board.getLog().getEnPassant() != null) {
 | 
			
		||||
			Move move = new Move(pos, board.getLog().getEnPassant(), Move.Type.EN_PASSANT);
 | 
			
		||||
			if (move.isDiagonal() && move.xDist == 1) moves.add(move);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -85,18 +85,29 @@ public abstract class Piece implements Cloneable {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static enum Type {
 | 
			
		||||
		KING, QUEEN, ROOK, KNIGHT, BISHOP, PAWN
 | 
			
		||||
		KING, QUEEN, ROOK, KNIGHT, BISHOP, PAWN;
 | 
			
		||||
		
 | 
			
		||||
		/**
 | 
			
		||||
		 * @return The first character of this {@link Type} in algebraic notation and lower case
 | 
			
		||||
		 */
 | 
			
		||||
		public char firstChar() {
 | 
			
		||||
			return this == KNIGHT ? 'n' : Character.toLowerCase(this.toString().charAt(0));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static enum Color {
 | 
			
		||||
		WHITE, BLACK;
 | 
			
		||||
 | 
			
		||||
		public Color opposite() {
 | 
			
		||||
			return this == WHITE ? BLACK : WHITE;
 | 
			
		||||
		public static Color fromFirstChar(char c) {
 | 
			
		||||
			return Character.toLowerCase(c) == 'w' ? WHITE : BLACK;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public char firstChar() {
 | 
			
		||||
			return this == WHITE ? 'w' : 'b';
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public Color opposite() {
 | 
			
		||||
			return this == WHITE ? BLACK : WHITE;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								src/dev/kske/chess/event/Event.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/dev/kske/chess/event/Event.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
package dev.kske.chess.event;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>Chess</strong><br>
 | 
			
		||||
 * File: <strong>Event.java</strong><br>
 | 
			
		||||
 * Created: <strong>7 Aug 2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
public interface Event<T> {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return The data associated with the event
 | 
			
		||||
	 */
 | 
			
		||||
	T getData();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								src/dev/kske/chess/event/EventBus.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/dev/kske/chess/event/EventBus.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
package dev.kske.chess.event;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>Chess</strong><br>
 | 
			
		||||
 * File: <strong>EventBus.java</strong><br>
 | 
			
		||||
 * Created: <strong>7 Aug 2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
public class EventBus {
 | 
			
		||||
 | 
			
		||||
	private List<Subscribable> subscribers;
 | 
			
		||||
 | 
			
		||||
	private static EventBus instance;
 | 
			
		||||
 | 
			
		||||
	public static EventBus getInstance() {
 | 
			
		||||
		if (instance == null) instance = new EventBus();
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private EventBus() {
 | 
			
		||||
		subscribers = new ArrayList<>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void register(Subscribable subscribable) {
 | 
			
		||||
		subscribers.add(subscribable);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								src/dev/kske/chess/event/MoveEvent.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/dev/kske/chess/event/MoveEvent.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
package dev.kske.chess.event;
 | 
			
		||||
 | 
			
		||||
import dev.kske.chess.board.Move;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>Chess</strong><br>
 | 
			
		||||
 * File: <strong>MoveEvent.java</strong><br>
 | 
			
		||||
 * Created: <strong>7 Aug 2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
public class MoveEvent implements Event<Move> {
 | 
			
		||||
 | 
			
		||||
	private final Move move;
 | 
			
		||||
 | 
			
		||||
	public MoveEvent(Move move) {
 | 
			
		||||
		this.move = move;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Move getData() { return move; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								src/dev/kske/chess/event/Subscribable.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/dev/kske/chess/event/Subscribable.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
package dev.kske.chess.event;
 | 
			
		||||
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>Chess</strong><br>
 | 
			
		||||
 * File: <strong>Subscribable.java</strong><br>
 | 
			
		||||
 * Created: <strong>7 Aug 2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
public interface Subscribable {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Consumes an event dispatched by an event bus.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param event The event dispatched by the event bus, only of supported type
 | 
			
		||||
	 */
 | 
			
		||||
	void handle(Event<?> event);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return A set of classes this class is supposed to handle in events
 | 
			
		||||
	 */
 | 
			
		||||
	Set<Class<?>> supports();
 | 
			
		||||
}
 | 
			
		||||
@@ -9,6 +9,8 @@ import dev.kske.chess.board.Board;
 | 
			
		||||
import dev.kske.chess.board.GameState;
 | 
			
		||||
import dev.kske.chess.board.Move;
 | 
			
		||||
import dev.kske.chess.board.Piece.Color;
 | 
			
		||||
import dev.kske.chess.event.EventBus;
 | 
			
		||||
import dev.kske.chess.event.MoveEvent;
 | 
			
		||||
import dev.kske.chess.game.ai.AIPlayer;
 | 
			
		||||
import dev.kske.chess.ui.BoardComponent;
 | 
			
		||||
import dev.kske.chess.ui.BoardPane;
 | 
			
		||||
@@ -24,18 +26,29 @@ import dev.kske.chess.ui.OverlayComponent;
 | 
			
		||||
 */
 | 
			
		||||
public class Game {
 | 
			
		||||
 | 
			
		||||
	private Map<Color, Player>	players;
 | 
			
		||||
	private Map<Color, Player>	players	= new HashMap<>();
 | 
			
		||||
	private Board				board;
 | 
			
		||||
	private OverlayComponent	overlayComponent;
 | 
			
		||||
	private BoardComponent		boardComponent;
 | 
			
		||||
 | 
			
		||||
	public Game(BoardPane boardPane, String whiteName, String blackName) {
 | 
			
		||||
		players				= new HashMap<>();
 | 
			
		||||
		board				= new Board();
 | 
			
		||||
		init(boardPane, whiteName, blackName);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Game(BoardPane boardPane, String whiteName, String blackName, String fen) {
 | 
			
		||||
		board = new Board(fen);
 | 
			
		||||
		init(boardPane, whiteName, blackName);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void init(BoardPane boardPane, String whiteName, String blackName) {
 | 
			
		||||
 | 
			
		||||
		// Initialize / synchronize UI
 | 
			
		||||
		overlayComponent	= boardPane.getOverlayComponent();
 | 
			
		||||
		boardComponent		= boardPane.getBoardComponent();
 | 
			
		||||
		boardComponent.setBoard(board);
 | 
			
		||||
 | 
			
		||||
		// Initialize players
 | 
			
		||||
		players.put(Color.WHITE, getPlayer(whiteName, Color.WHITE));
 | 
			
		||||
		players.put(Color.BLACK, getPlayer(blackName, Color.BLACK));
 | 
			
		||||
 | 
			
		||||
@@ -59,12 +72,17 @@ public class Game {
 | 
			
		||||
 | 
			
		||||
	public void onMove(Player player, Move move) {
 | 
			
		||||
		if (board.getPos(move).getColor() == player.color && board.attemptMove(move)) {
 | 
			
		||||
 | 
			
		||||
			// Redraw
 | 
			
		||||
			boardComponent.repaint();
 | 
			
		||||
			overlayComponent.displayArrow(move);
 | 
			
		||||
 | 
			
		||||
			// Run garbage collection
 | 
			
		||||
			System.gc();
 | 
			
		||||
 | 
			
		||||
			System.out.printf("%s: %s%n", player.color, move);
 | 
			
		||||
			System.out.println("FEN: " + board.toFEN());
 | 
			
		||||
			EventBus.getInstance().dispatch(new MoveEvent(move));
 | 
			
		||||
			GameState eventType = board.getGameEventType(board.getDest(move).getColor().opposite());
 | 
			
		||||
			switch (eventType) {
 | 
			
		||||
				case CHECKMATE:
 | 
			
		||||
@@ -76,30 +94,33 @@ public class Game {
 | 
			
		||||
				case CHECK:
 | 
			
		||||
					System.out.printf("%s in check!%n", player.color.opposite());
 | 
			
		||||
				default:
 | 
			
		||||
					players.get(board.getActiveColor()).requestMove();
 | 
			
		||||
					players.get(board.getLog().getActiveColor()).requestMove();
 | 
			
		||||
			}
 | 
			
		||||
		} else player.requestMove();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void start() {
 | 
			
		||||
		players.get(Color.WHITE).requestMove();
 | 
			
		||||
		players.get(board.getLog().getActiveColor()).requestMove();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void reset() {
 | 
			
		||||
		players.values().forEach(Player::cancelMove);
 | 
			
		||||
		board.initializeDefaultPositions();
 | 
			
		||||
		board.initDefaultPositions();
 | 
			
		||||
		boardComponent.repaint();
 | 
			
		||||
		overlayComponent.clearDots();
 | 
			
		||||
		overlayComponent.clearArrow();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Removed all connections between the game and the UI.
 | 
			
		||||
	 * Stops the game by disconnecting its players form the UI.
 | 
			
		||||
	 */
 | 
			
		||||
	public void disconnect() {
 | 
			
		||||
	public void stop() {
 | 
			
		||||
		players.values().forEach(Player::disconnect);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Assigns the players their opposite colors.
 | 
			
		||||
	 */
 | 
			
		||||
	public void swapColors() {
 | 
			
		||||
		players.values().forEach(Player::cancelMove);
 | 
			
		||||
		Player	white	= players.get(Color.WHITE);
 | 
			
		||||
@@ -108,10 +129,16 @@ public class Game {
 | 
			
		||||
		black.setColor(Color.WHITE);
 | 
			
		||||
		players.put(Color.WHITE, black);
 | 
			
		||||
		players.put(Color.BLACK, white);
 | 
			
		||||
		players.get(board.getActiveColor()).requestMove();
 | 
			
		||||
		players.get(board.getLog().getActiveColor()).requestMove();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return The board on which this game's moves are made
 | 
			
		||||
	 */
 | 
			
		||||
	public Board getBoard() { return board; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return The players participating in this game
 | 
			
		||||
	 */
 | 
			
		||||
	public Map<Color, Player> getPlayers() { return players; }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ public class NaturalPlayer extends Player implements MouseListener {
 | 
			
		||||
			pos = new Position(evt.getPoint().x / overlayComponent.getTileSize(),
 | 
			
		||||
					evt.getPoint().y / overlayComponent.getTileSize());
 | 
			
		||||
 | 
			
		||||
			Board board = (Board) NaturalPlayer.this.board.clone();
 | 
			
		||||
			Board board = new Board(this.board);
 | 
			
		||||
			if (board.get(pos) != null && board.get(pos).getColor() == color) {
 | 
			
		||||
				List<Position> positions = board.getMoves(pos)
 | 
			
		||||
					.stream()
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@ public class AIPlayer extends Player {
 | 
			
		||||
			/*
 | 
			
		||||
			 * Get a copy of the board and the available moves.
 | 
			
		||||
			 */
 | 
			
		||||
			Board		board	= (Board) AIPlayer.this.board.clone();
 | 
			
		||||
			Board		board	= new Board(this.board);
 | 
			
		||||
			List<Move>	moves	= board.getMoves(color);
 | 
			
		||||
 | 
			
		||||
			/*
 | 
			
		||||
@@ -64,7 +64,7 @@ public class AIPlayer extends Player {
 | 
			
		||||
			for (int i = 0; i < numThreads; i++) {
 | 
			
		||||
				if (rem-- > 0) ++endIndex;
 | 
			
		||||
				endIndex += step;
 | 
			
		||||
				processors.add(new MoveProcessor((Board) board.clone(), moves.subList(beginIndex, endIndex), color,
 | 
			
		||||
				processors.add(new MoveProcessor(new Board(board), moves.subList(beginIndex, endIndex), color,
 | 
			
		||||
						maxDepth, alphaBetaThreshold));
 | 
			
		||||
				beginIndex = endIndex;
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,7 @@ public interface UCIListener {
 | 
			
		||||
	/**
 | 
			
		||||
	 * The engine sends information to the GUI.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param additionalInfo Contains all pieces of information to be sent
 | 
			
		||||
	 * @param info Contains all pieces of information to be sent
 | 
			
		||||
	 */
 | 
			
		||||
	default void onInfo(UCIInfo info) {}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								src/dev/kske/chess/ui/FENDropTarget.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/dev/kske/chess/ui/FENDropTarget.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
package dev.kske.chess.ui;
 | 
			
		||||
 | 
			
		||||
import java.awt.datatransfer.DataFlavor;
 | 
			
		||||
import java.awt.datatransfer.UnsupportedFlavorException;
 | 
			
		||||
import java.awt.dnd.DnDConstants;
 | 
			
		||||
import java.awt.dnd.DropTargetAdapter;
 | 
			
		||||
import java.awt.dnd.DropTargetDropEvent;
 | 
			
		||||
import java.io.BufferedReader;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileReader;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import dev.kske.chess.game.Game;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>Chess</strong><br>
 | 
			
		||||
 * File: <strong>FENDropTarget.java</strong><br>
 | 
			
		||||
 * Created: <strong>13 Aug 2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
public class FENDropTarget extends DropTargetAdapter {
 | 
			
		||||
 | 
			
		||||
	private MainWindow mainWindow;
 | 
			
		||||
 | 
			
		||||
	public FENDropTarget(MainWindow mainWindow) {
 | 
			
		||||
		this.mainWindow = mainWindow;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@SuppressWarnings("unchecked")
 | 
			
		||||
	@Override
 | 
			
		||||
	public void drop(DropTargetDropEvent evt) {
 | 
			
		||||
		try {
 | 
			
		||||
			evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
 | 
			
		||||
			((List<File>) evt.getTransferable().getTransferData(DataFlavor.javaFileListFlavor)).forEach(file -> {
 | 
			
		||||
				try (BufferedReader br = new BufferedReader(new FileReader(file))) {
 | 
			
		||||
					final GamePane	gamePane	= mainWindow.addGamePane();
 | 
			
		||||
					final String	fen			= br.readLine();
 | 
			
		||||
					GameConfigurationDialog.show((whiteName, blackName) -> {
 | 
			
		||||
						final Game game = new Game(gamePane.getBoardPane(), whiteName, blackName, fen);
 | 
			
		||||
						gamePane.setGame(game);
 | 
			
		||||
						game.start();
 | 
			
		||||
					});
 | 
			
		||||
					evt.dropComplete(true);
 | 
			
		||||
				} catch (IOException e) {
 | 
			
		||||
					e.printStackTrace();
 | 
			
		||||
					evt.rejectDrop();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		} catch (UnsupportedFlavorException | IOException ex) {
 | 
			
		||||
			ex.printStackTrace();
 | 
			
		||||
			evt.rejectDrop();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -4,6 +4,7 @@ import java.awt.Font;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.function.BiConsumer;
 | 
			
		||||
 | 
			
		||||
import javax.swing.DefaultComboBoxModel;
 | 
			
		||||
import javax.swing.JButton;
 | 
			
		||||
@@ -17,24 +18,22 @@ import javax.swing.JLabel;
 | 
			
		||||
 * Created: <strong>24.07.2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
public class GameConfigurationDialog extends JDialog {
 | 
			
		||||
public class GameConfigurationDialog {
 | 
			
		||||
 | 
			
		||||
	private static final long serialVersionUID = 9080577278529876972L;
 | 
			
		||||
	private GameConfigurationDialog() {}
 | 
			
		||||
 | 
			
		||||
	private String	whiteName, blackName;
 | 
			
		||||
	private boolean	startGame;
 | 
			
		||||
	public static void show(BiConsumer<String, String> action) {
 | 
			
		||||
		new JDialog() {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Create the dialog.
 | 
			
		||||
	 */
 | 
			
		||||
	public GameConfigurationDialog() {
 | 
			
		||||
			private static final long serialVersionUID = -5768339760489440385L;
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				setTitle("Game Configuration");
 | 
			
		||||
				setBounds(100, 100, 281, 142);
 | 
			
		||||
				setModal(true);
 | 
			
		||||
				setLocationRelativeTo(null);
 | 
			
		||||
				getContentPane().setLayout(null);
 | 
			
		||||
 | 
			
		||||
		startGame = false;
 | 
			
		||||
				List<String> options = new ArrayList<>(Arrays.asList("Natural Player", "AI Player"));
 | 
			
		||||
				EngineUtil.getEngineInfos().forEach(info -> options.add(info.name));
 | 
			
		||||
 | 
			
		||||
@@ -60,10 +59,8 @@ public class GameConfigurationDialog extends JDialog {
 | 
			
		||||
 | 
			
		||||
				JButton btnStart = new JButton("Start");
 | 
			
		||||
				btnStart.addActionListener((evt) -> {
 | 
			
		||||
			startGame	= true;
 | 
			
		||||
			whiteName	= options.get(cbWhite.getSelectedIndex());
 | 
			
		||||
			blackName	= options.get(cbBlack.getSelectedIndex());
 | 
			
		||||
					dispose();
 | 
			
		||||
					action.accept(options.get(cbWhite.getSelectedIndex()), options.get(cbBlack.getSelectedIndex()));
 | 
			
		||||
				});
 | 
			
		||||
				btnStart.setBounds(20, 73, 89, 23);
 | 
			
		||||
				getContentPane().add(btnStart);
 | 
			
		||||
@@ -72,11 +69,8 @@ public class GameConfigurationDialog extends JDialog {
 | 
			
		||||
				btnCancel.addActionListener((evt) -> dispose());
 | 
			
		||||
				btnCancel.setBounds(157, 73, 89, 23);
 | 
			
		||||
				getContentPane().add(btnCancel);
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
		}.setVisible(true);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public String getWhiteName() { return whiteName; }
 | 
			
		||||
 | 
			
		||||
	public String getBlackName() { return blackName; }
 | 
			
		||||
 | 
			
		||||
	public boolean isStartGame() { return startGame; }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										126
									
								
								src/dev/kske/chess/ui/GamePane.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								src/dev/kske/chess/ui/GamePane.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
package dev.kske.chess.ui;
 | 
			
		||||
 | 
			
		||||
import java.awt.GridBagConstraints;
 | 
			
		||||
import java.awt.GridBagLayout;
 | 
			
		||||
import java.awt.GridLayout;
 | 
			
		||||
 | 
			
		||||
import javax.swing.JButton;
 | 
			
		||||
import javax.swing.JComponent;
 | 
			
		||||
import javax.swing.JLabel;
 | 
			
		||||
import javax.swing.JPanel;
 | 
			
		||||
 | 
			
		||||
import dev.kske.chess.board.Piece.Color;
 | 
			
		||||
import dev.kske.chess.game.Game;
 | 
			
		||||
import dev.kske.chess.game.NaturalPlayer;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>Chess</strong><br>
 | 
			
		||||
 * File: <strong>GamePane.java</strong><br>
 | 
			
		||||
 * Created: <strong>23.08.2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
public class GamePane extends JComponent {
 | 
			
		||||
 | 
			
		||||
	private static final long serialVersionUID = 4349772338239617477L;
 | 
			
		||||
 | 
			
		||||
	private JButton		btnRestart, btnSwapColors;
 | 
			
		||||
	private BoardPane	boardPane;
 | 
			
		||||
	private LogPanel	logPanel;
 | 
			
		||||
	private Game		game;
 | 
			
		||||
	private Color		activeColor;
 | 
			
		||||
 | 
			
		||||
	public GamePane() {
 | 
			
		||||
		activeColor = Color.WHITE;
 | 
			
		||||
 | 
			
		||||
		GridBagLayout gridBagLayout = new GridBagLayout();
 | 
			
		||||
		gridBagLayout.columnWidths	= new int[] { 450, 1, 0 };
 | 
			
		||||
		gridBagLayout.rowHeights	= new int[] { 33, 267, 1, 0 };
 | 
			
		||||
		gridBagLayout.columnWeights	= new double[] { 0.0, 0.0, Double.MIN_VALUE };
 | 
			
		||||
		gridBagLayout.rowWeights	= new double[] { 0.0, 0.0, 0.0, Double.MIN_VALUE };
 | 
			
		||||
		setLayout(gridBagLayout);
 | 
			
		||||
 | 
			
		||||
		JPanel toolPanel = new JPanel();
 | 
			
		||||
		btnRestart = new JButton("Restart");
 | 
			
		||||
		btnRestart.addActionListener((evt) -> { if (game != null) game.reset(); game.start(); });
 | 
			
		||||
 | 
			
		||||
		btnSwapColors = new JButton("Play as black");
 | 
			
		||||
		btnSwapColors.addActionListener((evt) -> {
 | 
			
		||||
			game.swapColors();
 | 
			
		||||
			btnSwapColors.setText("Play as " + activeColor.toString().toLowerCase());
 | 
			
		||||
			activeColor = activeColor.opposite();
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		toolPanel.add(btnRestart);
 | 
			
		||||
		toolPanel.add(btnSwapColors);
 | 
			
		||||
 | 
			
		||||
		GridBagConstraints gbc_toolPanel = new GridBagConstraints();
 | 
			
		||||
		gbc_toolPanel.anchor	= GridBagConstraints.NORTH;
 | 
			
		||||
		gbc_toolPanel.fill		= GridBagConstraints.HORIZONTAL;
 | 
			
		||||
		gbc_toolPanel.gridx		= 0;
 | 
			
		||||
		gbc_toolPanel.gridy		= 0;
 | 
			
		||||
		add(toolPanel, gbc_toolPanel);
 | 
			
		||||
		boardPane = new BoardPane();
 | 
			
		||||
		GridBagConstraints gbc_boardPane = new GridBagConstraints();
 | 
			
		||||
		gbc_boardPane.fill	= GridBagConstraints.BOTH;
 | 
			
		||||
		gbc_boardPane.gridx	= 0;
 | 
			
		||||
		gbc_boardPane.gridy	= 1;
 | 
			
		||||
		add(boardPane, gbc_boardPane);
 | 
			
		||||
 | 
			
		||||
		JPanel				numberPanel		= new JPanel(new GridLayout(8, 1));
 | 
			
		||||
		GridBagConstraints	gbc_numberPanel	= new GridBagConstraints();
 | 
			
		||||
		gbc_numberPanel.anchor	= GridBagConstraints.WEST;
 | 
			
		||||
		gbc_numberPanel.fill	= GridBagConstraints.VERTICAL;
 | 
			
		||||
		gbc_numberPanel.gridx	= 1;
 | 
			
		||||
		gbc_numberPanel.gridy	= 1;
 | 
			
		||||
		add(numberPanel, gbc_numberPanel);
 | 
			
		||||
 | 
			
		||||
		JPanel				letterPanel		= new JPanel(new GridLayout(1, 8));
 | 
			
		||||
		GridBagConstraints	gbc_letterPanel	= new GridBagConstraints();
 | 
			
		||||
		gbc_letterPanel.anchor	= GridBagConstraints.NORTH;
 | 
			
		||||
		gbc_letterPanel.fill	= GridBagConstraints.HORIZONTAL;
 | 
			
		||||
		gbc_letterPanel.gridx	= 0;
 | 
			
		||||
		gbc_letterPanel.gridy	= 2;
 | 
			
		||||
		add(letterPanel, gbc_letterPanel);
 | 
			
		||||
 | 
			
		||||
		// Initialize board coordinates
 | 
			
		||||
		for (int i = 0; i < 8; i++) {
 | 
			
		||||
			numberPanel.add(new JLabel(String.valueOf(8 - i)));
 | 
			
		||||
			JLabel letterLabel = new JLabel(String.valueOf((char) (65 + i)));
 | 
			
		||||
			letterLabel.setHorizontalAlignment(JLabel.CENTER);
 | 
			
		||||
			letterPanel.add(letterLabel);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Initialize LogPanel
 | 
			
		||||
		logPanel = new LogPanel();
 | 
			
		||||
		GridBagConstraints gbc_logPanel = new GridBagConstraints();
 | 
			
		||||
		gbc_logPanel.anchor	= GridBagConstraints.EAST;
 | 
			
		||||
		gbc_logPanel.fill	= GridBagConstraints.VERTICAL;
 | 
			
		||||
		gbc_logPanel.gridx	= 2;
 | 
			
		||||
		gbc_logPanel.gridy	= 1;
 | 
			
		||||
		add(logPanel, gbc_logPanel);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return The {@link BoardPane} instance associated with this game pane
 | 
			
		||||
	 */
 | 
			
		||||
	public BoardPane getBoardPane() { return boardPane; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return The {@link Game} instance associated with this game pane
 | 
			
		||||
	 */
 | 
			
		||||
	public Game getGame() { return game; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Assigns a new {@link Game} instance to this game pane. If exactly one of the
 | 
			
		||||
	 * players is natural, color swapping functionality is enabled.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param game The {@link Game} to assign to this game pane.
 | 
			
		||||
	 */
 | 
			
		||||
	public void setGame(Game game) {
 | 
			
		||||
		if (this.game != null) this.game.stop();
 | 
			
		||||
		this.game = game;
 | 
			
		||||
		btnSwapColors.setEnabled(game.getPlayers().get(Color.WHITE) instanceof NaturalPlayer
 | 
			
		||||
				^ game.getPlayers().get(Color.BLACK) instanceof NaturalPlayer);
 | 
			
		||||
		logPanel.setLog(game.getBoard().getLog());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,47 +0,0 @@
 | 
			
		||||
package dev.kske.chess.ui;
 | 
			
		||||
 | 
			
		||||
import java.awt.BorderLayout;
 | 
			
		||||
 | 
			
		||||
import javax.swing.JFrame;
 | 
			
		||||
import javax.swing.JPanel;
 | 
			
		||||
import javax.swing.JTable;
 | 
			
		||||
import javax.swing.border.EmptyBorder;
 | 
			
		||||
import javax.swing.table.DefaultTableModel;
 | 
			
		||||
 | 
			
		||||
import dev.kske.chess.board.Move;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>Chess</strong><br>
 | 
			
		||||
 * File: <strong>LogFrame.java</strong><br>
 | 
			
		||||
 * Created: <strong>17.07.2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
public class LogFrame extends JFrame {
 | 
			
		||||
 | 
			
		||||
	private static final long serialVersionUID = 1932671698254197119L;
 | 
			
		||||
 | 
			
		||||
	private JPanel	mcontentPane;
 | 
			
		||||
	private JTable	mtable;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Create the frame.
 | 
			
		||||
	 */
 | 
			
		||||
	public LogFrame() {
 | 
			
		||||
		setTitle("Move History");
 | 
			
		||||
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 | 
			
		||||
		setBounds(100, 100, 450, 300);
 | 
			
		||||
		mcontentPane = new JPanel();
 | 
			
		||||
		mcontentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
 | 
			
		||||
		mcontentPane.setLayout(new BorderLayout(0, 0));
 | 
			
		||||
		setContentPane(mcontentPane);
 | 
			
		||||
 | 
			
		||||
		mtable = new JTable();
 | 
			
		||||
		mtable.setModel(new DefaultTableModel(new Object[][] {}, new String[] { "White", "Black" }));
 | 
			
		||||
		mtable.setEnabled(false);
 | 
			
		||||
		mcontentPane.add(mtable, BorderLayout.CENTER);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void add(Move move) {
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										76
									
								
								src/dev/kske/chess/ui/LogPanel.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/dev/kske/chess/ui/LogPanel.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
package dev.kske.chess.ui;
 | 
			
		||||
 | 
			
		||||
import java.awt.BorderLayout;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import javax.swing.JPanel;
 | 
			
		||||
import javax.swing.JScrollPane;
 | 
			
		||||
import javax.swing.JTable;
 | 
			
		||||
import javax.swing.border.EmptyBorder;
 | 
			
		||||
import javax.swing.table.DefaultTableModel;
 | 
			
		||||
 | 
			
		||||
import dev.kske.chess.board.Log;
 | 
			
		||||
import dev.kske.chess.board.Log.MoveNode;
 | 
			
		||||
import dev.kske.chess.event.Event;
 | 
			
		||||
import dev.kske.chess.event.EventBus;
 | 
			
		||||
import dev.kske.chess.event.MoveEvent;
 | 
			
		||||
import dev.kske.chess.event.Subscribable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>Chess</strong><br>
 | 
			
		||||
 * File: <strong>LogPanel.java</strong><br>
 | 
			
		||||
 * Created: <strong>17.07.2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
public class LogPanel extends JPanel implements Subscribable {
 | 
			
		||||
 | 
			
		||||
	private static final long serialVersionUID = 1932671698254197119L;
 | 
			
		||||
 | 
			
		||||
	private JTable	mtable;
 | 
			
		||||
 | 
			
		||||
	private Log log;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Create the frame.
 | 
			
		||||
	 */
 | 
			
		||||
	public LogPanel() {
 | 
			
		||||
		setBorder(new EmptyBorder(5, 5, 5, 5));
 | 
			
		||||
		setLayout(new BorderLayout(0, 0));
 | 
			
		||||
 | 
			
		||||
		mtable = new JTable();
 | 
			
		||||
		mtable.setEnabled(false);
 | 
			
		||||
		add(new JScrollPane(mtable), BorderLayout.CENTER);
 | 
			
		||||
 | 
			
		||||
		EventBus.getInstance().register(this);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Set<Class<?>> supports() {
 | 
			
		||||
		return new HashSet<>(Arrays.asList(MoveEvent.class));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void handle(Event<?> event) {
 | 
			
		||||
		if (log == null) return;
 | 
			
		||||
 | 
			
		||||
		// TODO: Display log with variations
 | 
			
		||||
		final List<MoveNode>	moves	= /* log.getLoggedMoves() */ new ArrayList<>();
 | 
			
		||||
		String[][]				data	= new String[moves.size() / 2 + moves.size() % 2][2];
 | 
			
		||||
		for (int i = 0; i < data.length; i++) {
 | 
			
		||||
			data[i][0] = moves.get(i * 2).move.toSAN();
 | 
			
		||||
			if (i * 2 + 1 < moves.size()) data[i][1] = moves.get(i * 2 + 1).move.toSAN();
 | 
			
		||||
		}
 | 
			
		||||
		mtable.setModel(new DefaultTableModel(data, new String[] { "White", "Black" }));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Log getLog() { return log; }
 | 
			
		||||
 | 
			
		||||
	public void setLog(Log log) {
 | 
			
		||||
		this.log = log;
 | 
			
		||||
		handle(null);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,18 +1,11 @@
 | 
			
		||||
package dev.kske.chess.ui;
 | 
			
		||||
 | 
			
		||||
import java.awt.BorderLayout;
 | 
			
		||||
import java.awt.EventQueue;
 | 
			
		||||
import java.awt.GridLayout;
 | 
			
		||||
import java.awt.Toolkit;
 | 
			
		||||
import java.awt.dnd.DropTarget;
 | 
			
		||||
 | 
			
		||||
import javax.swing.JButton;
 | 
			
		||||
import javax.swing.JFrame;
 | 
			
		||||
import javax.swing.JLabel;
 | 
			
		||||
import javax.swing.JPanel;
 | 
			
		||||
 | 
			
		||||
import dev.kske.chess.board.Piece.Color;
 | 
			
		||||
import dev.kske.chess.game.Game;
 | 
			
		||||
import dev.kske.chess.game.NaturalPlayer;
 | 
			
		||||
import javax.swing.JTabbedPane;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>Chess</strong><br>
 | 
			
		||||
@@ -20,27 +13,21 @@ import dev.kske.chess.game.NaturalPlayer;
 | 
			
		||||
 * Created: <strong>01.07.2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
public class MainWindow {
 | 
			
		||||
public class MainWindow extends JFrame {
 | 
			
		||||
 | 
			
		||||
	private JFrame		mframe;
 | 
			
		||||
	private JButton		btnRestart, btnSwapColors;
 | 
			
		||||
	private BoardPane	boardPane;
 | 
			
		||||
	private Game		game;
 | 
			
		||||
	private Color		activeColor;
 | 
			
		||||
	private static final long serialVersionUID = -3100939302567978977L;
 | 
			
		||||
 | 
			
		||||
	private JTabbedPane tabbedPane;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Launch the application.
 | 
			
		||||
	 */
 | 
			
		||||
	public static void main(String[] args) {
 | 
			
		||||
		EventQueue.invokeLater(new Runnable() {
 | 
			
		||||
 | 
			
		||||
			public void run() {
 | 
			
		||||
		EventQueue.invokeLater(() -> {
 | 
			
		||||
			try {
 | 
			
		||||
					MainWindow window = new MainWindow();
 | 
			
		||||
					window.mframe.setVisible(true);
 | 
			
		||||
				} catch (Exception e) {
 | 
			
		||||
					e.printStackTrace();
 | 
			
		||||
				}
 | 
			
		||||
				new MainWindow();
 | 
			
		||||
			} catch (Exception ex) {
 | 
			
		||||
				ex.printStackTrace();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
@@ -49,6 +36,7 @@ public class MainWindow {
 | 
			
		||||
	 * Create the application.
 | 
			
		||||
	 */
 | 
			
		||||
	public MainWindow() {
 | 
			
		||||
		super("Chess by Kai S. K. Engelbart");
 | 
			
		||||
		initialize();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -56,58 +44,48 @@ public class MainWindow {
 | 
			
		||||
	 * Initialize the contents of the frame.
 | 
			
		||||
	 */
 | 
			
		||||
	private void initialize() {
 | 
			
		||||
		mframe = new JFrame("Chess by Kai S. K. Engelbart");
 | 
			
		||||
		mframe.setResizable(false);
 | 
			
		||||
		mframe.setBounds(100, 100, 494, 565);
 | 
			
		||||
		mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 | 
			
		||||
		mframe.setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("/pieces/queen_white.png")));
 | 
			
		||||
		// Configure frame
 | 
			
		||||
		setResizable(false);
 | 
			
		||||
		setBounds(100, 100, 494, 565);
 | 
			
		||||
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 | 
			
		||||
		setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("/pieces/queen_white.png")));
 | 
			
		||||
 | 
			
		||||
		boardPane = new BoardPane();
 | 
			
		||||
		mframe.getContentPane().add(boardPane, BorderLayout.CENTER);
 | 
			
		||||
		// Add frame content
 | 
			
		||||
		tabbedPane = new JTabbedPane();
 | 
			
		||||
		getContentPane().add(tabbedPane);
 | 
			
		||||
 | 
			
		||||
		JPanel toolPanel = new JPanel();
 | 
			
		||||
		btnRestart = new JButton("Restart");
 | 
			
		||||
		btnRestart.addActionListener((evt) -> { if (game != null) game.reset(); game.start(); });
 | 
			
		||||
		setJMenuBar(new MenuBar(this));
 | 
			
		||||
		new DropTarget(this, new FENDropTarget(this));
 | 
			
		||||
 | 
			
		||||
		activeColor		= Color.WHITE;
 | 
			
		||||
		btnSwapColors	= new JButton("Play as black");
 | 
			
		||||
		btnSwapColors.addActionListener((evt) -> {
 | 
			
		||||
			game.swapColors();
 | 
			
		||||
			btnSwapColors.setText("Play as " + activeColor.toString().toLowerCase());
 | 
			
		||||
			activeColor = activeColor.opposite();
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		toolPanel.add(btnRestart);
 | 
			
		||||
		toolPanel.add(btnSwapColors);
 | 
			
		||||
		mframe.getContentPane().add(toolPanel, BorderLayout.NORTH);
 | 
			
		||||
 | 
			
		||||
		JPanel letterPanel = new JPanel(new GridLayout(1, 8));
 | 
			
		||||
		for (int i = 0; i < 8; i++) {
 | 
			
		||||
			JLabel letterLabel = new JLabel(String.valueOf((char) (65 + i)));
 | 
			
		||||
			letterLabel.setHorizontalAlignment(JLabel.CENTER);
 | 
			
		||||
			letterPanel.add(letterLabel);
 | 
			
		||||
		}
 | 
			
		||||
		mframe.add(letterPanel, BorderLayout.SOUTH);
 | 
			
		||||
 | 
			
		||||
		JPanel numberPanel = new JPanel(new GridLayout(8, 1));
 | 
			
		||||
		for (int i = 0; i < 8; i++)
 | 
			
		||||
			numberPanel.add(new JLabel(String.valueOf(8 - i)));
 | 
			
		||||
		mframe.add(numberPanel, BorderLayout.EAST);
 | 
			
		||||
 | 
			
		||||
		mframe.setJMenuBar(new MenuBar(this));
 | 
			
		||||
 | 
			
		||||
		mframe.pack();
 | 
			
		||||
		mframe.setLocationRelativeTo(null);
 | 
			
		||||
		// Update position and dimensions
 | 
			
		||||
		pack();
 | 
			
		||||
		setLocationRelativeTo(null);
 | 
			
		||||
		setVisible(true);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public BoardPane getBoardPane() { return boardPane; }
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return The currently selected {@link GamePane} component
 | 
			
		||||
	 */
 | 
			
		||||
	public GamePane getSelectedGamePane() { return (GamePane) tabbedPane.getSelectedComponent(); }
 | 
			
		||||
 | 
			
		||||
	public Game getGame() { return game; }
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates a new {@link GamePane}, adds it to the tabbed pane and opens it.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return The new {@link GamePane}
 | 
			
		||||
	 */
 | 
			
		||||
	public GamePane addGamePane() {
 | 
			
		||||
		GamePane gamePane = new GamePane();
 | 
			
		||||
		tabbedPane.add("Game " + (tabbedPane.getComponentCount() + 1), gamePane);
 | 
			
		||||
		tabbedPane.setSelectedIndex(tabbedPane.getComponentCount() - 1);
 | 
			
		||||
		return gamePane;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setGame(Game game) {
 | 
			
		||||
		if (this.game != null) this.game.disconnect();
 | 
			
		||||
		this.game = game;
 | 
			
		||||
		btnSwapColors.setEnabled(game.getPlayers().get(Color.WHITE) instanceof NaturalPlayer
 | 
			
		||||
				^ game.getPlayers().get(Color.BLACK) instanceof NaturalPlayer);
 | 
			
		||||
	/**
 | 
			
		||||
	 * Removes a {@link GamePane} form the tabbed pane.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param index The index of the {@link GamePane} to remove
 | 
			
		||||
	 */
 | 
			
		||||
	public void removeGamePane(int index) {
 | 
			
		||||
		tabbedPane.remove(index);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,11 +21,9 @@ public class MenuBar extends JMenuBar {
 | 
			
		||||
	private static final long serialVersionUID = -7221583703531248228L;
 | 
			
		||||
 | 
			
		||||
	private final MainWindow mainWindow;
 | 
			
		||||
	private final BoardPane		boardPane;
 | 
			
		||||
 | 
			
		||||
	public MenuBar(MainWindow mainWindow) {
 | 
			
		||||
		this.mainWindow = mainWindow;
 | 
			
		||||
		boardPane		= mainWindow.getBoardPane();
 | 
			
		||||
 | 
			
		||||
		initGameMenu();
 | 
			
		||||
		initEngineMenu();
 | 
			
		||||
@@ -36,16 +34,16 @@ public class MenuBar extends JMenuBar {
 | 
			
		||||
		JMenu		gameMenu		= new JMenu("Game");
 | 
			
		||||
		JMenuItem	newGameMenuItem	= new JMenuItem("New Game");
 | 
			
		||||
		newGameMenuItem.addActionListener((evt) -> {
 | 
			
		||||
			GameConfigurationDialog dialog = new GameConfigurationDialog();
 | 
			
		||||
			dialog.setVisible(true);
 | 
			
		||||
			if (dialog.isStartGame()) startGame(new Game(boardPane, dialog.getWhiteName(), dialog.getBlackName()));
 | 
			
		||||
			GameConfigurationDialog.show((whiteName, blackName) -> {
 | 
			
		||||
				GamePane	gamePane	= mainWindow.addGamePane();
 | 
			
		||||
				Game		game		= new Game(gamePane.getBoardPane(), whiteName, blackName);
 | 
			
		||||
				gamePane.setGame(game);
 | 
			
		||||
				game.start();
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
		gameMenu.add(newGameMenuItem);
 | 
			
		||||
 | 
			
		||||
		add(gameMenu);
 | 
			
		||||
 | 
			
		||||
		// Start a game
 | 
			
		||||
		startGame(new Game(boardPane, "Natural Player", "Natural Player"));
 | 
			
		||||
		newGameMenuItem.doClick();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void initEngineMenu() {
 | 
			
		||||
@@ -71,19 +69,25 @@ public class MenuBar extends JMenuBar {
 | 
			
		||||
		JMenu toolsMenu = new JMenu("Tools");
 | 
			
		||||
 | 
			
		||||
		JMenuItem exportFENMenuItem = new JMenuItem("Export board to FEN");
 | 
			
		||||
		exportFENMenuItem.addActionListener((evt) -> Toolkit.getDefaultToolkit()
 | 
			
		||||
			.getSystemClipboard()
 | 
			
		||||
			.setContents(new StringSelection(mainWindow.getGame().getBoard().toFEN()), null));
 | 
			
		||||
		exportFENMenuItem.addActionListener((evt) -> {
 | 
			
		||||
			final String fen = mainWindow.getSelectedGamePane().getGame().getBoard().toFEN();
 | 
			
		||||
			Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(fen), null);
 | 
			
		||||
			JOptionPane.showMessageDialog(mainWindow, String.format("FEN-string copied to clipboard!%n%s", fen));
 | 
			
		||||
		});
 | 
			
		||||
		toolsMenu.add(exportFENMenuItem);
 | 
			
		||||
 | 
			
		||||
		JMenuItem loadFromFENMenuItem = new JMenuItem("Load board from FEN");
 | 
			
		||||
		loadFromFENMenuItem.addActionListener((evt) -> {
 | 
			
		||||
			final GamePane	gamePane	= mainWindow.addGamePane();
 | 
			
		||||
			final String	fen			= JOptionPane.showInputDialog("Enter a FEN string: ");
 | 
			
		||||
			GameConfigurationDialog.show((whiteName, blackName) -> {
 | 
			
		||||
				final Game game = new Game(gamePane.getBoardPane(), whiteName, blackName, fen);
 | 
			
		||||
				gamePane.setGame(game);
 | 
			
		||||
				game.start();
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
		toolsMenu.add(loadFromFENMenuItem);
 | 
			
		||||
 | 
			
		||||
		add(toolsMenu);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void startGame(Game game) {
 | 
			
		||||
		mainWindow.setGame(game);
 | 
			
		||||
 | 
			
		||||
		// Update board and board component
 | 
			
		||||
		game.reset();
 | 
			
		||||
		game.start();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,11 +19,9 @@ import dev.kske.chess.board.Piece;
 | 
			
		||||
 */
 | 
			
		||||
public class TextureUtil {
 | 
			
		||||
 | 
			
		||||
	private static Map<String, Image> textures, scaledTextures;
 | 
			
		||||
	private static Map<String, Image> textures = new HashMap<>(), scaledTextures = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
	static {
 | 
			
		||||
		textures = new HashMap<>();
 | 
			
		||||
		scaledTextures	= new HashMap<>();
 | 
			
		||||
		loadPieceTextures();
 | 
			
		||||
		scaledTextures.putAll(textures);
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package dev.kske.chess.test;
 | 
			
		||||
package dev.kske.chess.board;
 | 
			
		||||
 | 
			
		||||
import static org.junit.Assert.assertNotEquals;
 | 
			
		||||
import static org.junit.Assert.assertNotSame;
 | 
			
		||||
@@ -6,10 +6,7 @@ import static org.junit.Assert.assertNotSame;
 | 
			
		||||
import org.junit.jupiter.api.BeforeEach;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
 | 
			
		||||
import dev.kske.chess.board.Board;
 | 
			
		||||
import dev.kske.chess.board.Move;
 | 
			
		||||
import dev.kske.chess.board.Piece.Color;
 | 
			
		||||
import dev.kske.chess.board.Queen;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>Chess</strong><br>
 | 
			
		||||
@@ -34,13 +31,13 @@ class BoardTest {
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testClone() {
 | 
			
		||||
		Board clone = (Board) board.clone();
 | 
			
		||||
		Board clone = new Board(board);
 | 
			
		||||
		assertNotSame(clone, board);
 | 
			
		||||
		assertNotSame(clone.getBoardArr(), board.getBoardArr());
 | 
			
		||||
 | 
			
		||||
		clone.getBoardArr()[0][0] = new Queen(Color.BLACK, clone);
 | 
			
		||||
		clone.move(new Move(1, 1, 1, 2));
 | 
			
		||||
		assertNotEquals(clone.getBoardArr()[0][0], board.getBoardArr()[0][0]);
 | 
			
		||||
		assertNotEquals(clone.getActiveColor(), board.getActiveColor());
 | 
			
		||||
		assertNotEquals(clone.getLog().getActiveColor(), board.getLog().getActiveColor());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										165
									
								
								test/dev/kske/chess/board/LogTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								test/dev/kske/chess/board/LogTest.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,165 @@
 | 
			
		||||
package dev.kske.chess.board;
 | 
			
		||||
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertNull;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertTrue;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.fail;
 | 
			
		||||
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
 | 
			
		||||
import dev.kske.chess.board.Piece.Color;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>Chess</strong><br>
 | 
			
		||||
 * File: <strong>LogTest.java</strong><br>
 | 
			
		||||
 * Created: <strong>13 Sep 2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
class LogTest {
 | 
			
		||||
 | 
			
		||||
	Log log = new Log();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#Log()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testLog() {
 | 
			
		||||
		assertTrue(log.isEmpty());
 | 
			
		||||
		assertNull(log.getLast());
 | 
			
		||||
		assertNull(log.getRoot());
 | 
			
		||||
		assertEquals(log.getActiveColor(), Color.WHITE);
 | 
			
		||||
		assertNull(log.getEnPassant());
 | 
			
		||||
		assertEquals(log.getFullmoveCounter(), 1);
 | 
			
		||||
		assertEquals(log.getHalfmoveClock(), 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#clone()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testClone() {
 | 
			
		||||
		Log other = new Log(log, false);
 | 
			
		||||
		log.setActiveColor(Color.WHITE);
 | 
			
		||||
		other.setActiveColor(Color.BLACK);
 | 
			
		||||
		assertNotEquals(log.getActiveColor(), other.getActiveColor());
 | 
			
		||||
		log.add(Move.fromSAN("a2a4"), null, true);
 | 
			
		||||
		log.add(Move.fromSAN("a4a5"), null, true);
 | 
			
		||||
		other.add(Move.fromSAN("a2a4"), null, true);
 | 
			
		||||
		other.add(Move.fromSAN("a4a5"), null, true);
 | 
			
		||||
		assertNotEquals(log.getRoot(), other.getRoot());
 | 
			
		||||
		assertNotEquals(log.getRoot().getVariations(), other.getRoot().getVariations());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#add(dev.kske.chess.board.Move, dev.kske.chess.board.Piece, boolean)}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testAdd() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#removeLast()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testRemoveLast() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#isEmpty()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testIsEmpty() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#reset()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testReset() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#getRoot()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testGetRoot() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#getLast()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testGetLast() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#getEnPassant()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testGetEnPassant() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#setEnPassant(dev.kske.chess.board.Position)}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testSetEnPassant() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#getActiveColor()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testGetActiveColor() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#setActiveColor(dev.kske.chess.board.Piece.Color)}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testSetActiveColor() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#getFullmoveCounter()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testGetFullmoveCounter() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#setFullmoveCounter(int)}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testSetFullmoveCounter() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#getHalfmoveClock()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testGetHalfmoveClock() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Log#setHalfmoveClock(int)}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testSetHalfmoveClock() {
 | 
			
		||||
		fail("Not yet implemented");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								test/dev/kske/chess/board/PositionTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								test/dev/kske/chess/board/PositionTest.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
package dev.kske.chess.board;
 | 
			
		||||
 | 
			
		||||
import static org.junit.Assert.assertEquals;
 | 
			
		||||
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>Chess</strong><br>
 | 
			
		||||
 * File: <strong>PositionTest.java</strong><br>
 | 
			
		||||
 * Created: <strong>24.07.2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
class PositionTest {
 | 
			
		||||
 | 
			
		||||
	final int	n			= 4;
 | 
			
		||||
	Position[]	positions	= new Position[] { new Position(0, 0), new Position(7, 7), new Position(0, 7), new Position(7, 0) };
 | 
			
		||||
	String[]	sans		= new String[] { "a8", "h1", "a1", "h8" };
 | 
			
		||||
	String[]	strings		= new String[] { "[0, 0]", "[7, 7]", "[0, 7]", "[7, 0]" };
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for
 | 
			
		||||
	 * {@link dev.kske.chess.board.Position#fromSAN(java.lang.String)}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testFromSAN() {
 | 
			
		||||
		for (int i = 0; i < n; i++)
 | 
			
		||||
			assertEquals(positions[i], Position.fromSAN(sans[i]));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Position#toSAN()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testToSAN() {
 | 
			
		||||
		for (int i = 0; i < n; i++)
 | 
			
		||||
			assertEquals(sans[i], positions[i].toSAN());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Position#toString()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testToString() {
 | 
			
		||||
		for (int i = 0; i < n; i++)
 | 
			
		||||
			assertEquals(strings[i], positions[i].toString());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,52 +0,0 @@
 | 
			
		||||
package dev.kske.chess.test;
 | 
			
		||||
 | 
			
		||||
import static org.junit.Assert.assertEquals;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.stream.IntStream;
 | 
			
		||||
 | 
			
		||||
import org.junit.jupiter.api.BeforeEach;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
 | 
			
		||||
import dev.kske.chess.board.Position;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>Chess</strong><br>
 | 
			
		||||
 * File: <strong>PositionTest.java</strong><br>
 | 
			
		||||
 * Created: <strong>24.07.2019</strong><br>
 | 
			
		||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
			
		||||
 */
 | 
			
		||||
class PositionTest {
 | 
			
		||||
 | 
			
		||||
	List<Position>	positions;
 | 
			
		||||
	List<String>	sans;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @throws java.lang.Exception
 | 
			
		||||
	 */
 | 
			
		||||
	@BeforeEach
 | 
			
		||||
	void setUp() throws Exception {
 | 
			
		||||
		positions	= Arrays.asList(new Position(0, 0), new Position(7, 7), new Position(0, 7), new Position(7, 0));
 | 
			
		||||
		sans		= Arrays.asList("a8", "h1", "a1", "h8");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for
 | 
			
		||||
	 * {@link dev.kske.chess.board.Position#fromSAN(java.lang.String)}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testFromSAN() {
 | 
			
		||||
		IntStream.range(0, positions.size())
 | 
			
		||||
			.forEach(i -> assertEquals(positions.get(i), Position.fromSAN(sans.get(i))));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test method for {@link dev.kske.chess.board.Position#toSAN()}.
 | 
			
		||||
	 */
 | 
			
		||||
	@Test
 | 
			
		||||
	void testToSAN() {
 | 
			
		||||
		IntStream.range(0, positions.size()).forEach(i -> assertEquals(sans.get(i), positions.get(i).toSAN()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user