This repository has been archived on 2021-02-18. You can view files and clone it, but cannot push or open issues or pull requests.
chess/src/main/java/dev/kske/chess/pgn/PGNGame.java

179 lines
4.6 KiB
Java

package dev.kske.chess.pgn;
import java.io.PrintWriter;
import java.util.*;
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.board.Move;
import dev.kske.chess.board.Piece.Color;
/**
* Project: <strong>Chess</strong><br>
* File: <strong>PGNGame.java</strong><br>
* Created: <strong>22 Sep 2019</strong><br>
*
* @since Chess v0.5-alpha
* @author Kai S. K. Engelbart
*/
public class PGNGame {
private final Map<String, String> tagPairs = new HashMap<>(7);
private final Board board;
/**
* Creates an instance of {@link PGNGame}. A new default {@link Board} will
* be
* created.
*/
public PGNGame() {
board = new Board();
}
/**
* Creates an instance of {@link PGNGame}.
*
* @param board the board associated with the game
*/
public PGNGame(Board board) {
this.board = board;
}
/**
* Parses a game in {@code PGN} format from a {@link Scanner} instance
*
* @param sc the {@link Scanner} to parse the game from, which is not closed
* after this process
* @return the parsed {@link PGNGame}
*/
public static PGNGame parse(Scanner sc) {
PGNGame game = new 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|\\*");
// Parse tag pairs
while (sc.findInLine(tagPairPattern) != null) {
matchResult = sc.match();
if (matchResult.groupCount() == 2)
game.setTag(matchResult.group(1), matchResult.group(2));
else
break;
sc.nextLine();
}
// Parse movetext
while (true) {
// Skip NAG (Numeric Annotation Glyph)
sc.skip(nagPattern);
// TODO: Parse RAV (Recursive Annotation Variation)
if (sc.findWithinHorizon(movePattern, 20) != null) {
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()
+ ": " + 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");
return game;
}
/**
* Serializes this game to {@code PGN} format.
*
* @param pw the writer to write the game to
*/
public void writePGN(PrintWriter pw) {
// Set the unknown result tag if no result tag is specified
tagPairs.putIfAbsent("Result", "*");
// Write tag pairs
tagPairs.forEach((k, v) -> pw.printf("[%s \"%s\"]%n", k, v));
// Insert newline if tags were printed
if (!tagPairs.isEmpty())
pw.println();
if (!board.getLog().isEmpty()) {
// Collect SAN moves
Board clone = new Board(board, true);
List<String> chunks = new ArrayList<>();
boolean flag = true;
while (flag) {
Move move = clone.getLog().getLast().move;
flag = clone.getLog().hasParent();
clone.revert();
String chunk = clone.getLog().getActiveColor() == Color.WHITE
? String.format(" %d. ", clone.getLog().getFullmoveNumber())
: " ";
chunk += move.toSAN(clone);
chunks.add(chunk);
}
Collections.reverse(chunks);
// Write movetext
String line = "";
for (String chunk : chunks)
if (line.length() + chunk.length() <= 80)
line += chunk;
else {
pw.println(line);
line = chunk;
}
if (!line.isEmpty())
pw.println(line);
}
// Write game termination marker
pw.print(tagPairs.get("Result"));
}
/**
* @param tagName the name of a game tag
* @return the value of the game tag
*/
public String getTag(String tagName) {
return tagPairs.get(tagName);
}
/**
* @param tagName the name of a game tag
* @return {@code true} if the tag is present
*/
public boolean hasTag(String tagName) {
return tagPairs.containsKey(tagName);
}
/**
* Sets a game tag.
*
* @param tagName the name of the tag
* @param tagValue the value of the tag
*/
public void setTag(String tagName, String tagValue) {
tagPairs.put(tagName, tagValue);
}
/**
* @return the board associated with this game
*/
public Board getBoard() { return board; }
}