diff --git a/.classpath b/.classpath
index e4beaea..8469573 100644
--- a/.classpath
+++ b/.classpath
@@ -7,6 +7,11 @@
+
+
+
+
+
diff --git a/src/dev/kske/chess/board/Board.java b/src/dev/kske/chess/board/Board.java
index e353a35..94b481b 100644
--- a/src/dev/kske/chess/board/Board.java
+++ b/src/dev/kske/chess/board/Board.java
@@ -1,9 +1,11 @@
package dev.kske.chess.board;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -576,7 +578,7 @@ public class Board {
for (int j = 0; j < 8; j++) {
final Piece piece = boardArr[j][i];
if (piece == null) ++emptyCount;
- else { // TODO: rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b - - 1 2 error
+ else {
if (emptyCount != 0) {
sb.append(emptyCount);
emptyCount = 0;
@@ -616,6 +618,25 @@ public class Board {
return sb.toString();
}
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.deepHashCode(boardArr);
+ result = prime * result + Objects.hash(castlingRights, kingPos, log);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ 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);
+ }
+
/**
* @param pos The position from which to return a piece
* @return The piece at the position
diff --git a/src/dev/kske/chess/board/FENString.java b/src/dev/kske/chess/board/FENString.java
index 5e53511..6183015 100644
--- a/src/dev/kske/chess/board/FENString.java
+++ b/src/dev/kske/chess/board/FENString.java
@@ -1,7 +1,5 @@
package dev.kske.chess.board;
-import java.util.LinkedHashMap;
-import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -21,25 +19,23 @@ import dev.kske.chess.exception.ChessException;
*/
public class FENString {
- private Board board;
- private Map fields = new LinkedHashMap<>();
-
- public static enum FENField {
- PIECE_PLACEMENT, ACTIVE_COLOR, CASTLING_AVAILABILITY, EN_PASSANT_TARGET_SQUARE, HALFMOVE_CLOCK, FULLMOVE_NUMBER
- }
+ private Board board;
+ private String piecePlacement, castlingAvailability;
+ private int halfmoveClock, fullmoveNumber;
+ private Color activeColor;
+ private Position enPassantTargetSquare;
/**
* Constructs a {@link FENString} representing the starting position
* {@code rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1}.
*/
public FENString() {
- board = new Board();
- fields.put(FENField.PIECE_PLACEMENT, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR");
- fields.put(FENField.ACTIVE_COLOR, "w");
- fields.put(FENField.CASTLING_AVAILABILITY, "KQkq");
- fields.put(FENField.EN_PASSANT_TARGET_SQUARE, "-");
- fields.put(FENField.HALFMOVE_CLOCK, "0");
- fields.put(FENField.FULLMOVE_NUMBER, "1");
+ board = new Board();
+ piecePlacement = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR";
+ activeColor = Color.WHITE;
+ castlingAvailability = "KQkq";
+ halfmoveClock = 0;
+ fullmoveNumber = 1;
}
/**
@@ -51,11 +47,18 @@ public class FENString {
public FENString(String fen) throws ChessException {
// Check fen string against regex
Pattern fenPattern = Pattern.compile(
- "^(?(?:[1-8nbrqkpNBRQKP]{1,8}\\/){7}[1-8nbrqkpNBRQKP]{1,8}) (?[wb]) (?-|[KQkq]{1,4}) (?-|[a-h][1-8]) (?\\d) (?\\d)$");
+ "^(?(?:[1-8nbrqkpNBRQKP]{1,8}\\/){7}[1-8nbrqkpNBRQKP]{1,8}) (?[wb]) (?-|[KQkq]{1,4}) (?-|[a-h][1-8]) (?\\d) (?\\d)$");
Matcher matcher = fenPattern.matcher(fen);
if (!matcher.find()) throw new ChessException("FEN string does not match pattern " + fenPattern.pattern());
- for (FENField field : FENField.values())
- fields.put(field, matcher.group(field.toString()));
+
+ // Initialize data fields
+ piecePlacement = matcher.group("piecePlacement");
+ activeColor = Color.fromFirstChar(matcher.group("activeColor").charAt(0));
+ castlingAvailability = matcher.group("castlingAvailability");
+ if (!matcher.group("enPassantTargetSquare").equals("-"))
+ enPassantTargetSquare = Position.fromLAN(matcher.group("enPassantTargetSquare"));
+ halfmoveClock = Integer.parseInt(matcher.group("halfmoveClock"));
+ fullmoveNumber = Integer.parseInt(matcher.group("fullmoveNumber"));
// Initialize and clean board
board = new Board();
@@ -66,7 +69,7 @@ public class FENString {
// Parse individual fields
// Piece placement
- final String[] rows = fields.get(FENField.PIECE_PLACEMENT).split("/");
+ final String[] rows = piecePlacement.split("/");
if (rows.length != 8) throw new ChessException("FEN string contains invalid piece placement");
for (int i = 0; i < 8; i++) {
final char[] cols = rows[i].toCharArray();
@@ -80,22 +83,22 @@ public class FENString {
Color color = Character.isUpperCase(c) ? Color.WHITE : Color.BLACK;
switch (Character.toUpperCase(c)) {
case 'K':
- board.getBoardArr()[i][j] = new King(color, board);
+ board.getBoardArr()[j][i] = new King(color, board);
break;
case 'Q':
- board.getBoardArr()[i][j] = new Queen(color, board);
+ board.getBoardArr()[j][i] = new Queen(color, board);
break;
case 'R':
- board.getBoardArr()[i][j] = new Rook(color, board);
+ board.getBoardArr()[j][i] = new Rook(color, board);
break;
case 'N':
- board.getBoardArr()[i][j] = new Knight(color, board);
+ board.getBoardArr()[j][i] = new Knight(color, board);
break;
case 'B':
- board.getBoardArr()[i][j] = new Bishop(color, board);
+ board.getBoardArr()[j][i] = new Bishop(color, board);
break;
case 'P':
- board.getBoardArr()[i][j] = new Pawn(color, board);
+ board.getBoardArr()[j][i] = new Pawn(color, board);
break;
}
++j;
@@ -104,19 +107,18 @@ public class FENString {
}
// Active color
- board.getLog().setActiveColor(Color.fromFirstChar(fields.get(FENField.ACTIVE_COLOR).charAt(0)));
+ board.getLog().setActiveColor(activeColor);
// TODO: Castling availability
// En passant square
- if (!fields.get(FENField.EN_PASSANT_TARGET_SQUARE).equals("-"))
- board.getLog().setEnPassant(Position.fromLAN(fields.get(FENField.EN_PASSANT_TARGET_SQUARE)));
+ board.getLog().setEnPassant(enPassantTargetSquare);
// Halfmove clock
- board.getLog().setHalfmoveClock(Integer.parseInt(fields.get(FENField.HALFMOVE_CLOCK)));
+ board.getLog().setHalfmoveClock(halfmoveClock);
// Fullmove number
- board.getLog().setFullmoveNumber(Integer.parseInt(fields.get(FENField.FULLMOVE_NUMBER)));
+ board.getLog().setFullmoveNumber(fullmoveNumber);
}
/**
@@ -134,7 +136,7 @@ public class FENString {
for (int i = 0; i < 8; i++) {
int empty = 0;
for (int j = 0; j < 8; j++) {
- final Piece piece = board.getBoardArr()[i][j];
+ final Piece piece = board.getBoardArr()[j][i];
if (piece == null) ++empty;
else {
@@ -159,22 +161,21 @@ public class FENString {
if (i < 7) sb.append('/');
}
- fields.put(FENField.PIECE_PLACEMENT, sb.toString());
+ piecePlacement = sb.toString();
// Active color
- fields.put(FENField.ACTIVE_COLOR, String.valueOf(board.getLog().getActiveColor().firstChar()));
+ activeColor = board.getLog().getActiveColor();
// TODO: Castling availability
// En passant availability
- final Position enPassantPosition = board.getLog().getEnPassant();
- fields.put(FENField.EN_PASSANT_TARGET_SQUARE, enPassantPosition == null ? "-" : enPassantPosition.toLAN());
+ enPassantTargetSquare = board.getLog().getEnPassant();
// Halfmove clock
- fields.put(FENField.HALFMOVE_CLOCK, String.valueOf(board.getLog().getHalfmoveClock()));
+ halfmoveClock = board.getLog().getHalfmoveClock();
// Fullmove counter
- fields.put(FENField.FULLMOVE_NUMBER, String.valueOf(board.getLog().getFullmoveNumber()));
+ fullmoveNumber = board.getLog().getFullmoveNumber();
}
/**
@@ -184,7 +185,13 @@ public class FENString {
*/
@Override
public String toString() {
- return String.join(" ", fields.values());
+ return String.format("%s %c %s %s %d %d",
+ piecePlacement,
+ activeColor.firstChar(),
+ castlingAvailability,
+ enPassantTargetSquare == null ? "-" : enPassantTargetSquare.toLAN(),
+ halfmoveClock,
+ fullmoveNumber);
}
/**
diff --git a/src/dev/kske/chess/board/Log.java b/src/dev/kske/chess/board/Log.java
index d5c1794..133d42a 100644
--- a/src/dev/kske/chess/board/Log.java
+++ b/src/dev/kske/chess/board/Log.java
@@ -1,6 +1,7 @@
package dev.kske.chess.board;
import java.util.Iterator;
+import java.util.Objects;
import dev.kske.chess.board.Piece.Color;
@@ -160,6 +161,22 @@ public class Log implements Iterable {
halfmoveClock = current.halfmoveClock;
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(activeColor, current, enPassant, fullmoveNumber, halfmoveClock, root);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ 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 The first logged move, or {@code null} if there is none
*/
diff --git a/src/dev/kske/chess/board/Move.java b/src/dev/kske/chess/board/Move.java
index 74f8cb7..34cacf9 100644
--- a/src/dev/kske/chess/board/Move.java
+++ b/src/dev/kske/chess/board/Move.java
@@ -1,5 +1,7 @@
package dev.kske.chess.board;
+import java.util.Objects;
+
/**
* Project: Chess
* File: Move.java
@@ -50,6 +52,21 @@ public class Move {
return String.format("%s -> %s", pos, dest);
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(dest, pos, type, xDist, xSign, yDist, ySign);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ Move other = (Move) obj;
+ return Objects.equals(dest, other.dest) && Objects.equals(pos, other.pos) && type == other.type
+ && xDist == other.xDist && xSign == other.xSign && yDist == other.yDist && ySign == other.ySign;
+ }
+
public static enum Type {
NORMAL, PAWN_PROMOTION, CASTLING, EN_PASSANT, UNKNOWN
}
diff --git a/src/dev/kske/chess/board/MoveNode.java b/src/dev/kske/chess/board/MoveNode.java
index 6d4afad..e71c4b8 100644
--- a/src/dev/kske/chess/board/MoveNode.java
+++ b/src/dev/kske/chess/board/MoveNode.java
@@ -2,6 +2,7 @@ package dev.kske.chess.board;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import dev.kske.chess.board.Piece.Color;
@@ -93,4 +94,22 @@ public class MoveNode {
public boolean hasParent() {
return parent != null;
}
+
+ @Override
+ public int hashCode() {
+ return Objects
+ .hash(activeColor, capturedPiece, enPassant, fullmoveCounter, halfmoveClock, move, parent, variations);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ 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);
+ }
}
\ 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 e9ca766..79cb5d6 100644
--- a/src/dev/kske/chess/board/Piece.java
+++ b/src/dev/kske/chess/board/Piece.java
@@ -2,6 +2,7 @@ package dev.kske.chess.board;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
/**
* Project: Chess
@@ -84,6 +85,20 @@ public abstract class Piece implements Cloneable {
--moveCounter;
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(color, moveCounter);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ Piece other = (Piece) obj;
+ return color == other.color && moveCounter == other.moveCounter;
+ }
+
public static enum Type {
KING, QUEEN, ROOK, KNIGHT, BISHOP, PAWN;
diff --git a/test/dev/kske/chess/board/FENStringTest.java b/test/dev/kske/chess/board/FENStringTest.java
new file mode 100644
index 0000000..8d2810f
--- /dev/null
+++ b/test/dev/kske/chess/board/FENStringTest.java
@@ -0,0 +1,72 @@
+package dev.kske.chess.board;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import dev.kske.chess.board.Piece.Color;
+import dev.kske.chess.exception.ChessException;
+
+/**
+ * Project: Chess
+ * File: FENStringTest.java
+ * Created: 24 Oct 2019
+ *
+ * @author Kai S. K. Engelbart
+ */
+class FENStringTest {
+
+ List fenStrings = new ArrayList<>();
+ List boards = new ArrayList<>();
+
+ void cleanBoard(Board board) {
+ for (int i = 0; i < 8; i++)
+ for (int j = 0; j < 8; j++)
+ board.getBoardArr()[i][j] = null;
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @BeforeEach
+ void setUp() throws Exception {
+ fenStrings.addAll(Arrays.asList("rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b - - 1 2"));
+ Board board = new Board();
+ board.set(Position.fromLAN("c7"), null);
+ board.set(Position.fromLAN("c5"), new Pawn(Color.BLACK, board));
+ board.set(Position.fromLAN("e4"), new Pawn(Color.WHITE, board));
+ board.set(Position.fromLAN("f3"), new Knight(Color.WHITE, board));
+ board.set(Position.fromLAN("e2"), null);
+ board.set(Position.fromLAN("g1"), null);
+
+ board.getLog().setActiveColor(Color.BLACK);
+ board.getLog().setHalfmoveClock(1);
+ board.getLog().setFullmoveNumber(2);
+ boards.add(board);
+ }
+
+ /**
+ * Test method for {@link dev.kske.chess.board.FENString#toString()}.
+ */
+ @Test
+ void testToString() {
+ for (int i = 0; i < fenStrings.size(); i++)
+ assertEquals(fenStrings.get(i), new FENString(boards.get(i)).toString());
+ }
+
+ /**
+ * Test method for {@link dev.kske.chess.board.FENString#getBoard()}.
+ *
+ * @throws ChessException
+ */
+ @Test
+ void testGetBoard() throws ChessException {
+ for (int i = 0; i < boards.size(); i++)
+ assertEquals(boards.get(i), new FENString(fenStrings.get(i)).getBoard());
+ }
+}