diff --git a/src/dev/kske/chess/board/Board.java b/src/dev/kske/chess/board/Board.java
index 5c2a8ac..fbab10d 100644
--- a/src/dev/kske/chess/board/Board.java
+++ b/src/dev/kske/chess/board/Board.java
@@ -63,13 +63,6 @@ public class Board {
*/
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); }
-
/**
* Creates a copy of another {@link Board} instance.
* The created object is a deep copy, but does not contain any move history
@@ -416,138 +409,6 @@ public class Board {
log.reset();
}
- /**
- * Initialized the board with a position specified in a FEN-encoded string.
- *
- * @param fen The FEN-encoded string representing target state of the board
- */
- public void initFromFEN(String fen) {
- String[] parts = fen.split(" ");
- log.reset();
-
- // Piece placement (from white's perspective)
- String[] rows = parts[0].split("/");
- for (int i = 0; i < 8; i++) {
- char[] places = rows[i].toCharArray();
- for (int j = 0, k = 0; k < places.length; j++, k++) {
- if (Character.isDigit(places[k])) {
- for (int l = j; l < Character.digit(places[k], 10); l++, j++)
- boardArr[j][i] = null;
- --j;
- } 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);
- }
- }
- }
- }
-
- // Active color
- log.setActiveColor(Color.fromFirstChar(parts[1].charAt(0)));
-
- // Castling rights
- Map 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.fromLAN(parts[3]));
-
- // Halfmove clock
- log.setHalfmoveClock(Integer.parseInt(parts[4]));
-
- // Fullmove counter
- log.setFullmoveNumber(Integer.parseInt(parts[5]));
- }
-
- /**
- * @return a FEN-encoded string representing the board
- */
- public String toFEN() {
- StringBuilder sb = new StringBuilder();
-
- // Piece placement (from white's perspective)
- for (int i = 0; i < 8; i++) {
- int emptyCount = 0;
- for (int j = 0; j < 8; j++) {
- final Piece piece = boardArr[j][i];
- if (piece == null) ++emptyCount;
- else {
- if (emptyCount != 0) {
- sb.append(emptyCount);
- emptyCount = 0;
- }
- char p = boardArr[j][i].getType().firstChar();
- sb.append(piece.getColor() == Color.WHITE ? Character.toUpperCase(p) : p);
- }
- }
- if (emptyCount != 0) sb.append(emptyCount);
- if (i < 7) sb.append('/');
- }
-
- // Active color
- sb.append(" " + log.getActiveColor().firstChar());
-
- // 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 (castlingSb.length() == 0) sb.append("-");
- sb.append(castlingSb);
-
- final MoveNode lastMove = log.getLast();
-
- // En passant availability
- sb.append(" " + (lastMove == null || lastMove.enPassant == null ? "-" : lastMove.enPassant.toLAN()));
-
- // Halfmove clock
- sb.append(" " + String.valueOf(lastMove == null ? 0 : lastMove.halfmoveClock));
-
- // Fullmove counter
- sb.append(" " + String.valueOf(lastMove == null ? 1 : lastMove.fullmoveCounter));
-
- return sb.toString();
- }
-
@Override
public int hashCode() {
final int prime = 31;
diff --git a/src/dev/kske/chess/board/FENString.java b/src/dev/kske/chess/board/FENString.java
index 4811fcc..536cecf 100644
--- a/src/dev/kske/chess/board/FENString.java
+++ b/src/dev/kske/chess/board/FENString.java
@@ -47,7 +47,7 @@ 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());
@@ -55,8 +55,7 @@ public class FENString {
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"));
+ if (!matcher.group("enPassantTargetSquare").equals("-")) enPassantTargetSquare = Position.fromLAN(matcher.group("enPassantTargetSquare"));
halfmoveClock = Integer.parseInt(matcher.group("halfmoveClock"));
fullmoveNumber = Integer.parseInt(matcher.group("fullmoveNumber"));
diff --git a/src/dev/kske/chess/game/UCIPlayer.java b/src/dev/kske/chess/game/UCIPlayer.java
index 12b73d3..369c16d 100644
--- a/src/dev/kske/chess/game/UCIPlayer.java
+++ b/src/dev/kske/chess/game/UCIPlayer.java
@@ -2,6 +2,7 @@ package dev.kske.chess.game;
import java.io.IOException;
+import dev.kske.chess.board.FENString;
import dev.kske.chess.board.Move;
import dev.kske.chess.board.Piece.Color;
import dev.kske.chess.uci.UCIHandle;
@@ -30,7 +31,7 @@ public class UCIPlayer extends Player implements UCIListener {
@Override
public void requestMove() {
- handle.positionFEN(board.toFEN());
+ handle.positionFEN(new FENString(board).toString());
handle.go();
}
diff --git a/src/dev/kske/chess/pgn/PGNGame.java b/src/dev/kske/chess/pgn/PGNGame.java
index 9b56b8f..6293976 100644
--- a/src/dev/kske/chess/pgn/PGNGame.java
+++ b/src/dev/kske/chess/pgn/PGNGame.java
@@ -7,6 +7,7 @@ import java.util.regex.MatchResult;
import java.util.regex.Pattern;
import dev.kske.chess.board.Board;
+import dev.kske.chess.board.FENString;
import dev.kske.chess.exception.ChessException;
/**
@@ -26,8 +27,7 @@ public class PGNGame {
MatchResult matchResult;
Pattern tagPairPattern = Pattern.compile("\\[(\\w+) \"(.*)\"]"),
movePattern = Pattern.compile("\\d+\\.\\s+(?:(?:(\\S+)\\s+(\\S+))|(?:O-O-O)|(?:O-O))(?:\\+{0,2}|\\#)"),
- nagPattern = Pattern.compile("(\\$\\d{1,3})*"),
- terminationMarkerPattern = Pattern.compile("1-0|0-1|1\\/2-1\\/2|\\*");
+ nagPattern = Pattern.compile("(\\$\\d{1,3})*"), terminationMarkerPattern = Pattern.compile("1-0|0-1|1\\/2-1\\/2|\\*");
// Parse tag pairs
while (sc.findInLine(tagPairPattern) != null) {
@@ -48,30 +48,23 @@ public class PGNGame {
matchResult = sc.match();
if (matchResult.groupCount() > 0) for (int i = 1; i < matchResult.groupCount() + 1; i++) {
game.board.move(matchResult.group(i));
- System.out.println(game.getBoard().getLog().getLast().move.toLAN() + ": " + game.board.toFEN());
+ System.out.println(game.getBoard().getLog().getLast().move.toLAN() + ": " + new FENString(game.board).toString());
}
else break;
} else break;
}
// Parse game termination marker
- if (sc.findWithinHorizon(terminationMarkerPattern, 20) == null)
- System.err.println("Termination marker expected");
+ if (sc.findWithinHorizon(terminationMarkerPattern, 20) == null) System.err.println("Termination marker expected");
return game;
}
- public String getTag(String tagName) {
- return tagPairs.get(tagName);
- }
+ public String getTag(String tagName) { return tagPairs.get(tagName); }
- public boolean hasTag(String tagName) {
- return tagPairs.containsKey(tagName);
- }
+ public boolean hasTag(String tagName) { return tagPairs.containsKey(tagName); }
- public void setTag(String tagName, String tagValue) {
- tagPairs.put(tagName, tagValue);
- }
+ public void setTag(String tagName, String tagValue) { tagPairs.put(tagName, tagValue); }
public Board getBoard() { return board; }
}
diff --git a/src/dev/kske/chess/ui/FENDropTarget.java b/src/dev/kske/chess/ui/FENDropTarget.java
index b8ffc6d..aea43b3 100644
--- a/src/dev/kske/chess/ui/FENDropTarget.java
+++ b/src/dev/kske/chess/ui/FENDropTarget.java
@@ -11,7 +11,10 @@ import java.io.FileReader;
import java.io.IOException;
import java.util.List;
-import dev.kske.chess.board.Board;
+import javax.swing.JOptionPane;
+
+import dev.kske.chess.board.FENString;
+import dev.kske.chess.exception.ChessException;
import dev.kske.chess.game.Game;
/**
@@ -24,9 +27,7 @@ public class FENDropTarget extends DropTargetAdapter {
private MainWindow mainWindow;
- public FENDropTarget(MainWindow mainWindow) {
- this.mainWindow = mainWindow;
- }
+ public FENDropTarget(MainWindow mainWindow) { this.mainWindow = mainWindow; }
@SuppressWarnings("unchecked")
@Override
@@ -38,9 +39,18 @@ public class FENDropTarget extends DropTargetAdapter {
final GamePane gamePane = mainWindow.addGamePane();
final String fen = br.readLine();
DialogUtil.showGameConfigurationDialog(null, (whiteName, blackName) -> {
- final Game game = new Game(gamePane.getBoardPane(), whiteName, blackName, new Board(fen));
- gamePane.setGame(game);
- game.start();
+ Game game;
+ try {
+ game = new Game(gamePane.getBoardPane(), whiteName, blackName, new FENString(fen).getBoard());
+ gamePane.setGame(game);
+ game.start();
+ } catch (ChessException e) {
+ e.printStackTrace();
+ JOptionPane.showMessageDialog(mainWindow,
+ "Failed to load FEN string: " + e.toString(),
+ "FEN loading error",
+ JOptionPane.ERROR_MESSAGE);
+ }
});
evt.dropComplete(true);
} catch (IOException e) {
diff --git a/src/dev/kske/chess/ui/MenuBar.java b/src/dev/kske/chess/ui/MenuBar.java
index 1d995b1..8e796db 100644
--- a/src/dev/kske/chess/ui/MenuBar.java
+++ b/src/dev/kske/chess/ui/MenuBar.java
@@ -12,7 +12,8 @@ import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
-import dev.kske.chess.board.Board;
+import dev.kske.chess.board.FENString;
+import dev.kske.chess.exception.ChessException;
import dev.kske.chess.game.Game;
import dev.kske.chess.io.EngineUtil;
import dev.kske.chess.pgn.PGNDatabase;
@@ -42,8 +43,7 @@ public class MenuBar extends JMenuBar {
JMenu gameMenu = new JMenu("Game");
JMenuItem newGameMenuItem = new JMenuItem("New Game");
- newGameMenuItem
- .addActionListener((evt) -> DialogUtil.showGameConfigurationDialog(mainWindow, (whiteName, blackName) -> {
+ newGameMenuItem.addActionListener((evt) -> DialogUtil.showGameConfigurationDialog(mainWindow, (whiteName, blackName) -> {
GamePane gamePane = mainWindow.addGamePane();
Game game = new Game(gamePane.getBoardPane(), whiteName, blackName);
gamePane.setGame(game);
@@ -64,10 +64,8 @@ public class MenuBar extends JMenuBar {
JMenuItem addEngineMenuItem = new JMenuItem("Add engine");
addEngineMenuItem.addActionListener((evt) -> {
- String enginePath = JOptionPane.showInputDialog(getParent(),
- "Enter the path to a UCI-compatible chess engine:",
- "Engine selection",
- JOptionPane.QUESTION_MESSAGE);
+ String enginePath = JOptionPane
+ .showInputDialog(getParent(), "Enter the path to a UCI-compatible chess engine:", "Engine selection", JOptionPane.QUESTION_MESSAGE);
if (enginePath != null) EngineUtil.addEngine(enginePath);
});
@@ -83,7 +81,7 @@ public class MenuBar extends JMenuBar {
JMenuItem exportFENMenuItem = new JMenuItem("Export board to FEN");
exportFENMenuItem.addActionListener((evt) -> {
- final String fen = mainWindow.getSelectedGamePane().getGame().getBoard().toFEN();
+ final String fen = new FENString(mainWindow.getSelectedGamePane().getGame().getBoard()).toString();
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(fen), null);
JOptionPane.showMessageDialog(mainWindow, String.format("FEN-string copied to clipboard!%n%s", fen));
});
@@ -94,9 +92,16 @@ public class MenuBar extends JMenuBar {
final GamePane gamePane = mainWindow.addGamePane();
final String fen = JOptionPane.showInputDialog("Enter a FEN string: ");
DialogUtil.showGameConfigurationDialog(mainWindow, (whiteName, blackName) -> {
- final Game game = new Game(gamePane.getBoardPane(), whiteName, blackName, new Board(fen));
- gamePane.setGame(game);
- game.start();
+ Game game;
+ try {
+ game = new Game(gamePane.getBoardPane(), whiteName, blackName, new FENString(fen).getBoard());
+ gamePane.setGame(game);
+ game.start();
+ } catch (ChessException e) {
+ e.printStackTrace();
+ JOptionPane
+ .showMessageDialog(mainWindow, "Failed to load FEN string: " + e.toString(), "FEN loading error", JOptionPane.ERROR_MESSAGE);
+ }
});
});
toolsMenu.add(loadFromFENMenuItem);
@@ -113,9 +118,18 @@ public class MenuBar extends JMenuBar {
final GamePane gamePane = mainWindow.addGamePane(name);
final String fen = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
DialogUtil.showGameConfigurationDialog(mainWindow, (whiteName, blackName) -> {
- final Game game = new Game(gamePane.getBoardPane(), whiteName, blackName, new Board(fen));
- gamePane.setGame(game);
- game.start();
+ Game game;
+ try {
+ game = new Game(gamePane.getBoardPane(), whiteName, blackName, new FENString(fen).getBoard());
+ gamePane.setGame(game);
+ game.start();
+ } catch (ChessException e) {
+ e.printStackTrace();
+ JOptionPane.showMessageDialog(mainWindow,
+ "Failed to load FEN string: " + e.toString(),
+ "FEN loading error",
+ JOptionPane.ERROR_MESSAGE);
+ }
});
} catch (Exception e) {
e.printStackTrace();
@@ -134,14 +148,10 @@ public class MenuBar extends JMenuBar {
String[] gameNames = new String[pgnDB.getGames().size()];
for (int i = 0; i < gameNames.length; i++) {
final PGNGame game = pgnDB.getGames().get(i);
- gameNames[i] = String.format("%s vs %s: %s",
- game.getTag("White"),
- game.getTag("Black"),
- game.getTag("Result"));
+ gameNames[i] = String.format("%s vs %s: %s", game.getTag("White"), game.getTag("Black"), game.getTag("Result"));
}
JComboBox comboBox = new JComboBox<>(gameNames);
- JOptionPane
- .showInputDialog(mainWindow, comboBox, "Select a game", JOptionPane.QUESTION_MESSAGE);
+ JOptionPane.showInputDialog(mainWindow, comboBox, "Select a game", JOptionPane.QUESTION_MESSAGE);
DialogUtil.showGameConfigurationDialog(mainWindow, (whiteName, blackName) -> {
final Game game = new Game(gamePane.getBoardPane(), whiteName, blackName,
pgnDB.getGames().get(comboBox.getSelectedIndex()).getBoard());
diff --git a/test/dev/kske/chess/board/FENStringTest.java b/test/dev/kske/chess/board/FENStringTest.java
index 8d2810f..b2c1e43 100644
--- a/test/dev/kske/chess/board/FENStringTest.java
+++ b/test/dev/kske/chess/board/FENStringTest.java
@@ -35,7 +35,7 @@ class FENStringTest {
*/
@BeforeEach
void setUp() throws Exception {
- fenStrings.addAll(Arrays.asList("rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b - - 1 2"));
+ fenStrings.addAll(Arrays.asList("rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2"));
Board board = new Board();
board.set(Position.fromLAN("c7"), null);
board.set(Position.fromLAN("c5"), new Pawn(Color.BLACK, board));