feat: pgn parser
This commit is contained in:
172
app/src/main/java/chess/pgn/PgnImport.java
Normal file
172
app/src/main/java/chess/pgn/PgnImport.java
Normal file
@@ -0,0 +1,172 @@
|
||||
package chess.pgn;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import chess.controller.CommandExecutor;
|
||||
import chess.controller.PlayerCommand;
|
||||
import chess.controller.commands.CastlingCommand;
|
||||
import chess.controller.commands.MoveCommand;
|
||||
import chess.controller.commands.NewGameCommand;
|
||||
import chess.controller.commands.PromoteCommand;
|
||||
import chess.controller.commands.PromoteCommand.PromoteType;
|
||||
import chess.controller.event.EmptyGameDispatcher;
|
||||
import chess.model.ChessBoard;
|
||||
import chess.model.Coordinate;
|
||||
import chess.model.Game;
|
||||
import chess.model.Move;
|
||||
import chess.model.Piece;
|
||||
import chess.model.pieces.Bishop;
|
||||
import chess.model.pieces.King;
|
||||
import chess.model.pieces.Knight;
|
||||
import chess.model.pieces.Pawn;
|
||||
import chess.model.pieces.Queen;
|
||||
import chess.model.pieces.Rook;
|
||||
|
||||
public class PgnImport {
|
||||
|
||||
private static final Map<String, Class<? extends Piece>> pieceMap = Map.of(
|
||||
"K", King.class,
|
||||
"Q", Queen.class,
|
||||
"R", Rook.class,
|
||||
"B", Bishop.class,
|
||||
"N", Knight.class);
|
||||
|
||||
private static final Map<String, PromoteType> promoteMap = Map.of(
|
||||
"Q", PromoteType.Queen,
|
||||
"R", PromoteType.Rook,
|
||||
"B", PromoteType.Bishop,
|
||||
"N", PromoteType.Knight);
|
||||
|
||||
public static List<PlayerCommand> importGame(String pgnContent) {
|
||||
String[] parts = pgnContent.split("\n\n");
|
||||
// we just ignore headers
|
||||
return getMoves(parts[parts.length - 1]);
|
||||
}
|
||||
|
||||
private static List<PlayerCommand> getMoves(String unparsedMoves) {
|
||||
String[] moves = unparsedMoves.replaceAll("\\{.*?\\}", "") // Remove comments
|
||||
.replaceAll("\\n", " ") // Remove new lines
|
||||
.split("[\\s.]+"); // Split by whitespace and dots (trimming it also)
|
||||
|
||||
Game virtualGame = new Game();
|
||||
CommandExecutor commandExecutor = new CommandExecutor(virtualGame, new EmptyGameDispatcher());
|
||||
List<PlayerCommand> instructions = new ArrayList<>();
|
||||
|
||||
commandExecutor.executeCommand(new NewGameCommand());
|
||||
|
||||
for (int i = 0; i < moves.length; i++) {
|
||||
if (i % 3 == 0)
|
||||
continue;
|
||||
|
||||
String move = moves[i];
|
||||
|
||||
if (move.equals("1-0") || move.equals("0-1") || move.equals("1/2-1/2")) {
|
||||
break; // End of the game
|
||||
}
|
||||
|
||||
List<PlayerCommand> cmds = parseMove(move, virtualGame);
|
||||
commandExecutor.executeCommands(cmds);
|
||||
|
||||
instructions.addAll(cmds);
|
||||
}
|
||||
return instructions;
|
||||
}
|
||||
|
||||
private static List<PlayerCommand> parseMove(String move, Game game) {
|
||||
if (move.equals("O-O-O"))
|
||||
return Arrays.asList(new CastlingCommand(true));
|
||||
if (move.equals("O-O"))
|
||||
return Arrays.asList(new CastlingCommand(false));
|
||||
|
||||
move = move.replaceAll("[x|#|\\+]", "");
|
||||
|
||||
PromoteCommand promoteCommand = null;
|
||||
|
||||
if (move.contains("=")) {
|
||||
String promoteString = move.substring(move.length() - 1);
|
||||
promoteCommand = new PromoteCommand(promoteMap.get(promoteString));
|
||||
move = move.substring(0, move.length() - 2);
|
||||
}
|
||||
|
||||
Class<? extends Piece> pieceType = pieceMap.get(move.substring(0, 1));
|
||||
|
||||
if (pieceType == null)
|
||||
pieceType = Pawn.class;
|
||||
else
|
||||
move = move.substring(1);
|
||||
|
||||
assert move.length() == 3 || move.length() == 2;
|
||||
|
||||
Coordinate ambiguity = new Coordinate(-1, -1);
|
||||
|
||||
// ambiguity
|
||||
if (move.length() == 3) {
|
||||
ambiguity = getAmbiguityPattern(move.charAt(0));
|
||||
move = move.substring(1);
|
||||
}
|
||||
|
||||
Coordinate dest = stringToCoordinate(move);
|
||||
|
||||
Coordinate start = getStartCoord(dest, pieceType, ambiguity, game);
|
||||
|
||||
List<PlayerCommand> cmds = new ArrayList<>();
|
||||
cmds.add(new MoveCommand(new Move(start, dest)));
|
||||
if (promoteCommand != null)
|
||||
cmds.add(promoteCommand);
|
||||
|
||||
return cmds;
|
||||
}
|
||||
|
||||
private static Coordinate getStartCoord(Coordinate dest, Class<? extends Piece> pieceType, Coordinate ambiguity,
|
||||
Game game) {
|
||||
final ChessBoard board = game.getBoard();
|
||||
|
||||
List<Coordinate> starts = board.getAllowedStarts(dest, game.getPlayerTurn());
|
||||
|
||||
assert !starts.isEmpty() : "No moves allowed!";
|
||||
|
||||
for (Coordinate start : starts) {
|
||||
Piece piece = board.pieceAt(start);
|
||||
if (piece.getClass().equals(pieceType) && coordPatternMatch(start, ambiguity))
|
||||
return start;
|
||||
}
|
||||
|
||||
assert false : "There is a small problem ...";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int getXCoord(char xPos) {
|
||||
return xPos - 'a';
|
||||
}
|
||||
|
||||
private static int getYCoord(char yPos) {
|
||||
return Coordinate.VALUE_MAX - 1 - (yPos - '1');
|
||||
}
|
||||
|
||||
private static boolean coordPatternMatch(Coordinate coord, Coordinate pattern) {
|
||||
if (pattern.getX() != -1 && coord.getX() != pattern.getX())
|
||||
return false;
|
||||
|
||||
if (pattern.getY() != -1 && coord.getY() != pattern.getY())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Coordinate getAmbiguityPattern(char amb) {
|
||||
if (Character.isDigit(amb))
|
||||
return new Coordinate(-1, getYCoord(amb));
|
||||
return new Coordinate(getXCoord(amb), -1);
|
||||
}
|
||||
|
||||
private static Coordinate stringToCoordinate(String coordinates) {
|
||||
char xPos = coordinates.charAt(0);
|
||||
char yPos = coordinates.charAt(1);
|
||||
|
||||
return new Coordinate(getXCoord(xPos), getYCoord(yPos));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user