Fixed castling, added castling export to FEN
+ isFreePath implementation in Piece - Removed isFreePath from Bishop, Rook, Queen and King + canCastleKingside and canCastleQueenside methods in King + Castling rights record in Board + FEN export + equals method in Position + UCI 'position startpos' command - Switched to Java 8 compliance for compatibility reasons
This commit is contained in:
parent
46b74a527d
commit
a6fcaee70e
@ -7,7 +7,7 @@
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk1.8.0_212">
|
||||
<attributes>
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
|
@ -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<Move> getPseudolegalMoves(Position pos) {
|
||||
List<Move> moves = new ArrayList<>();
|
||||
|
@ -17,10 +17,11 @@ import dev.kske.chess.board.Piece.Type;
|
||||
*/
|
||||
public class Board implements Cloneable {
|
||||
|
||||
private Piece[][] boardArr;
|
||||
private Map<Color, Position> kingPos;
|
||||
private Color activeColor;
|
||||
private Log log;
|
||||
private Piece[][] boardArr;
|
||||
private Map<Color, Position> kingPos;
|
||||
private Map<Color, Map<Type, Boolean>> 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<Type, Boolean> 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; }
|
||||
}
|
||||
|
@ -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<Move> getPseudolegalMoves(Position pos) {
|
||||
List<Move> 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; }
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<Move> getPseudolegalMoves(Position pos) {
|
||||
List<Move> moves = new ArrayList<>();
|
||||
|
@ -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<Move> getPseudolegalMoves(Position pos) {
|
||||
List<Move> moves = new ArrayList<>();
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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();
|
||||
|
54
src/dev/kske/chess/ui/LogFrame.java
Normal file
54
src/dev/kske/chess/ui/LogFrame.java
Normal file
@ -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: <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;
|
||||
|
||||
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; }
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user