diff --git a/.classpath b/.classpath
index 83f2c4a..d0a08f5 100644
--- a/.classpath
+++ b/.classpath
@@ -7,7 +7,7 @@
-
+
diff --git a/src/dev/kske/chess/board/Bishop.java b/src/dev/kske/chess/board/Bishop.java
index c4dc97d..8f2cb9d 100644
--- a/src/dev/kske/chess/board/Bishop.java
+++ b/src/dev/kske/chess/board/Bishop.java
@@ -20,14 +20,6 @@ public class Bishop extends Piece {
return move.isDiagonal() && isFreePath(move);
}
- @Override
- protected boolean isFreePath(Move move) {
- for (int i = move.pos.x + move.xSign, j = move.pos.y
- + move.ySign; i != move.dest.x; i += move.xSign, j += move.ySign)
- if (board.getBoardArr()[i][j] != null) return false;
- return checkDestination(move);
- }
-
@Override
protected List getPseudolegalMoves(Position pos) {
List moves = new ArrayList<>();
diff --git a/src/dev/kske/chess/board/Board.java b/src/dev/kske/chess/board/Board.java
index 7404fc3..88cb298 100644
--- a/src/dev/kske/chess/board/Board.java
+++ b/src/dev/kske/chess/board/Board.java
@@ -17,10 +17,11 @@ import dev.kske.chess.board.Piece.Type;
*/
public class Board implements Cloneable {
- private Piece[][] boardArr;
- private Map kingPos;
- private Color activeColor;
- private Log log;
+ private Piece[][] boardArr;
+ private Map kingPos;
+ private Map> castlingRights;
+ private Color activeColor;
+ private Log log;
private int fullmoveCounter, halfmoveClock;
@@ -62,9 +63,10 @@ public class Board implements Cloneable {
}
public Board() {
- boardArr = new Piece[8][8];
- kingPos = new HashMap<>();
- log = new Log();
+ boardArr = new Piece[8][8];
+ kingPos = new HashMap<>();
+ castlingRights = new HashMap<>();
+ log = new Log();
initializeDefaultPositions();
}
@@ -140,8 +142,13 @@ public class Board implements Cloneable {
// Increment move counter
getDest(move).incMoveCounter();
- // Update the king's position if the moved piece is the king
- if (piece.getType() == Type.KING) kingPos.put(piece.getColor(), move.dest);
+ // Update the king's position if the moved piece is the king and castling
+ // availability
+ if (piece.getType() == Type.KING) {
+ kingPos.put(piece.getColor(), move.dest);
+ castlingRights.get(piece.getColor()).put(Type.KING, false);
+ castlingRights.get(piece.getColor()).put(Type.QUEEN, false);
+ }
// Update log
log.add(move, capturePiece);
@@ -154,6 +161,8 @@ public class Board implements Cloneable {
// Increment halfmove clock
++halfmoveClock;
+
+ updateCastlingRights();
}
/**
@@ -211,6 +220,30 @@ public class Board implements Cloneable {
// Decrement halfmove clock
--halfmoveClock;
+
+ updateCastlingRights();
+ }
+
+ private void updateCastlingRights() {
+ // White
+ if (new Position(4, 7).equals(kingPos.get(Color.WHITE))) {
+ final King king = (King) get(kingPos.get(Color.WHITE));
+ castlingRights.get(Color.WHITE).put(Type.KING, king.canCastleKingside());
+ castlingRights.get(Color.WHITE).put(Type.QUEEN, king.canCastleQueenside());
+ } else {
+ castlingRights.get(Color.WHITE).put(Type.KING, false);
+ castlingRights.get(Color.WHITE).put(Type.QUEEN, false);
+ }
+
+ // Black
+ if (new Position(4, 0).equals(kingPos.get(Color.BLACK))) {
+ final King king = (King) get(kingPos.get(Color.BLACK));
+ castlingRights.get(Color.BLACK).put(Type.KING, king.canCastleKingside());
+ castlingRights.get(Color.BLACK).put(Type.QUEEN, king.canCastleQueenside());
+ } else {
+ castlingRights.get(Color.BLACK).put(Type.KING, false);
+ castlingRights.get(Color.BLACK).put(Type.QUEEN, false);
+ }
}
/**
@@ -354,9 +387,19 @@ public class Board implements Cloneable {
for (int j = 2; j < 6; j++)
boardArr[i][j] = null;
+ // Initialize castling rights
+ castlingRights.clear();
+ Map whiteCastling = new HashMap<>(), blackCastling = new HashMap<>();
+ whiteCastling.put(Type.KING, true);
+ whiteCastling.put(Type.QUEEN, true);
+ blackCastling.put(Type.KING, true);
+ blackCastling.put(Type.QUEEN, true);
+ castlingRights.put(Color.WHITE, whiteCastling);
+ castlingRights.put(Color.BLACK, blackCastling);
+
activeColor = Color.WHITE;
-
- fullmoveCounter = 1;
+
+ fullmoveCounter = 1;
halfmoveClock = 0;
}
@@ -437,8 +480,14 @@ public class Board implements Cloneable {
// Active color
sb.append(" " + (activeColor == Color.WHITE ? 'w' : 'b'));
- // TODO: castling rights
- sb.append(" -");
+ sb.append(' ');
+ StringBuilder castlingSb = new StringBuilder();
+ if (castlingRights.get(Color.WHITE).get(Type.KING)) castlingSb.append('K');
+ if (castlingRights.get(Color.WHITE).get(Type.QUEEN)) castlingSb.append('Q');
+ if (castlingRights.get(Color.BLACK).get(Type.KING)) castlingSb.append('k');
+ if (castlingRights.get(Color.BLACK).get(Type.QUEEN)) castlingSb.append('q');
+ if (castlingSb.length() == 0) sb.append("-");
+ sb.append(castlingSb);
// TODO: en passant availability
sb.append(" -");
@@ -481,5 +530,8 @@ public class Board implements Cloneable {
*/
public Piece[][] getBoardArr() { return boardArr; }
+ /**
+ * @return The active color for the next move
+ */
public Color getActiveColor() { return activeColor; }
}
diff --git a/src/dev/kske/chess/board/King.java b/src/dev/kske/chess/board/King.java
index 7c0ad2e..78f672d 100644
--- a/src/dev/kske/chess/board/King.java
+++ b/src/dev/kske/chess/board/King.java
@@ -18,27 +18,40 @@ public class King extends Piece {
@Override
public boolean isValidMove(Move move) {
// Castling
- if (getMoveCounter() == 0 && move.xDist == 2 && move.yDist == 0) {
-
- // Kingside
- if (board.getBoardArr()[7][move.pos.y] != null && board.getBoardArr()[7][move.pos.y].getType() == Type.ROOK
- && isFreePath(new Move(new Position(5, move.pos.y), new Position(7, move.pos.y)))) {
+ if (move.xDist == 2 && move.yDist == 0) {
+ if (canCastleKingside()) {
move.type = Move.Type.CASTLING;
return true;
}
-
- // Queenside
- if (board.getBoardArr()[0][move.pos.y] != null && board.getBoardArr()[0][move.pos.y].getType() == Type.ROOK
- && isFreePath(new Move(new Position(1, move.pos.y), new Position(4, move.pos.y)))) {
+ if (canCastleQueenside()) {
move.type = Move.Type.CASTLING;
return true;
}
-
}
return move.xDist <= 1 && move.yDist <= 1 && checkDestination(move);
}
+ public boolean canCastleKingside() {
+ if (getMoveCounter() == 0) {
+ int y = getColor() == Color.WHITE ? 7 : 0;
+ Position rookPos = new Position(7, y);
+ Piece rook = board.get(rookPos);
+ return rook != null && rook.getType() == Type.ROOK && rook.getMoveCounter() == 0
+ && isFreePath(new Move(new Position(4, y), new Position(6, y)));
+ } else return false;
+ }
+
+ public boolean canCastleQueenside() {
+ if (getMoveCounter() == 0) {
+ int y = getColor() == Color.WHITE ? 7 : 0;
+ Position rookPos = new Position(0, y);
+ Piece rook = board.get(rookPos);
+ return rook != null && rook.getType() == Type.ROOK && rook.getMoveCounter() == 0
+ && isFreePath(new Move(new Position(4, y), new Position(1, y)));
+ } else return false;
+ }
+
@Override
protected List getPseudolegalMoves(Position pos) {
List moves = new ArrayList<>();
@@ -50,31 +63,13 @@ public class King extends Piece {
}
// Castling
- // TODO: Check attacked squares in between
- // TODO: Castling out of check?
- if (getMoveCounter() == 0) {
-
- // Kingside
- if (board.getBoardArr()[7][pos.y] != null && board.getBoardArr()[7][pos.y].getType() == Type.ROOK
- && isFreePath(new Move(new Position(5, pos.y), new Position(7, pos.y))))
- moves.add(new Move(pos, new Position(6, pos.y), Move.Type.CASTLING));
-
- // Queenside
- if (board.getBoardArr()[0][pos.y] != null && board.getBoardArr()[0][pos.y].getType() == Type.ROOK
- && isFreePath(new Move(new Position(1, pos.y), new Position(4, pos.y))))
- moves.add(new Move(pos, new Position(2, pos.y), Move.Type.CASTLING));
- }
+ // TODO: Condition: cannot castle out of, through or into check
+ if (canCastleKingside()) moves.add(new Move(pos, new Position(6, pos.y), Move.Type.CASTLING));
+ if (canCastleQueenside()) moves.add(new Move(pos, new Position(2, pos.y), Move.Type.CASTLING));
return moves;
}
- @Override
- protected boolean isFreePath(Move move) {
- for (int i = move.pos.x, j = move.pos.y; i != move.dest.x || j != move.dest.y; i += move.xSign, j += move.ySign)
- if (board.getBoardArr()[i][j] != null) return false;
- return true;
- }
-
@Override
public Type getType() { return Type.KING; }
}
diff --git a/src/dev/kske/chess/board/Piece.java b/src/dev/kske/chess/board/Piece.java
index 3298a8d..0006e84 100644
--- a/src/dev/kske/chess/board/Piece.java
+++ b/src/dev/kske/chess/board/Piece.java
@@ -35,8 +35,16 @@ public abstract class Piece implements Cloneable {
public abstract boolean isValidMove(Move move);
+ /**
+ * Checks, if the squares between the position and the destination of a move are
+ * free.
+ *
+ * @param move The move to check
+ */
protected boolean isFreePath(Move move) {
- // Only check destination by default
+ for (int i = move.pos.x + move.xSign, j = move.pos.y + move.ySign; i != move.dest.x
+ || j != move.dest.y; i += move.xSign, j += move.ySign)
+ if (board.getBoardArr()[i][j] != null) return false;
return checkDestination(move);
}
diff --git a/src/dev/kske/chess/board/Position.java b/src/dev/kske/chess/board/Position.java
index a3dcadd..69bb03b 100644
--- a/src/dev/kske/chess/board/Position.java
+++ b/src/dev/kske/chess/board/Position.java
@@ -23,4 +23,24 @@ public class Position {
public String toString() {
return String.format("[%d, %d]", x, y);
}
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + x;
+ result = prime * result + y;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ Position other = (Position) obj;
+ if (x != other.x) return false;
+ if (y != other.y) return false;
+ return true;
+ }
}
diff --git a/src/dev/kske/chess/board/Queen.java b/src/dev/kske/chess/board/Queen.java
index 29a2ba6..6fcc401 100644
--- a/src/dev/kske/chess/board/Queen.java
+++ b/src/dev/kske/chess/board/Queen.java
@@ -20,22 +20,6 @@ public class Queen extends Piece {
return ((move.isHorizontal() || move.isVertical()) || move.isDiagonal()) && isFreePath(move);
}
- @Override
- protected boolean isFreePath(Move move) {
- if (move.isHorizontal()) {
- for (int i = move.pos.x + move.xSign; i != move.dest.x; i += move.xSign)
- if (board.getBoardArr()[i][move.pos.y] != null) return false;
- } else if (move.isVertical()) {
- for (int i = move.pos.y + move.ySign; i != move.dest.y; i += move.ySign)
- if (board.getBoardArr()[move.pos.x][i] != null) return false;
- } else {
- for (int i = move.pos.x + move.xSign, j = move.pos.y
- + move.ySign; i != move.dest.x; i += move.xSign, j += move.ySign)
- if (board.getBoardArr()[i][j] != null) return false;
- }
- return checkDestination(move);
- }
-
@Override
protected List getPseudolegalMoves(Position pos) {
List moves = new ArrayList<>();
diff --git a/src/dev/kske/chess/board/Rook.java b/src/dev/kske/chess/board/Rook.java
index 158eefe..affc7b0 100644
--- a/src/dev/kske/chess/board/Rook.java
+++ b/src/dev/kske/chess/board/Rook.java
@@ -20,18 +20,6 @@ public class Rook extends Piece {
return (move.isHorizontal() || move.isVertical()) && isFreePath(move);
}
- @Override
- protected boolean isFreePath(Move move) {
- if (move.isHorizontal()) {
- for (int i = move.pos.x + move.xSign; i != move.dest.x; i += move.xSign)
- if (board.getBoardArr()[i][move.pos.y] != null) return false;
- } else {
- for (int i = move.pos.y + move.ySign; i != move.dest.y; i += move.ySign)
- if (board.getBoardArr()[move.pos.x][i] != null) return false;
- }
- return checkDestination(move);
- }
-
@Override
protected List getPseudolegalMoves(Position pos) {
List moves = new ArrayList<>();
diff --git a/src/dev/kske/chess/uci/UCIHandle.java b/src/dev/kske/chess/uci/UCIHandle.java
index 8c8be2a..cf13e63 100644
--- a/src/dev/kske/chess/uci/UCIHandle.java
+++ b/src/dev/kske/chess/uci/UCIHandle.java
@@ -94,6 +94,13 @@ public class UCIHandle {
// TODO: position
+ /**
+ * Sets up the position in its initial state.
+ */
+ public void startPosition() {
+ out.println("position startpos");
+ }
+
/**
* Sets up the position described in the FEN string.
*
diff --git a/src/dev/kske/chess/uci/UCIReceiver.java b/src/dev/kske/chess/uci/UCIReceiver.java
index 5793356..fbfb686 100644
--- a/src/dev/kske/chess/uci/UCIReceiver.java
+++ b/src/dev/kske/chess/uci/UCIReceiver.java
@@ -32,7 +32,7 @@ public class UCIReceiver implements Runnable {
String line;
while (!Thread.currentThread().isInterrupted())
try {
- if ((line = in.readLine()) != null && !line.isBlank()) parse(line);
+ if ((line = in.readLine()) != null && !line.isEmpty()) parse(line);
} catch (IndexOutOfBoundsException ex) {
System.err.println("Too few arguments were provided!");
ex.printStackTrace();
diff --git a/src/dev/kske/chess/ui/LogFrame.java b/src/dev/kske/chess/ui/LogFrame.java
new file mode 100644
index 0000000..cabcabc
--- /dev/null
+++ b/src/dev/kske/chess/ui/LogFrame.java
@@ -0,0 +1,54 @@
+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.Log;
+
+/**
+ * Project: Chess
+ * File: LogFrame.java
+ * Created: 17.07.2019
+ * Author: Kai S. K. Engelbart
+ */
+public class LogFrame extends JFrame {
+
+ private static final long serialVersionUID = 1932671698254197119L;
+
+ private JPanel mcontentPane;
+ private JTable mtable;
+
+ private Log log;
+
+ /**
+ * 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[] { "Black", "New column" }));
+ mtable.setEnabled(false);
+ mcontentPane.add(mtable, BorderLayout.CENTER);
+ }
+
+ public void update() {
+
+ }
+
+ public Log getLog() { return log; }
+
+ public void setLog(Log log) { this.log = log; }
+
+}
diff --git a/test/dev/kske/chess/test/BoardTest.java b/test/dev/kske/chess/test/BoardTest.java
index 415d3a4..44212ee 100644
--- a/test/dev/kske/chess/test/BoardTest.java
+++ b/test/dev/kske/chess/test/BoardTest.java
@@ -7,6 +7,7 @@ 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;
@@ -38,6 +39,8 @@ class BoardTest {
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());
}
}