Implemented saving to PGN file

+ copyVariations parameter in copy constructors of Board and Log

This procedure still required work in the form of efficiently rewinding
the board to the first position for SAN move extraction and shortening
SAN moves to the smallest possible representation.
This commit is contained in:
Kai S. K. Engelbart 2019-12-06 23:54:11 +01:00
parent 4cb6e0e08b
commit 4723127bc8
Signed by: kske
GPG Key ID: 8BEB13EC5DF7EF13
8 changed files with 65 additions and 45 deletions

View File

@ -34,9 +34,9 @@ public class Board {
* apart from the current {@link MoveNode}.
*
* @param other The {@link Board} instance to copy
* @param copyVariations TODO
*/
public Board(Board other) {
boardArr = new Piece[8][8];
public Board(Board other, boolean copyVariations) {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
if (other.boardArr[i][j] == null) continue;
@ -45,7 +45,11 @@ public class Board {
}
kingPos.putAll(other.kingPos);
log = new Log(other.log, false);
log = new Log(other.log, copyVariations);
// Synchronize the current move node with the board
while (log.getLast().hasVariations())
log.selectNextNode(0);
}
/**

View File

@ -43,7 +43,7 @@ public class Log implements Iterable<MoveNode> {
// The new root is the current node of the copied instance
if (!other.isEmpty()) {
root = new MoveNode(other.current, copyVariations);
root = new MoveNode(other.root, copyVariations);
root.setParent(null);
current = root;
}
@ -244,7 +244,7 @@ public class Log implements Iterable<MoveNode> {
public int getFullmoveNumber() { return fullmoveNumber; }
public void setFullmoveNumber(int fullmoveCounter) { this.fullmoveNumber = fullmoveCounter; }
public void setFullmoveNumber(int fullmoveCounter) { fullmoveNumber = fullmoveCounter; }
public int getHalfmoveClock() { return halfmoveClock; }

View File

@ -157,8 +157,8 @@ public class Move {
// Position
// TODO: Deconstruct position into optional file or rank
// TODO: Omit if the move is a pawn push
sb.append(pos.toLAN());
// Omit position if the move is a pawn push
if (!(piece instanceof Pawn && xDist == 0)) sb.append(pos.toLAN());
// Capture indicator
if (board.get(dest) != null) sb.append('x');

View File

@ -64,11 +64,11 @@ public class MoveNode {
other.fullmoveCounter, other.halfmoveClock);
if (copyVariations && other.variations != null) {
if (variations == null) variations = new ArrayList<>();
other.variations.forEach(variation -> {
for (MoveNode variation : other.variations) {
MoveNode copy = new MoveNode(variation, true);
copy.parent = this;
variations.add(copy);
});
}
}
}

View File

@ -52,7 +52,7 @@ public class AIPlayer extends Player {
/*
* Get a copy of the board and the available moves.
*/
Board board = new Board(this.board);
Board board = new Board(this.board, false);
List<Move> moves = board.getMoves(color);
/*
@ -66,7 +66,7 @@ public class AIPlayer extends Player {
for (int i = 0; i < numThreads; i++) {
if (rem-- > 0) ++endIndex;
endIndex += step;
processors.add(new MoveProcessor(new Board(board), moves.subList(beginIndex, endIndex), color,
processors.add(new MoveProcessor(new Board(board, false), moves.subList(beginIndex, endIndex), color,
maxDepth, alphaBetaThreshold));
beginIndex = endIndex;
}

View File

@ -1,7 +1,9 @@
package dev.kske.chess.pgn;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.MatchResult;
@ -9,7 +11,7 @@ import java.util.regex.Pattern;
import dev.kske.chess.board.Board;
import dev.kske.chess.board.FENString;
import dev.kske.chess.board.Piece.Color;
import dev.kske.chess.board.Move;
import dev.kske.chess.exception.ChessException;
/**
@ -78,11 +80,25 @@ public class PGNGame {
// Insert newline if tags were printed
if (!tagPairs.isEmpty()) pw.println();
// Collect SAN moves
Board clone = new Board(board, true);
List<String> sanMoves = new ArrayList<>();
while (clone.getLog().hasParent()) {
Move move = clone.getLog().getLast().move;
clone.revert();
sanMoves.add(move.toSAN(clone));
}
// Write movetext
board.getLog().forEach(m -> {
if (m.activeColor == Color.BLACK) pw.printf("%d. ", m.fullmoveCounter);
pw.printf("%s ", m.move); // TODO: Convert to SAN
});
for (int i = sanMoves.size() - 1; i >= 0; i--)
pw.printf("%s ", sanMoves.get(i));
// Write movetext
// board.getLog().forEach(m -> {
// if (m.activeColor == Color.BLACK) pw.printf("%d. ", m.fullmoveCounter);
// pw.printf("%s ", m.move.toSAN(board));
// });
// Write game termination marker
pw.print(tagPairs.get("Result"));

View File

@ -62,7 +62,7 @@ public class DialogUtil {
dialogPanel.add(lblWhite);
JComboBox<Object> cbWhite = new JComboBox<>();
cbWhite.setModel(new DefaultComboBoxModel<Object>(options.toArray()));
cbWhite.setModel(new DefaultComboBoxModel<>(options.toArray()));
cbWhite.setBounds(98, 9, 159, 22);
dialogPanel.add(cbWhite);
@ -72,7 +72,7 @@ public class DialogUtil {
dialogPanel.add(lblBlack);
JComboBox<Object> cbBlack = new JComboBox<>();
cbBlack.setModel(new DefaultComboBoxModel<Object>(options.toArray()));
cbBlack.setModel(new DefaultComboBoxModel<>(options.toArray()));
cbBlack.setBounds(98, 36, 159, 22);
dialogPanel.add(cbBlack);

View File

@ -31,7 +31,7 @@ class BoardTest {
*/
@Test
void testClone() {
Board clone = new Board(board);
Board clone = new Board(board, false);
assertNotSame(clone, board);
assertNotSame(clone.getBoardArr(), board.getBoardArr());