From 4857b48e4ef7e040e3390478a1dc52d699399ead Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Fri, 25 Oct 2019 11:34:07 +0200 Subject: [PATCH] Moved castling right logging to Log * Removed move counter from Piece * Added castling right array to MoveNode and Log * Removed castling right map from Board * Added castling right serialization and deserialization to FENString * Modified LogTest --- src/dev/kske/chess/board/Board.java | 183 +++++++----------------- src/dev/kske/chess/board/FENString.java | 26 +++- src/dev/kske/chess/board/King.java | 14 +- src/dev/kske/chess/board/Log.java | 62 +++++--- src/dev/kske/chess/board/MoveNode.java | 27 ++-- src/dev/kske/chess/board/Piece.java | 15 +- test/dev/kske/chess/board/LogTest.java | 8 +- 7 files changed, 143 insertions(+), 192 deletions(-) diff --git a/src/dev/kske/chess/board/Board.java b/src/dev/kske/chess/board/Board.java index 94b481b..5c2a8ac 100644 --- a/src/dev/kske/chess/board/Board.java +++ b/src/dev/kske/chess/board/Board.java @@ -20,63 +20,55 @@ import dev.kske.chess.board.Piece.Type; */ public class Board { - private Piece[][] boardArr = new Piece[8][8]; - private Map kingPos = new HashMap<>(); - private Map> castlingRights = new HashMap<>(); - private Log log = new Log(); + private Piece[][] boardArr = new Piece[8][8]; + private Map kingPos = new HashMap<>(); + private Log log = new Log(); private static final Map positionScores; static { positionScores = new HashMap<>(); positionScores.put(Type.KING, - new int[][] { new int[] { -3, -4, -4, -5, -5, -4, -4, -3 }, + new int[][] { new int[] { -3, -4, -4, -5, -5, -4, -4, -3 }, new int[] { -3, -4, -4, -5, -4, -4, -4, -3 }, new int[] { -3, -4, -4, -5, -4, -4, -4, -3 }, new int[] { -3, -4, -4, -5, -4, -4, -4, -3 }, - new int[] { -3, -4, -4, -5, -4, -4, -4, -3 }, new int[] { -2, -3, -3, -2, -2, -2, -2, -1 }, - new int[] { -1, -2, -2, -2, -2, -2, -2, -1 }, new int[] { 2, 2, 0, 0, 0, 0, 2, 2 }, - new int[] { 2, 3, 1, 0, 0, 1, 3, 2 } }); + new int[] { -2, -3, -3, -2, -2, -2, -2, -1 }, new int[] { -1, -2, -2, -2, -2, -2, -2, -1 }, + new int[] { 2, 2, 0, 0, 0, 0, 2, 2 }, new int[] { 2, 3, 1, 0, 0, 1, 3, 2 } }); positionScores.put(Type.QUEEN, new int[][] { new int[] { -2, -1, -1, -1, -1, -1, -1, -2 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, - new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, - new int[] { 0, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 1, 1, 1, 1, 1, 0, -1 }, - new int[] { -1, 0, 1, 0, 0, 0, 0, -1 }, new int[] { -2, -1, -1, -1, -1, -1, -1, -2 } }); + new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { 0, 0, 1, 1, 1, 1, 0, -1 }, + new int[] { -1, 1, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 0, 1, 0, 0, 0, 0, -1 }, + new int[] { -2, -1, -1, -1, -1, -1, -1, -2 } }); positionScores.put(Type.ROOK, - new int[][] { new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 1, 1, 1, 1, 1, 1, 1, 1 }, - new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, - new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, + new int[][] { new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 1, 1, 1, 1, 1, 1, 1, 1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, + new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { 0, 0, 0, 1, 1, 0, 0, 0 } }); positionScores.put(Type.KNIGHT, new int[][] { new int[] { -5, -4, -3, -3, -3, -3, -4, -5 }, new int[] { -4, -2, 0, 0, 0, 0, -2, -4 }, - new int[] { -3, 0, 1, 2, 2, 1, 0, -3 }, new int[] { -3, 1, 2, 2, 2, 2, 1, -3 }, - new int[] { -3, 0, 2, 2, 2, 2, 0, -1 }, new int[] { -3, 1, 1, 2, 2, 1, 1, -3 }, - new int[] { -4, -2, 0, 1, 1, 0, -2, -4 }, new int[] { -5, -4, -3, -3, -3, -3, -4, -5 } }); + new int[] { -3, 0, 1, 2, 2, 1, 0, -3 }, new int[] { -3, 1, 2, 2, 2, 2, 1, -3 }, new int[] { -3, 0, 2, 2, 2, 2, 0, -1 }, + new int[] { -3, 1, 1, 2, 2, 1, 1, -3 }, new int[] { -4, -2, 0, 1, 1, 0, -2, -4 }, + new int[] { -5, -4, -3, -3, -3, -3, -4, -5 } }); positionScores.put(Type.BISHOP, new int[][] { new int[] { -2, -1, -1, -1, -1, -1, -1, 2 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, - new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 1, 1, 1, 1, 1, 1, -1 }, - new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 1, 1, 1, 1, 1, 1, -1 }, - new int[] { -1, 1, 0, 0, 0, 0, 1, -1 }, new int[] { -2, -1, -1, -1, -1, -1, -1, -2 } }); + new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 1, 1, 1, 1, 1, 1, -1 }, new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, + new int[] { -1, 1, 1, 1, 1, 1, 1, -1 }, new int[] { -1, 1, 0, 0, 0, 0, 1, -1 }, + new int[] { -2, -1, -1, -1, -1, -1, -1, -2 } }); positionScores.put(Type.PAWN, - new int[][] { new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 5, 5, 5, 5, 5, 5, 5, 5 }, - new int[] { 1, 1, 2, 3, 3, 2, 1, 1 }, new int[] { 0, 0, 1, 3, 3, 1, 0, 0 }, - new int[] { 0, 0, 0, 2, 2, 0, 0, 0 }, new int[] { 0, 0, -1, 0, 0, -1, 0, 0 }, + new int[][] { new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 5, 5, 5, 5, 5, 5, 5, 5 }, new int[] { 1, 1, 2, 3, 3, 2, 1, 1 }, + new int[] { 0, 0, 1, 3, 3, 1, 0, 0 }, new int[] { 0, 0, 0, 2, 2, 0, 0, 0 }, new int[] { 0, 0, -1, 0, 0, -1, 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() { - initDefaultPositions(); - } + public Board() { initDefaultPositions(); } /** * Initializes the board with data from a FEN-string. * * @param fen The FEN-string to initialize the board from */ - public Board(String fen) { - initFromFEN(fen); - } + public Board(String fen) { initFromFEN(fen); } /** * Creates a copy of another {@link Board} instance.
@@ -95,10 +87,6 @@ public class Board { } kingPos.putAll(other.kingPos); - Map 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); } @@ -158,8 +146,6 @@ public class Board { : new Move(0, move.pos.y, 3, move.pos.y); // Queenside setDest(rookMove, getPos(rookMove)); setPos(rookMove, null); - - getDest(rookMove).incMoveCounter(); break; case UNKNOWN: System.err.printf("Move of unknown type %s found!%n", move); @@ -171,17 +157,11 @@ public class Board { System.err.printf("Move %s of unimplemented type found!%n", move); } - // Increment move counter - getDest(move).incMoveCounter(); - - // Update the king's position if the moved piece is the king and castling - // availability + // Update the king's position if the moved piece is the king if (piece.getType() == Type.KING) kingPos.put(piece.getColor(), move.dest); // Update log - log.add(move, capturePiece, piece.getType() == Type.PAWN); - - updateCastlingRights(); + log.add(move, piece, capturePiece); } /** @@ -195,9 +175,7 @@ public class Board { Pattern.compile( "^(?[NBRQK])(?:(?[a-h])|(?[1-8])|(?[a-h][1-8]))?x?(?[a-h][1-8])(?:\\+{0,2}|\\#)$")); patterns.put("pawnCapture", - Pattern - .compile( - "^(?[a-h])(?[1-8])?x(?[a-h][1-8])(?[NBRQK])?(?:\\+{0,2}|\\#)?$")); + Pattern.compile("^(?[a-h])(?[1-8])?x(?[a-h][1-8])(?[NBRQK])?(?:\\+{0,2}|\\#)?$")); patterns.put("pawnPush", Pattern.compile("^(?[a-h][1-8])(?[NBRQK])?(?:\\+{0,2}|\\#)$")); patterns.put("castling", Pattern.compile("^(?O-O-O)|(?O-O)(?:\\+{0,2}|\\#)?$")); @@ -228,8 +206,7 @@ public class Board { case "pawnCapture": dest = Position.fromLAN(m.group("toSquare")); char file = m.group("fromFile").charAt(0); - int rank = m.group("fromRank") == null ? get(Type.PAWN, file) - : Integer.parseInt(m.group("fromRank")); + int rank = m.group("fromRank") == null ? get(Type.PAWN, file) : Integer.parseInt(m.group("fromRank")); pos = Position.fromLAN(String.format("%c%d", file, rank)); break; case "pawnPush": @@ -282,8 +259,6 @@ public class Board { : new Move(3, move.pos.y, 0, move.pos.y); // Queenside setDest(rookMove, getPos(rookMove)); setPos(rookMove, null); - - getDest(rookMove).decMoveCounter(); break; case UNKNOWN: System.err.printf("Move of unknown type %s found!%n", move); @@ -295,38 +270,11 @@ public class Board { System.err.printf("Move %s of unimplemented type found!%n", move); } - // Decrement move counter - getPos(move).decMoveCounter(); - // Update the king's position if the moved piece is the king if (getPos(move).getType() == Type.KING) kingPos.put(getPos(move).getColor(), move.pos); // Update log log.removeLast(); - - 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); - } } /** @@ -339,14 +287,11 @@ public class Board { List moves = new ArrayList<>(); for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++) - if (boardArr[i][j] != null && boardArr[i][j].getColor() == color) - moves.addAll(boardArr[i][j].getMoves(new Position(i, j))); + if (boardArr[i][j] != null && boardArr[i][j].getColor() == color) moves.addAll(boardArr[i][j].getMoves(new Position(i, j))); return moves; } - public List getMoves(Position pos) { - return get(pos).getMoves(pos); - } + public List getMoves(Position pos) { return get(pos).getMoves(pos); } /** * Checks, if the king is in check. @@ -358,9 +303,7 @@ public class Board { for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++) { Position pos = new Position(i, j); - if (get(pos) != null && get(pos).getColor() != color - && get(pos).isValidMove(new Move(pos, kingPos.get(color)))) - return true; + if (get(pos) != null && get(pos).getColor() != color && get(pos).isValidMove(new Move(pos, kingPos.get(color)))) return true; } return false; } @@ -388,8 +331,7 @@ public class Board { public GameState getGameEventType(Color color) { return checkCheck(color) ? checkCheckmate(color) ? GameState.CHECKMATE : GameState.CHECK - : getMoves(color).isEmpty() || log.getLast().halfmoveClock >= 50 ? GameState.STALEMATE - : GameState.NORMAL; + : getMoves(color).isEmpty() || log.getLast().halfmoveClock >= 50 ? GameState.STALEMATE : GameState.NORMAL; } /** @@ -471,15 +413,6 @@ public class Board { for (int j = 2; j < 6; j++) boardArr[i][j] = null; - // Initialize castling rights - 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); - log.reset(); } @@ -524,9 +457,7 @@ public class Board { 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); + System.err.printf("Unknown character '%c' in board declaration of FEN string '%s'%n", places[k], fen); } } } @@ -550,11 +481,10 @@ public class Board { case '-': break; default: - System.err - .printf("Unknown character '%c' in castling rights declaration of FEN string '%s'", c, fen); + 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); + // castlingRights.put(Color.WHITE, whiteCastling); + // castlingRights.put(Color.BLACK, blackCastling); // En passant availability if (!parts[3].equals("-")) log.setEnPassant(Position.fromLAN(parts[3])); @@ -597,10 +527,10 @@ public class Board { // Castling Rights 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 (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); @@ -623,7 +553,7 @@ public class Board { final int prime = 31; int result = 1; result = prime * result + Arrays.deepHashCode(boardArr); - result = prime * result + Objects.hash(castlingRights, kingPos, log); + result = prime * result + Objects.hash(kingPos, log); return result; } @@ -633,17 +563,14 @@ public class Board { if (obj == null) return false; if (getClass() != obj.getClass()) return false; Board other = (Board) obj; - return Arrays.deepEquals(boardArr, other.boardArr) && Objects.equals(castlingRights, other.castlingRights) - && Objects.equals(kingPos, other.kingPos) && Objects.equals(log, other.log); + return Arrays.deepEquals(boardArr, other.boardArr) && Objects.equals(kingPos, other.kingPos) && Objects.equals(log, other.log); } /** * @param pos The position from which to return a piece * @return The piece at the position */ - public Piece get(Position pos) { - return boardArr[pos.x][pos.y]; - } + public Piece get(Position pos) { return boardArr[pos.x][pos.y]; } /** * Searches for a {@link Piece} inside a file (A - H). @@ -656,9 +583,7 @@ public class Board { public int get(Type type, char file) { int x = file - 97; for (int i = 0; i < 8; i++) - if (boardArr[x][i] != null && boardArr[x][i].getType() == type - && boardArr[x][i].getColor() == log.getActiveColor()) - return 8 - i; + if (boardArr[x][i] != null && boardArr[x][i].getType() == type && boardArr[x][i].getColor() == log.getActiveColor()) return 8 - i; return -1; } @@ -673,8 +598,7 @@ public class Board { public char get(Type type, int rank) { int y = rank - 1; for (int i = 0; i < 8; i++) - if (boardArr[i][y] != null && boardArr[i][y].getType() == type - && boardArr[i][y].getColor() == log.getActiveColor()) + if (boardArr[i][y] != null && boardArr[i][y].getType() == type && boardArr[i][y].getColor() == log.getActiveColor()) return (char) (i + 97); return '-'; } @@ -689,8 +613,7 @@ public class Board { public Position get(Type type, Position dest) { for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++) - if (boardArr[i][j] != null && boardArr[i][j].getType() == type - && boardArr[i][j].getColor() == log.getActiveColor()) { + if (boardArr[i][j] != null && boardArr[i][j].getType() == type && boardArr[i][j].getColor() == log.getActiveColor()) { Position pos = new Position(i, j); if (boardArr[i][j].isValidMove(new Move(pos, dest))) return pos; } @@ -703,25 +626,19 @@ public class Board { * @param pos The position to place the piece at * @param piece The piece to place */ - public void set(Position pos, Piece piece) { - boardArr[pos.x][pos.y] = piece; - } + public void set(Position pos, Piece piece) { boardArr[pos.x][pos.y] = piece; } /** * @param move The move from which position to return a piece * @return The piece at the position of the move */ - public Piece getPos(Move move) { - return get(move.pos); - } + public Piece getPos(Move move) { return get(move.pos); } /** * @param move The move from which destination to return a piece * @return The piece at the destination of the move */ - public Piece getDest(Move move) { - return get(move.dest); - } + public Piece getDest(Move move) { return get(move.dest); } /** * Places a piece at the position of a move. @@ -729,9 +646,7 @@ public class Board { * @param move The move at which position to place the piece * @param piece The piece to place */ - public void setPos(Move move, Piece piece) { - set(move.pos, piece); - } + public void setPos(Move move, Piece piece) { set(move.pos, piece); } /** * Places a piece at the destination of a move. @@ -739,9 +654,7 @@ public class Board { * @param move The move at which destination to place the piece * @param piece The piece to place */ - public void setDest(Move move, Piece piece) { - set(move.dest, piece); - } + public void setDest(Move move, Piece piece) { set(move.dest, piece); } /** * @return The board array diff --git a/src/dev/kske/chess/board/FENString.java b/src/dev/kske/chess/board/FENString.java index 6183015..4811fcc 100644 --- a/src/dev/kske/chess/board/FENString.java +++ b/src/dev/kske/chess/board/FENString.java @@ -109,7 +109,24 @@ public class FENString { // Active color board.getLog().setActiveColor(activeColor); - // TODO: Castling availability + // Castling availability + boolean castlingRights[] = new boolean[4]; + for (char c : castlingAvailability.toCharArray()) + switch (c) { + case 'K': + castlingRights[MoveNode.WHITE_KINGSIDE] = true; + break; + case 'Q': + castlingRights[MoveNode.WHITE_QUEENSIDE] = true; + break; + case 'k': + castlingRights[MoveNode.BLACK_KINGSIDE] = true; + break; + case 'q': + castlingRights[MoveNode.BLACK_QUEENSIDE] = true; + break; + } + board.getLog().setCastlingRights(castlingRights); // En passant square board.getLog().setEnPassant(enPassantTargetSquare); @@ -166,7 +183,12 @@ public class FENString { // Active color activeColor = board.getLog().getActiveColor(); - // TODO: Castling availability + // Castling availability + castlingAvailability = ""; + final char castlingRightsChars[] = new char[] { 'K', 'Q', 'k', 'q' }; + for (int i = 0; i < 4; i++) + if (board.getLog().getCastlingRights()[i]) castlingAvailability += castlingRightsChars[i]; + if (castlingAvailability.isEmpty()) castlingAvailability = "-"; // En passant availability enPassantTargetSquare = board.getLog().getEnPassant(); diff --git a/src/dev/kske/chess/board/King.java b/src/dev/kske/chess/board/King.java index 78f672d..de90fbf 100644 --- a/src/dev/kske/chess/board/King.java +++ b/src/dev/kske/chess/board/King.java @@ -11,9 +11,7 @@ import java.util.List; */ public class King extends Piece { - public King(Color color, Board board) { - super(color, board); - } + public King(Color color, Board board) { super(color, board); } @Override public boolean isValidMove(Move move) { @@ -33,22 +31,20 @@ public class King extends Piece { } public boolean canCastleKingside() { - if (getMoveCounter() == 0) { + if (board.getLog().getCastlingRights()[getColor() == Color.WHITE ? MoveNode.WHITE_KINGSIDE : MoveNode.BLACK_KINGSIDE]) { 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))); + return rook != null && rook.getType() == Type.ROOK && isFreePath(new Move(new Position(4, y), new Position(6, y))); } else return false; } public boolean canCastleQueenside() { - if (getMoveCounter() == 0) { + if (board.getLog().getCastlingRights()[getColor() == Color.WHITE ? MoveNode.WHITE_QUEENSIDE : MoveNode.BLACK_QUEENSIDE]) { 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))); + return rook != null && rook.getType() == Type.ROOK && isFreePath(new Move(new Position(4, y), new Position(1, y))); } else return false; } diff --git a/src/dev/kske/chess/board/Log.java b/src/dev/kske/chess/board/Log.java index 133d42a..3ae372e 100644 --- a/src/dev/kske/chess/board/Log.java +++ b/src/dev/kske/chess/board/Log.java @@ -1,9 +1,11 @@ package dev.kske.chess.board; +import java.util.Arrays; import java.util.Iterator; import java.util.Objects; import dev.kske.chess.board.Piece.Color; +import dev.kske.chess.board.Piece.Type; /** * Project: Chess
@@ -15,13 +17,12 @@ public class Log implements Iterable { private MoveNode root, current; - private Position enPassant; private Color activeColor; + private boolean[] castlingRights; + private Position enPassant; private int fullmoveNumber, halfmoveClock; - public Log() { - reset(); - } + public Log() { reset(); } /** * Creates a (partially deep) copy of another {@link Log} instance which begins @@ -34,6 +35,7 @@ public class Log implements Iterable { */ public Log(Log other, boolean copyVariations) { enPassant = other.enPassant; + castlingRights = other.castlingRights.clone(); activeColor = other.activeColor; fullmoveNumber = other.fullmoveNumber; halfmoveClock = other.halfmoveClock; @@ -54,9 +56,7 @@ public class Log implements Iterable { private boolean hasNext = true; @Override - public boolean hasNext() { - return hasNext; - } + public boolean hasNext() { return hasNext; } @Override public MoveNode next() { @@ -72,16 +72,20 @@ public class Log implements Iterable { * Adds a move to the move history and adjusts the log to the new position. * * @param move The move to log + * @param piece The piece that performed the move * @param capturedPiece The piece captured with the move - * @param pawnMove {@code true} if the move was made by a pawn */ - public void add(Move move, Piece capturedPiece, boolean pawnMove) { - enPassant = pawnMove && move.yDist == 2 ? new Position(move.pos.x, move.pos.y + move.ySign) : null; + public void add(Move move, Piece piece, Piece capturedPiece) { + enPassant = piece.getType() == Type.PAWN && move.yDist == 2 ? new Position(move.pos.x, move.pos.y + move.ySign) : null; if (activeColor == Color.BLACK) ++fullmoveNumber; - if (pawnMove || capturedPiece != null) halfmoveClock = 0; + if (piece.getType() == Type.PAWN || capturedPiece != null) halfmoveClock = 0; else++halfmoveClock; activeColor = activeColor.opposite(); - final MoveNode leaf = new MoveNode(move, capturedPiece, enPassant, activeColor, fullmoveNumber, halfmoveClock); + + // Disable castling rights if a king or a rook has been moved + if (piece.getType() == Type.KING || piece.getType() == Type.ROOK) disableCastlingRights(piece, move.pos); + + final MoveNode leaf = new MoveNode(move, capturedPiece, castlingRights.clone(), enPassant, activeColor, fullmoveNumber, halfmoveClock); if (isEmpty()) { root = leaf; @@ -106,9 +110,7 @@ public class Log implements Iterable { public boolean isEmpty() { return root == null; } - public boolean hasParent() { - return !isEmpty() && current.hasParent(); - } + public boolean hasParent() { return !isEmpty() && current.hasParent(); } /** * Reverts the log to its initial state corresponding to the default board @@ -117,6 +119,7 @@ public class Log implements Iterable { public void reset() { root = null; current = null; + castlingRights = new boolean[] { true, true, true, true }; enPassant = null; activeColor = Color.WHITE; fullmoveNumber = 1; @@ -124,8 +127,9 @@ public class Log implements Iterable { } /** + * Changes the current node to one of its children (variations). * - * @param index + * @param index the index of the variation to select */ public void selectNextNode(int index) { if (!isEmpty() && current.hasVariations() && index < current.getVariations().size()) { @@ -156,14 +160,29 @@ public class Log implements Iterable { private void update() { activeColor = current.activeColor; + castlingRights = current.castlingRights.clone(); enPassant = current.enPassant; fullmoveNumber = current.fullmoveCounter; halfmoveClock = current.halfmoveClock; } + private void disableCastlingRights(Piece piece, Position initialPosition) { + // Kingside + if (piece.getType() == Type.KING || piece.getType() == Type.ROOK && initialPosition.x == 7) + castlingRights[piece.getColor() == Color.WHITE ? MoveNode.WHITE_KINGSIDE : MoveNode.BLACK_KINGSIDE] = false; + + // Queenside + if (piece.getType() == Type.KING || piece.getType() == Type.ROOK && initialPosition.x == 0) + castlingRights[piece.getColor() == Color.WHITE ? MoveNode.WHITE_QUEENSIDE : MoveNode.BLACK_QUEENSIDE] = false; + } + @Override public int hashCode() { - return Objects.hash(activeColor, current, enPassant, fullmoveNumber, halfmoveClock, root); + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(castlingRights); + result = prime * result + Objects.hash(activeColor, current, enPassant, fullmoveNumber, halfmoveClock); + return result; } @Override @@ -172,9 +191,8 @@ public class Log implements Iterable { if (obj == null) return false; if (getClass() != obj.getClass()) return false; Log other = (Log) obj; - return activeColor == other.activeColor && Objects.equals(current, other.current) - && Objects.equals(enPassant, other.enPassant) && fullmoveNumber == other.fullmoveNumber - && halfmoveClock == other.halfmoveClock && Objects.equals(root, other.root); + return activeColor == other.activeColor && Arrays.equals(castlingRights, other.castlingRights) && Objects.equals(current, other.current) + && Objects.equals(enPassant, other.enPassant) && fullmoveNumber == other.fullmoveNumber && halfmoveClock == other.halfmoveClock; } /** @@ -187,6 +205,10 @@ public class Log implements Iterable { */ public MoveNode getLast() { return current; } + public boolean[] getCastlingRights() { return castlingRights; } + + public void setCastlingRights(boolean[] castlingRights) { this.castlingRights = castlingRights; } + public Position getEnPassant() { return enPassant; } public void setEnPassant(Position enPassant) { this.enPassant = enPassant; } diff --git a/src/dev/kske/chess/board/MoveNode.java b/src/dev/kske/chess/board/MoveNode.java index e71c4b8..600a0c9 100644 --- a/src/dev/kske/chess/board/MoveNode.java +++ b/src/dev/kske/chess/board/MoveNode.java @@ -1,6 +1,7 @@ package dev.kske.chess.board; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -14,8 +15,11 @@ import dev.kske.chess.board.Piece.Color; */ public class MoveNode { + public static final int WHITE_KINGSIDE = 0, WHITE_QUEENSIDE = 1, BLACK_KINGSIDE = 2, BLACK_QUEENSIDE = 3; + public final Move move; public final Piece capturedPiece; + public final boolean[] castlingRights; public final Position enPassant; public final Color activeColor; public final int fullmoveCounter, halfmoveClock; @@ -34,10 +38,11 @@ public class MoveNode { * @param fullmoveCounter * @param halfmoveClock */ - public MoveNode(Move move, Piece capturedPiece, Position enPassant, Color activeColor, int fullmoveCounter, - int halfmoveClock) { + public MoveNode(Move move, Piece capturedPiece, boolean castlingRights[], Position enPassant, Color activeColor, + int fullmoveCounter, int halfmoveClock) { this.move = move; this.capturedPiece = capturedPiece; + this.castlingRights = castlingRights; this.enPassant = enPassant; this.activeColor = activeColor; this.fullmoveCounter = fullmoveCounter; @@ -53,8 +58,8 @@ public class MoveNode { * considers subsequent variations */ public MoveNode(MoveNode other, boolean copyVariations) { - this(other.move, other.capturedPiece, other.enPassant, other.activeColor, other.fullmoveCounter, - other.halfmoveClock); + this(other.move, other.capturedPiece, other.castlingRights.clone(), other.enPassant, other.activeColor, + other.fullmoveCounter, other.halfmoveClock); if (copyVariations && other.variations != null) { if (variations == null) variations = new ArrayList<>(); other.variations.forEach(variation -> { @@ -97,8 +102,12 @@ public class MoveNode { @Override public int hashCode() { - return Objects - .hash(activeColor, capturedPiece, enPassant, fullmoveCounter, halfmoveClock, move, parent, variations); + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(castlingRights); + result = prime * result + + Objects.hash(activeColor, capturedPiece, enPassant, fullmoveCounter, halfmoveClock, move); + return result; } @Override @@ -108,8 +117,8 @@ public class MoveNode { if (getClass() != obj.getClass()) return false; MoveNode other = (MoveNode) obj; return activeColor == other.activeColor && Objects.equals(capturedPiece, other.capturedPiece) - && Objects.equals(enPassant, other.enPassant) && fullmoveCounter == other.fullmoveCounter - && halfmoveClock == other.halfmoveClock && Objects.equals(move, other.move) - && Objects.equals(parent, other.parent) && Objects.equals(variations, other.variations); + && Arrays.equals(castlingRights, other.castlingRights) && Objects.equals(enPassant, other.enPassant) + && fullmoveCounter == other.fullmoveCounter && halfmoveClock == other.halfmoveClock + && Objects.equals(move, other.move); } } \ No newline at end of file diff --git a/src/dev/kske/chess/board/Piece.java b/src/dev/kske/chess/board/Piece.java index 79cb5d6..ef4ea90 100644 --- a/src/dev/kske/chess/board/Piece.java +++ b/src/dev/kske/chess/board/Piece.java @@ -14,7 +14,6 @@ public abstract class Piece implements Cloneable { private final Color color; protected Board board; - private int moveCounter; public Piece(Color color, Board board) { this.color = color; @@ -75,19 +74,9 @@ public abstract class Piece implements Cloneable { public Color getColor() { return color; } - public int getMoveCounter() { return moveCounter; } - - public void incMoveCounter() { - ++moveCounter; - } - - public void decMoveCounter() { - --moveCounter; - } - @Override public int hashCode() { - return Objects.hash(color, moveCounter); + return Objects.hash(color); } @Override @@ -96,7 +85,7 @@ public abstract class Piece implements Cloneable { if (obj == null) return false; if (getClass() != obj.getClass()) return false; Piece other = (Piece) obj; - return color == other.color && moveCounter == other.moveCounter; + return color == other.color; } public static enum Type { diff --git a/test/dev/kske/chess/board/LogTest.java b/test/dev/kske/chess/board/LogTest.java index 204b6be..fafc6dc 100644 --- a/test/dev/kske/chess/board/LogTest.java +++ b/test/dev/kske/chess/board/LogTest.java @@ -43,10 +43,10 @@ class LogTest { log.setActiveColor(Color.WHITE); other.setActiveColor(Color.BLACK); assertNotEquals(log.getActiveColor(), other.getActiveColor()); - log.add(Move.fromLAN("a2a4"), null, true); - log.add(Move.fromLAN("a4a5"), null, true); - other.add(Move.fromLAN("a2a4"), null, true); - other.add(Move.fromLAN("a4a5"), null, true); + log.add(Move.fromLAN("a2a4"), new Pawn(Color.WHITE, null), null); + log.add(Move.fromLAN("a4a5"), new Pawn(Color.WHITE, null), null); + other.add(Move.fromLAN("a2a4"), new Pawn(Color.WHITE, null), null); + other.add(Move.fromLAN("a4a5"), new Pawn(Color.WHITE, null), null); assertNotEquals(log.getRoot(), other.getRoot()); assertNotEquals(log.getRoot().getVariations(), other.getRoot().getVariations()); }