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"/>
|
<attribute name="test" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</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.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"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
@ -5,7 +5,7 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
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.Color;
|
||||||
import dev.kske.chess.board.Piece.Type;
|
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>
|
* Created: <strong>01.07.2019</strong><br>
|
||||||
* Author: <strong>Kai S. K. Engelbart</strong>
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
*/
|
*/
|
||||||
public class Board implements Cloneable {
|
public class Board {
|
||||||
|
|
||||||
private Piece[][] boardArr;
|
private Piece[][] boardArr = new Piece[8][8];
|
||||||
private Map<Color, Position> kingPos;
|
private Map<Color, Position> kingPos = new HashMap<>();
|
||||||
private Map<Color, Map<Type, Boolean>> castlingRights;
|
private Map<Color, Map<Type, Boolean>> castlingRights = new HashMap<>();
|
||||||
private Log log;
|
private Log log = new Log();
|
||||||
|
|
||||||
private static final Map<Type, int[][]> positionScores;
|
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 } });
|
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() {
|
public Board() {
|
||||||
boardArr = new Piece[8][8];
|
initDefaultPositions();
|
||||||
kingPos = new HashMap<>();
|
}
|
||||||
castlingRights = new HashMap<>();
|
|
||||||
log = new Log();
|
/**
|
||||||
initializeDefaultPositions();
|
* 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];
|
||||||
|
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.
|
* Moves a piece across the board without checking if the move is legal.
|
||||||
*
|
*
|
||||||
* @param move The move to execute
|
* @param move The move to execute
|
||||||
* @return The captures piece, or null if the move's destination was empty
|
|
||||||
*/
|
*/
|
||||||
public void move(Move move) {
|
public void move(Move move) {
|
||||||
Piece piece = getPos(move);
|
Piece piece = getPos(move);
|
||||||
@ -156,9 +187,9 @@ public class Board implements Cloneable {
|
|||||||
* Reverts the last move.
|
* Reverts the last move.
|
||||||
*/
|
*/
|
||||||
public void revert() {
|
public void revert() {
|
||||||
LoggedMove loggedMove = log.getLast();
|
MoveNode moveNode = log.getLast();
|
||||||
Move move = loggedMove.move;
|
Move move = moveNode.move;
|
||||||
Piece capturedPiece = loggedMove.capturedPiece;
|
Piece capturedPiece = moveNode.capturedPiece;
|
||||||
|
|
||||||
switch (move.type) {
|
switch (move.type) {
|
||||||
case PAWN_PROMOTION:
|
case PAWN_PROMOTION:
|
||||||
@ -327,7 +358,7 @@ public class Board implements Cloneable {
|
|||||||
/**
|
/**
|
||||||
* Initialized the board array with the default chess pieces and positions.
|
* Initialized the board array with the default chess pieces and positions.
|
||||||
*/
|
*/
|
||||||
public void initializeDefaultPositions() {
|
public void initDefaultPositions() {
|
||||||
// Initialize pawns
|
// Initialize pawns
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
boardArr[i][1] = new Pawn(Color.BLACK, this);
|
boardArr[i][1] = new Pawn(Color.BLACK, this);
|
||||||
@ -370,7 +401,6 @@ public class Board implements Cloneable {
|
|||||||
boardArr[i][j] = null;
|
boardArr[i][j] = null;
|
||||||
|
|
||||||
// Initialize castling rights
|
// Initialize castling rights
|
||||||
castlingRights.clear();
|
|
||||||
Map<Type, Boolean> whiteCastling = new HashMap<>(), blackCastling = new HashMap<>();
|
Map<Type, Boolean> whiteCastling = new HashMap<>(), blackCastling = new HashMap<>();
|
||||||
whiteCastling.put(Type.KING, true);
|
whiteCastling.put(Type.KING, true);
|
||||||
whiteCastling.put(Type.QUEEN, 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
|
* Initialized the board with a position specified in a FEN-encoded string.
|
||||||
* {@code kingPos} and {code boardArr}
|
*
|
||||||
|
* @param fen The FEN-encoded string representing target state of the board
|
||||||
*/
|
*/
|
||||||
@Override
|
public void initFromFEN(String fen) {
|
||||||
public Object clone() {
|
String[] parts = fen.split(" ");
|
||||||
Board board = null;
|
log.reset();
|
||||||
try {
|
|
||||||
board = (Board) super.clone();
|
// Piece placement (from white's perspective)
|
||||||
} catch (CloneNotSupportedException ex) {
|
String[] rows = parts[0].split("/");
|
||||||
ex.printStackTrace();
|
for (int i = 0; i < 8; i++) {
|
||||||
}
|
char[] places = rows[i].toCharArray();
|
||||||
board.boardArr = new Piece[8][8];
|
for (int j = 0, k = 0; k < places.length; j++, k++) {
|
||||||
for (int i = 0; i < 8; i++)
|
if (Character.isDigit(places[k])) {
|
||||||
for (int j = 0; j < 8; j++) {
|
for (int l = j; l < Character.digit(places[k], 10); l++, j++)
|
||||||
if (boardArr[i][j] == null) continue;
|
boardArr[j][i] = null;
|
||||||
board.boardArr[i][j] = (Piece) boardArr[i][j].clone();
|
--j;
|
||||||
board.boardArr[i][j].board = board;
|
} 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.kingPos = new HashMap<>();
|
// Active color
|
||||||
board.kingPos.putAll(kingPos);
|
log.setActiveColor(Color.fromFirstChar(parts[1].charAt(0)));
|
||||||
board.log = (Log) log.clone();
|
|
||||||
|
|
||||||
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() {
|
public String toFEN() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
@ -418,39 +504,18 @@ public class Board implements Cloneable {
|
|||||||
// Piece placement (from white's perspective)
|
// Piece placement (from white's perspective)
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
int emptyCount = 0;
|
int emptyCount = 0;
|
||||||
for (int j = 0; j < 8; j++)
|
for (int j = 0; j < 8; j++) {
|
||||||
if (boardArr[j][i] == null) ++emptyCount;
|
final Piece piece = boardArr[j][i];
|
||||||
|
if (piece == null) ++emptyCount;
|
||||||
else {
|
else {
|
||||||
if (emptyCount != 0) {
|
if (emptyCount != 0) {
|
||||||
sb.append(emptyCount);
|
sb.append(emptyCount);
|
||||||
emptyCount = 0;
|
emptyCount = 0;
|
||||||
}
|
}
|
||||||
char piece;
|
char p = boardArr[j][i].getType().firstChar();
|
||||||
switch (boardArr[j][i].getType()) {
|
sb.append(piece.getColor() == Color.WHITE ? Character.toUpperCase(p) : p);
|
||||||
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 = '-';
|
|
||||||
}
|
|
||||||
if (boardArr[j][i].getColor() == Color.BLACK) piece = Character.toLowerCase(piece);
|
|
||||||
sb.append(piece);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (emptyCount != 0) sb.append(emptyCount);
|
if (emptyCount != 0) sb.append(emptyCount);
|
||||||
if (i < 7) sb.append('/');
|
if (i < 7) sb.append('/');
|
||||||
}
|
}
|
||||||
@ -458,6 +523,7 @@ public class Board implements Cloneable {
|
|||||||
// Active color
|
// Active color
|
||||||
sb.append(" " + log.getActiveColor().firstChar());
|
sb.append(" " + log.getActiveColor().firstChar());
|
||||||
|
|
||||||
|
// Castling Rights
|
||||||
sb.append(' ');
|
sb.append(' ');
|
||||||
StringBuilder castlingSb = new StringBuilder();
|
StringBuilder castlingSb = new StringBuilder();
|
||||||
if (castlingRights.get(Color.WHITE).get(Type.KING)) castlingSb.append('K');
|
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("-");
|
if (castlingSb.length() == 0) sb.append("-");
|
||||||
sb.append(castlingSb);
|
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()));
|
sb.append(" " + (lastMove == null || lastMove.enPassant == null ? "-" : lastMove.enPassant.toSAN()));
|
||||||
|
|
||||||
// Halfmove clock
|
// Halfmove clock
|
||||||
@ -481,26 +547,56 @@ public class Board implements Cloneable {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param pos The position from which to return a piece
|
||||||
|
* @return The piece at the position
|
||||||
|
*/
|
||||||
public Piece get(Position pos) {
|
public Piece get(Position pos) {
|
||||||
return boardArr[pos.x][pos.y];
|
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) {
|
public void set(Position pos, Piece piece) {
|
||||||
boardArr[pos.x][pos.y] = 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) {
|
public Piece getPos(Move move) {
|
||||||
return get(move.pos);
|
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) {
|
public Piece getDest(Move move) {
|
||||||
return get(move.dest);
|
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) {
|
public void setPos(Move move, Piece piece) {
|
||||||
set(move.pos, 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) {
|
public void setDest(Move move, Piece piece) {
|
||||||
set(move.dest, piece);
|
set(move.dest, piece);
|
||||||
}
|
}
|
||||||
@ -511,12 +607,7 @@ public class Board implements Cloneable {
|
|||||||
public Piece[][] getBoardArr() { return boardArr; }
|
public Piece[][] getBoardArr() { return boardArr; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The active color for the next move
|
* @return The move log
|
||||||
*/
|
*/
|
||||||
public Color getActiveColor() { return log.getActiveColor(); }
|
public Log getLog() { return log; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The current en passant square, or null if there isn't any
|
|
||||||
*/
|
|
||||||
public Position getEnPassantSquare() { return log.getLast() == null ? null : log.getLast().enPassant; }
|
|
||||||
}
|
}
|
||||||
|
@ -11,77 +11,190 @@ import dev.kske.chess.board.Piece.Color;
|
|||||||
* Created: <strong>09.07.2019</strong><br>
|
* Created: <strong>09.07.2019</strong><br>
|
||||||
* Author: <strong>Kai S. K. Engelbart</strong>
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
*/
|
*/
|
||||||
public class Log implements Cloneable {
|
public class Log {
|
||||||
|
|
||||||
private List<LoggedMove> moves;
|
private MoveNode root, current;
|
||||||
private Color activeColor;
|
|
||||||
|
private Position enPassant;
|
||||||
|
private Color activeColor;
|
||||||
|
private int fullmoveCounter, halfmoveClock;
|
||||||
|
|
||||||
public Log() {
|
public Log() {
|
||||||
moves = new ArrayList<>();
|
reset();
|
||||||
activeColor = Color.WHITE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
// 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) {
|
public void add(Move move, Piece capturedPiece, boolean pawnMove) {
|
||||||
// En passant availability
|
enPassant = pawnMove && move.yDist == 2 ? new Position(move.pos.x, move.pos.y + move.ySign) : null;
|
||||||
Position enPassant = null;
|
if (activeColor == Color.BLACK) ++fullmoveCounter;
|
||||||
if (pawnMove && move.yDist == 2) enPassant = new Position(move.pos.x, move.pos.y + move.ySign);
|
if (pawnMove || capturedPiece != null) halfmoveClock = 0;
|
||||||
|
else++halfmoveClock;
|
||||||
// Fullmove counter and halfmove clock
|
|
||||||
int fullmoveCounter, halfmoveClock;
|
|
||||||
if (moves.isEmpty()) {
|
|
||||||
fullmoveCounter = 1;
|
|
||||||
halfmoveClock = 0;
|
|
||||||
} else {
|
|
||||||
fullmoveCounter = getLast().fullmoveCounter;
|
|
||||||
if (activeColor == Color.BLACK) ++fullmoveCounter;
|
|
||||||
halfmoveClock = capturedPiece != null || pawnMove ? 0 : getLast().halfmoveClock + 1;
|
|
||||||
}
|
|
||||||
activeColor = activeColor.opposite();
|
activeColor = activeColor.opposite();
|
||||||
moves.add(new LoggedMove(move, capturedPiece, enPassant, fullmoveCounter, halfmoveClock));
|
final MoveNode leaf = new MoveNode(move, capturedPiece, enPassant, activeColor, fullmoveCounter, halfmoveClock);
|
||||||
|
|
||||||
|
if (isEmpty()) {
|
||||||
|
root = leaf;
|
||||||
|
current = leaf;
|
||||||
|
} else {
|
||||||
|
current.addVariation(leaf);
|
||||||
|
current = leaf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoggedMove getLast() { return moves.isEmpty() ? null : moves.get(moves.size() - 1); }
|
/**
|
||||||
|
* Removed the last move from the log and adjusts its state to the previous
|
||||||
|
* move.
|
||||||
|
*/
|
||||||
public void removeLast() {
|
public void removeLast() {
|
||||||
if (!moves.isEmpty()) {
|
if (!isEmpty() && current.parent != null) {
|
||||||
activeColor = activeColor.opposite();
|
current.parent.variations.remove(current);
|
||||||
moves.remove(moves.size() - 1);
|
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() {
|
public void reset() {
|
||||||
moves.clear();
|
root = null;
|
||||||
activeColor = Color.WHITE;
|
current = null;
|
||||||
}
|
enPassant = null;
|
||||||
|
activeColor = Color.WHITE;
|
||||||
@Override
|
fullmoveCounter = 1;
|
||||||
public Object clone() {
|
halfmoveClock = 0;
|
||||||
Log log = null;
|
|
||||||
try {
|
|
||||||
log = (Log) super.clone();
|
|
||||||
} catch (CloneNotSupportedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
log.moves = new ArrayList<>();
|
|
||||||
log.moves.addAll(this.moves);
|
|
||||||
return log;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The first logged move, or {@code null} if there is none
|
||||||
|
*/
|
||||||
|
public MoveNode getRoot() { return root; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the last logged move, or {@code null} if there is none
|
||||||
|
*/
|
||||||
|
public MoveNode getLast() { return current; }
|
||||||
|
|
||||||
|
public Position getEnPassant() { return enPassant; }
|
||||||
|
|
||||||
|
public void setEnPassant(Position enPassant) { this.enPassant = enPassant; }
|
||||||
|
|
||||||
public Color getActiveColor() { return activeColor; }
|
public Color getActiveColor() { return activeColor; }
|
||||||
|
|
||||||
public static class LoggedMove {
|
public void setActiveColor(Color activeColor) { this.activeColor = activeColor; }
|
||||||
|
|
||||||
public final Move move;
|
public int getFullmoveCounter() { return fullmoveCounter; }
|
||||||
public final Piece capturedPiece;
|
|
||||||
|
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 Position enPassant;
|
||||||
public final int fullmoveCounter, halfmoveClock;
|
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.move = move;
|
||||||
this.capturedPiece = capturedPiece;
|
this.capturedPiece = capturedPiece;
|
||||||
this.enPassant = enPassant;
|
this.enPassant = enPassant;
|
||||||
|
this.activeColor = activeColor;
|
||||||
this.fullmoveCounter = fullmoveCounter;
|
this.fullmoveCounter = fullmoveCounter;
|
||||||
this.halfmoveClock = halfmoveClock;
|
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)));
|
Position.fromSAN(move.substring(2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toSAN() {
|
||||||
|
return pos.toSAN() + dest.toSAN();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isHorizontal() { return yDist == 0; }
|
public boolean isHorizontal() { return yDist == 0; }
|
||||||
|
|
||||||
public boolean isVertical() { return xDist == 0; }
|
public boolean isVertical() { return xDist == 0; }
|
||||||
|
@ -29,7 +29,7 @@ public class Pawn extends Piece {
|
|||||||
move.type = Move.Type.PAWN_PROMOTION;
|
move.type = Move.Type.PAWN_PROMOTION;
|
||||||
|
|
||||||
// Mark the move as en passant if necessary
|
// 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;
|
enPassant = true;
|
||||||
move.type = Move.Type.EN_PASSANT;
|
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);
|
moves.parallelStream().forEach(m -> m.type = Move.Type.PAWN_PROMOTION);
|
||||||
|
|
||||||
// Add en passant move if necessary
|
// Add en passant move if necessary
|
||||||
if (board.getEnPassantSquare() != null) {
|
if (board.getLog().getEnPassant() != null) {
|
||||||
Move move = new Move(pos, board.getEnPassantSquare(), Move.Type.EN_PASSANT);
|
Move move = new Move(pos, board.getLog().getEnPassant(), Move.Type.EN_PASSANT);
|
||||||
if (move.isDiagonal() && move.xDist == 1) moves.add(move);
|
if (move.isDiagonal() && move.xDist == 1) moves.add(move);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,18 +85,29 @@ public abstract class Piece implements Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static enum Type {
|
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 {
|
public static enum Color {
|
||||||
WHITE, BLACK;
|
WHITE, BLACK;
|
||||||
|
|
||||||
public Color opposite() {
|
public static Color fromFirstChar(char c) {
|
||||||
return this == WHITE ? BLACK : WHITE;
|
return Character.toLowerCase(c) == 'w' ? WHITE : BLACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
public char firstChar() {
|
public char firstChar() {
|
||||||
return this == WHITE ? 'w' : 'b';
|
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.GameState;
|
||||||
import dev.kske.chess.board.Move;
|
import dev.kske.chess.board.Move;
|
||||||
import dev.kske.chess.board.Piece.Color;
|
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.game.ai.AIPlayer;
|
||||||
import dev.kske.chess.ui.BoardComponent;
|
import dev.kske.chess.ui.BoardComponent;
|
||||||
import dev.kske.chess.ui.BoardPane;
|
import dev.kske.chess.ui.BoardPane;
|
||||||
@ -24,18 +26,29 @@ import dev.kske.chess.ui.OverlayComponent;
|
|||||||
*/
|
*/
|
||||||
public class Game {
|
public class Game {
|
||||||
|
|
||||||
private Map<Color, Player> players;
|
private Map<Color, Player> players = new HashMap<>();
|
||||||
private Board board;
|
private Board board;
|
||||||
private OverlayComponent overlayComponent;
|
private OverlayComponent overlayComponent;
|
||||||
private BoardComponent boardComponent;
|
private BoardComponent boardComponent;
|
||||||
|
|
||||||
public Game(BoardPane boardPane, String whiteName, String blackName) {
|
public Game(BoardPane boardPane, String whiteName, String blackName) {
|
||||||
players = new HashMap<>();
|
|
||||||
board = new Board();
|
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();
|
overlayComponent = boardPane.getOverlayComponent();
|
||||||
boardComponent = boardPane.getBoardComponent();
|
boardComponent = boardPane.getBoardComponent();
|
||||||
boardComponent.setBoard(board);
|
boardComponent.setBoard(board);
|
||||||
|
|
||||||
|
// Initialize players
|
||||||
players.put(Color.WHITE, getPlayer(whiteName, Color.WHITE));
|
players.put(Color.WHITE, getPlayer(whiteName, Color.WHITE));
|
||||||
players.put(Color.BLACK, getPlayer(blackName, Color.BLACK));
|
players.put(Color.BLACK, getPlayer(blackName, Color.BLACK));
|
||||||
|
|
||||||
@ -59,12 +72,17 @@ public class Game {
|
|||||||
|
|
||||||
public void onMove(Player player, Move move) {
|
public void onMove(Player player, Move move) {
|
||||||
if (board.getPos(move).getColor() == player.color && board.attemptMove(move)) {
|
if (board.getPos(move).getColor() == player.color && board.attemptMove(move)) {
|
||||||
|
|
||||||
// Redraw
|
// Redraw
|
||||||
boardComponent.repaint();
|
boardComponent.repaint();
|
||||||
overlayComponent.displayArrow(move);
|
overlayComponent.displayArrow(move);
|
||||||
|
|
||||||
|
// Run garbage collection
|
||||||
|
System.gc();
|
||||||
|
|
||||||
System.out.printf("%s: %s%n", player.color, move);
|
System.out.printf("%s: %s%n", player.color, move);
|
||||||
System.out.println("FEN: " + board.toFEN());
|
System.out.println("FEN: " + board.toFEN());
|
||||||
|
EventBus.getInstance().dispatch(new MoveEvent(move));
|
||||||
GameState eventType = board.getGameEventType(board.getDest(move).getColor().opposite());
|
GameState eventType = board.getGameEventType(board.getDest(move).getColor().opposite());
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case CHECKMATE:
|
case CHECKMATE:
|
||||||
@ -76,30 +94,33 @@ public class Game {
|
|||||||
case CHECK:
|
case CHECK:
|
||||||
System.out.printf("%s in check!%n", player.color.opposite());
|
System.out.printf("%s in check!%n", player.color.opposite());
|
||||||
default:
|
default:
|
||||||
players.get(board.getActiveColor()).requestMove();
|
players.get(board.getLog().getActiveColor()).requestMove();
|
||||||
}
|
}
|
||||||
} else player.requestMove();
|
} else player.requestMove();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
players.get(Color.WHITE).requestMove();
|
players.get(board.getLog().getActiveColor()).requestMove();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
players.values().forEach(Player::cancelMove);
|
players.values().forEach(Player::cancelMove);
|
||||||
board.initializeDefaultPositions();
|
board.initDefaultPositions();
|
||||||
boardComponent.repaint();
|
boardComponent.repaint();
|
||||||
overlayComponent.clearDots();
|
overlayComponent.clearDots();
|
||||||
overlayComponent.clearArrow();
|
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);
|
players.values().forEach(Player::disconnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns the players their opposite colors.
|
||||||
|
*/
|
||||||
public void swapColors() {
|
public void swapColors() {
|
||||||
players.values().forEach(Player::cancelMove);
|
players.values().forEach(Player::cancelMove);
|
||||||
Player white = players.get(Color.WHITE);
|
Player white = players.get(Color.WHITE);
|
||||||
@ -108,10 +129,16 @@ public class Game {
|
|||||||
black.setColor(Color.WHITE);
|
black.setColor(Color.WHITE);
|
||||||
players.put(Color.WHITE, black);
|
players.put(Color.WHITE, black);
|
||||||
players.put(Color.BLACK, white);
|
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; }
|
public Board getBoard() { return board; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The players participating in this game
|
||||||
|
*/
|
||||||
public Map<Color, Player> getPlayers() { return players; }
|
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(),
|
pos = new Position(evt.getPoint().x / overlayComponent.getTileSize(),
|
||||||
evt.getPoint().y / 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) {
|
if (board.get(pos) != null && board.get(pos).getColor() == color) {
|
||||||
List<Position> positions = board.getMoves(pos)
|
List<Position> positions = board.getMoves(pos)
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -50,7 +50,7 @@ public class AIPlayer extends Player {
|
|||||||
/*
|
/*
|
||||||
* Get a copy of the board and the available moves.
|
* 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);
|
List<Move> moves = board.getMoves(color);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -64,7 +64,7 @@ public class AIPlayer extends Player {
|
|||||||
for (int i = 0; i < numThreads; i++) {
|
for (int i = 0; i < numThreads; i++) {
|
||||||
if (rem-- > 0) ++endIndex;
|
if (rem-- > 0) ++endIndex;
|
||||||
endIndex += step;
|
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));
|
maxDepth, alphaBetaThreshold));
|
||||||
beginIndex = endIndex;
|
beginIndex = endIndex;
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ public interface UCIListener {
|
|||||||
/**
|
/**
|
||||||
* The engine sends information to the GUI.
|
* 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) {}
|
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.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import javax.swing.DefaultComboBoxModel;
|
import javax.swing.DefaultComboBoxModel;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@ -17,66 +18,59 @@ import javax.swing.JLabel;
|
|||||||
* Created: <strong>24.07.2019</strong><br>
|
* Created: <strong>24.07.2019</strong><br>
|
||||||
* Author: <strong>Kai S. K. Engelbart</strong>
|
* 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;
|
public static void show(BiConsumer<String, String> action) {
|
||||||
private boolean startGame;
|
new JDialog() {
|
||||||
|
|
||||||
/**
|
private static final long serialVersionUID = -5768339760489440385L;
|
||||||
* Create the dialog.
|
|
||||||
*/
|
|
||||||
public GameConfigurationDialog() {
|
|
||||||
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"));
|
setTitle("Game Configuration");
|
||||||
EngineUtil.getEngineInfos().forEach(info -> options.add(info.name));
|
setBounds(100, 100, 281, 142);
|
||||||
|
setModal(true);
|
||||||
|
setLocationRelativeTo(null);
|
||||||
|
getContentPane().setLayout(null);
|
||||||
|
|
||||||
JLabel lblWhite = new JLabel("White:");
|
List<String> options = new ArrayList<>(Arrays.asList("Natural Player", "AI Player"));
|
||||||
lblWhite.setFont(new Font("Tahoma", Font.PLAIN, 14));
|
EngineUtil.getEngineInfos().forEach(info -> options.add(info.name));
|
||||||
lblWhite.setBounds(10, 11, 49, 14);
|
|
||||||
getContentPane().add(lblWhite);
|
|
||||||
|
|
||||||
JComboBox<Object> cbWhite = new JComboBox<>();
|
JLabel lblWhite = new JLabel("White:");
|
||||||
cbWhite.setModel(new DefaultComboBoxModel<Object>(options.toArray()));
|
lblWhite.setFont(new Font("Tahoma", Font.PLAIN, 14));
|
||||||
cbWhite.setBounds(98, 9, 159, 22);
|
lblWhite.setBounds(10, 11, 49, 14);
|
||||||
getContentPane().add(cbWhite);
|
getContentPane().add(lblWhite);
|
||||||
|
|
||||||
JLabel lblBlack = new JLabel("Black:");
|
JComboBox<Object> cbWhite = new JComboBox<>();
|
||||||
lblBlack.setFont(new Font("Tahoma", Font.PLAIN, 14));
|
cbWhite.setModel(new DefaultComboBoxModel<Object>(options.toArray()));
|
||||||
lblBlack.setBounds(10, 38, 49, 14);
|
cbWhite.setBounds(98, 9, 159, 22);
|
||||||
getContentPane().add(lblBlack);
|
getContentPane().add(cbWhite);
|
||||||
|
|
||||||
JComboBox<Object> cbBlack = new JComboBox<>();
|
JLabel lblBlack = new JLabel("Black:");
|
||||||
cbBlack.setModel(new DefaultComboBoxModel<Object>(options.toArray()));
|
lblBlack.setFont(new Font("Tahoma", Font.PLAIN, 14));
|
||||||
cbBlack.setBounds(98, 36, 159, 22);
|
lblBlack.setBounds(10, 38, 49, 14);
|
||||||
getContentPane().add(cbBlack);
|
getContentPane().add(lblBlack);
|
||||||
|
|
||||||
JButton btnStart = new JButton("Start");
|
JComboBox<Object> cbBlack = new JComboBox<>();
|
||||||
btnStart.addActionListener((evt) -> {
|
cbBlack.setModel(new DefaultComboBoxModel<Object>(options.toArray()));
|
||||||
startGame = true;
|
cbBlack.setBounds(98, 36, 159, 22);
|
||||||
whiteName = options.get(cbWhite.getSelectedIndex());
|
getContentPane().add(cbBlack);
|
||||||
blackName = options.get(cbBlack.getSelectedIndex());
|
|
||||||
dispose();
|
|
||||||
});
|
|
||||||
btnStart.setBounds(20, 73, 89, 23);
|
|
||||||
getContentPane().add(btnStart);
|
|
||||||
|
|
||||||
JButton btnCancel = new JButton("Cancel");
|
JButton btnStart = new JButton("Start");
|
||||||
btnCancel.addActionListener((evt) -> dispose());
|
btnStart.addActionListener((evt) -> {
|
||||||
btnCancel.setBounds(157, 73, 89, 23);
|
dispose();
|
||||||
getContentPane().add(btnCancel);
|
action.accept(options.get(cbWhite.getSelectedIndex()), options.get(cbBlack.getSelectedIndex()));
|
||||||
|
});
|
||||||
|
btnStart.setBounds(20, 73, 89, 23);
|
||||||
|
getContentPane().add(btnStart);
|
||||||
|
|
||||||
|
JButton btnCancel = new JButton("Cancel");
|
||||||
|
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;
|
package dev.kske.chess.ui;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.awt.GridLayout;
|
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.dnd.DropTarget;
|
||||||
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JTabbedPane;
|
||||||
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>
|
* Project: <strong>Chess</strong><br>
|
||||||
@ -20,27 +13,21 @@ import dev.kske.chess.game.NaturalPlayer;
|
|||||||
* Created: <strong>01.07.2019</strong><br>
|
* Created: <strong>01.07.2019</strong><br>
|
||||||
* Author: <strong>Kai S. K. Engelbart</strong>
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
*/
|
*/
|
||||||
public class MainWindow {
|
public class MainWindow extends JFrame {
|
||||||
|
|
||||||
private JFrame mframe;
|
private static final long serialVersionUID = -3100939302567978977L;
|
||||||
private JButton btnRestart, btnSwapColors;
|
|
||||||
private BoardPane boardPane;
|
private JTabbedPane tabbedPane;
|
||||||
private Game game;
|
|
||||||
private Color activeColor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launch the application.
|
* Launch the application.
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
EventQueue.invokeLater(new Runnable() {
|
EventQueue.invokeLater(() -> {
|
||||||
|
try {
|
||||||
public void run() {
|
new MainWindow();
|
||||||
try {
|
} catch (Exception ex) {
|
||||||
MainWindow window = new MainWindow();
|
ex.printStackTrace();
|
||||||
window.mframe.setVisible(true);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -49,6 +36,7 @@ public class MainWindow {
|
|||||||
* Create the application.
|
* Create the application.
|
||||||
*/
|
*/
|
||||||
public MainWindow() {
|
public MainWindow() {
|
||||||
|
super("Chess by Kai S. K. Engelbart");
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,58 +44,48 @@ public class MainWindow {
|
|||||||
* Initialize the contents of the frame.
|
* Initialize the contents of the frame.
|
||||||
*/
|
*/
|
||||||
private void initialize() {
|
private void initialize() {
|
||||||
mframe = new JFrame("Chess by Kai S. K. Engelbart");
|
// Configure frame
|
||||||
mframe.setResizable(false);
|
setResizable(false);
|
||||||
mframe.setBounds(100, 100, 494, 565);
|
setBounds(100, 100, 494, 565);
|
||||||
mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
mframe.setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("/pieces/queen_white.png")));
|
setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("/pieces/queen_white.png")));
|
||||||
|
|
||||||
boardPane = new BoardPane();
|
// Add frame content
|
||||||
mframe.getContentPane().add(boardPane, BorderLayout.CENTER);
|
tabbedPane = new JTabbedPane();
|
||||||
|
getContentPane().add(tabbedPane);
|
||||||
|
|
||||||
JPanel toolPanel = new JPanel();
|
setJMenuBar(new MenuBar(this));
|
||||||
btnRestart = new JButton("Restart");
|
new DropTarget(this, new FENDropTarget(this));
|
||||||
btnRestart.addActionListener((evt) -> { if (game != null) game.reset(); game.start(); });
|
|
||||||
|
|
||||||
activeColor = Color.WHITE;
|
// Update position and dimensions
|
||||||
btnSwapColors = new JButton("Play as black");
|
pack();
|
||||||
btnSwapColors.addActionListener((evt) -> {
|
setLocationRelativeTo(null);
|
||||||
game.swapColors();
|
setVisible(true);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
* Removes a {@link GamePane} form the tabbed pane.
|
||||||
this.game = game;
|
*
|
||||||
btnSwapColors.setEnabled(game.getPlayers().get(Color.WHITE) instanceof NaturalPlayer
|
* @param index The index of the {@link GamePane} to remove
|
||||||
^ game.getPlayers().get(Color.BLACK) instanceof NaturalPlayer);
|
*/
|
||||||
|
public void removeGamePane(int index) {
|
||||||
|
tabbedPane.remove(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,10 @@ public class MenuBar extends JMenuBar {
|
|||||||
|
|
||||||
private static final long serialVersionUID = -7221583703531248228L;
|
private static final long serialVersionUID = -7221583703531248228L;
|
||||||
|
|
||||||
private final MainWindow mainWindow;
|
private final MainWindow mainWindow;
|
||||||
private final BoardPane boardPane;
|
|
||||||
|
|
||||||
public MenuBar(MainWindow mainWindow) {
|
public MenuBar(MainWindow mainWindow) {
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
boardPane = mainWindow.getBoardPane();
|
|
||||||
|
|
||||||
initGameMenu();
|
initGameMenu();
|
||||||
initEngineMenu();
|
initEngineMenu();
|
||||||
@ -36,16 +34,16 @@ public class MenuBar extends JMenuBar {
|
|||||||
JMenu gameMenu = new JMenu("Game");
|
JMenu gameMenu = new JMenu("Game");
|
||||||
JMenuItem newGameMenuItem = new JMenuItem("New Game");
|
JMenuItem newGameMenuItem = new JMenuItem("New Game");
|
||||||
newGameMenuItem.addActionListener((evt) -> {
|
newGameMenuItem.addActionListener((evt) -> {
|
||||||
GameConfigurationDialog dialog = new GameConfigurationDialog();
|
GameConfigurationDialog.show((whiteName, blackName) -> {
|
||||||
dialog.setVisible(true);
|
GamePane gamePane = mainWindow.addGamePane();
|
||||||
if (dialog.isStartGame()) startGame(new Game(boardPane, dialog.getWhiteName(), dialog.getBlackName()));
|
Game game = new Game(gamePane.getBoardPane(), whiteName, blackName);
|
||||||
|
gamePane.setGame(game);
|
||||||
|
game.start();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
gameMenu.add(newGameMenuItem);
|
gameMenu.add(newGameMenuItem);
|
||||||
|
|
||||||
add(gameMenu);
|
add(gameMenu);
|
||||||
|
newGameMenuItem.doClick();
|
||||||
// Start a game
|
|
||||||
startGame(new Game(boardPane, "Natural Player", "Natural Player"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initEngineMenu() {
|
private void initEngineMenu() {
|
||||||
@ -71,19 +69,25 @@ public class MenuBar extends JMenuBar {
|
|||||||
JMenu toolsMenu = new JMenu("Tools");
|
JMenu toolsMenu = new JMenu("Tools");
|
||||||
|
|
||||||
JMenuItem exportFENMenuItem = new JMenuItem("Export board to FEN");
|
JMenuItem exportFENMenuItem = new JMenuItem("Export board to FEN");
|
||||||
exportFENMenuItem.addActionListener((evt) -> Toolkit.getDefaultToolkit()
|
exportFENMenuItem.addActionListener((evt) -> {
|
||||||
.getSystemClipboard()
|
final String fen = mainWindow.getSelectedGamePane().getGame().getBoard().toFEN();
|
||||||
.setContents(new StringSelection(mainWindow.getGame().getBoard().toFEN()), null));
|
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(fen), null);
|
||||||
|
JOptionPane.showMessageDialog(mainWindow, String.format("FEN-string copied to clipboard!%n%s", fen));
|
||||||
|
});
|
||||||
toolsMenu.add(exportFENMenuItem);
|
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);
|
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 {
|
public class TextureUtil {
|
||||||
|
|
||||||
private static Map<String, Image> textures, scaledTextures;
|
private static Map<String, Image> textures = new HashMap<>(), scaledTextures = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
textures = new HashMap<>();
|
|
||||||
scaledTextures = new HashMap<>();
|
|
||||||
loadPieceTextures();
|
loadPieceTextures();
|
||||||
scaledTextures.putAll(textures);
|
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.assertNotEquals;
|
||||||
import static org.junit.Assert.assertNotSame;
|
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.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
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.Piece.Color;
|
||||||
import dev.kske.chess.board.Queen;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project: <strong>Chess</strong><br>
|
* Project: <strong>Chess</strong><br>
|
||||||
@ -34,13 +31,13 @@ class BoardTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testClone() {
|
void testClone() {
|
||||||
Board clone = (Board) board.clone();
|
Board clone = new Board(board);
|
||||||
assertNotSame(clone, board);
|
assertNotSame(clone, board);
|
||||||
assertNotSame(clone.getBoardArr(), board.getBoardArr());
|
assertNotSame(clone.getBoardArr(), board.getBoardArr());
|
||||||
|
|
||||||
clone.getBoardArr()[0][0] = new Queen(Color.BLACK, clone);
|
clone.getBoardArr()[0][0] = new Queen(Color.BLACK, clone);
|
||||||
clone.move(new Move(1, 1, 1, 2));
|
clone.move(new Move(1, 1, 1, 2));
|
||||||
assertNotEquals(clone.getBoardArr()[0][0], board.getBoardArr()[0][0]);
|
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