Removed old FEN string methods, fixed FEN regex
This commit is contained in:
		| @@ -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.<br> | ||||
| 	 * 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<Type, Boolean> 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; | ||||
|   | ||||
| @@ -47,7 +47,7 @@ public class FENString { | ||||
| 	public FENString(String fen) throws ChessException { | ||||
| 		// Check fen string against regex | ||||
| 		Pattern	fenPattern	= Pattern.compile( | ||||
| 				"^(?<piecePlacement>(?:[1-8nbrqkpNBRQKP]{1,8}\\/){7}[1-8nbrqkpNBRQKP]{1,8}) (?<activeColor>[wb]) (?<castlingAvailability>-|[KQkq]{1,4}) (?<enPassantTargetSquare>-|[a-h][1-8]) (?<halfmoveClock>\\d) (?<fullmoveNumber>\\d)$"); | ||||
| 				"^(?<piecePlacement>(?:[1-8nbrqkpNBRQKP]{1,8}\\/){7}[1-8nbrqkpNBRQKP]{1,8}) (?<activeColor>[wb]) (?<castlingAvailability>-|[KQkq]{1,4}) (?<enPassantTargetSquare>-|[a-h][1-8]) (?<halfmoveClock>\\d+) (?<fullmoveNumber>\\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")); | ||||
|  | ||||
|   | ||||
| @@ -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(); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -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; } | ||||
| } | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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<String> 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()); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user