199 lines
5.5 KiB
Java
199 lines
5.5 KiB
Java
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;
|
|
|
|
/**
|
|
* Import a game from PGN format.
|
|
*/
|
|
|
|
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 final int COORDINATE_ANY = -1;
|
|
|
|
/**
|
|
* Parse the moves from a PGN string.
|
|
* @param unparsedMoves
|
|
* @return
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Parse a move from the PGN format and plays it in the given game.
|
|
* @param move
|
|
* @param game
|
|
* @return
|
|
*/
|
|
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(COORDINATE_ANY, COORDINATE_ANY);
|
|
|
|
// 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;
|
|
}
|
|
|
|
/**
|
|
* Get the start coordinate of a piece.
|
|
* @param dest the end position of the moving piece
|
|
* @param pieceType the type of the piece
|
|
* @param ambiguity coordinates of the moving piece, indicated with the constant COORDINATE_ANY.
|
|
* @param game the game
|
|
* @see COORDINATE_ANY
|
|
* @see getAmbiguityPattern
|
|
* @return the start coordinate of the piece
|
|
*/
|
|
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() != COORDINATE_ANY && coord.getX() != pattern.getX())
|
|
return false;
|
|
|
|
if (pattern.getY() != COORDINATE_ANY && coord.getY() != pattern.getY())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
private static Coordinate getAmbiguityPattern(char amb) {
|
|
if (Character.isDigit(amb))
|
|
return new Coordinate(COORDINATE_ANY, getYCoord(amb));
|
|
return new Coordinate(getXCoord(amb), COORDINATE_ANY);
|
|
}
|
|
|
|
private static Coordinate stringToCoordinate(String coordinates) {
|
|
char xPos = coordinates.charAt(0);
|
|
char yPos = coordinates.charAt(1);
|
|
|
|
return new Coordinate(getXCoord(xPos), getYCoord(yPos));
|
|
}
|
|
}
|