Fixed memory leak, improved copy constructors

This commit is contained in:
Kai S. K. Engelbart 2019-09-19 21:31:24 +02:00
parent 47a120e651
commit 2c3ac42516
7 changed files with 71 additions and 52 deletions

View File

@ -15,7 +15,7 @@ import dev.kske.chess.board.Piece.Type;
* Created: <strong>01.07.2019</strong><br> * Created: <strong>01.07.2019</strong><br>
* Author: <strong>Kai S. K. Engelbart</strong> * Author: <strong>Kai S. K. Engelbart</strong>
*/ */
public class Board implements Cloneable { public class Board {
private Piece[][] boardArr = new Piece[8][8]; private Piece[][] boardArr = new Piece[8][8];
private Map<Color, Position> kingPos = new HashMap<>(); private Map<Color, Position> kingPos = new HashMap<>();
@ -75,6 +75,30 @@ public class Board implements Cloneable {
initFromFEN(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
* apart from the current {@link MoveNode}.
*
* @param other The {@link Board} instance to copy
*/
public Board(Board other) {
boardArr = new Piece[8][8];
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
if (other.boardArr[i][j] == null) continue;
boardArr[i][j] = (Piece) other.boardArr[i][j].clone();
boardArr[i][j].board = this;
}
kingPos.putAll(other.kingPos);
Map<Type, Boolean> whiteCastling = new HashMap<>(other.castlingRights.get(Color.WHITE)),
blackCastling = new HashMap<>(other.castlingRights.get(Color.BLACK));
castlingRights.put(Color.WHITE, whiteCastling);
castlingRights.put(Color.BLACK, blackCastling);
log = new Log(other.log, false);
}
/** /**
* Moves a piece across the board if the move is legal. * Moves a piece across the board if the move is legal.
* *
@ -523,33 +547,6 @@ public class Board implements Cloneable {
return sb.toString(); return sb.toString();
} }
/**
* @return A new instance of this class with a shallow copy of both
* {@code kingPos} and {code boardArr}
*/
@Override
public Object clone() {
Board board = null;
try {
board = (Board) super.clone();
} catch (CloneNotSupportedException ex) {
ex.printStackTrace();
}
board.boardArr = new Piece[8][8];
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
if (boardArr[i][j] == null) continue;
board.boardArr[i][j] = (Piece) boardArr[i][j].clone();
board.boardArr[i][j].board = board;
}
board.kingPos = new HashMap<>();
board.kingPos.putAll(kingPos);
board.log = new Log(log);
return board;
}
/** /**
* @param pos The position from which to return a piece * @param pos The position from which to return a piece
* @return The piece at the position * @return The piece at the position

View File

@ -24,12 +24,15 @@ public class Log {
} }
/** /**
* Creates a partial deep copy of another {@link Log} instance which begins with * Creates a (partially deep) copy of another {@link Log} instance which begins
* the current node. * with the current {@link MoveNode}.
* *
* @param other The {@link Log} instance to copy * @param other The {@link Log} instance to copy
* @param copyVariations If set to {@code true}, subsequent variations of the
* current {@link MoveNode} are copied with the
* {@link Log}
*/ */
public Log(Log other) { public Log(Log other, boolean copyVariations) {
enPassant = other.enPassant; enPassant = other.enPassant;
activeColor = other.activeColor; activeColor = other.activeColor;
fullmoveCounter = other.fullmoveCounter; fullmoveCounter = other.fullmoveCounter;
@ -37,7 +40,7 @@ public class Log {
// The new root is the current node of the copied instance // The new root is the current node of the copied instance
if (!other.isEmpty()) { if (!other.isEmpty()) {
root = new MoveNode(other.current); root = new MoveNode(other.current, copyVariations);
root.parent = null; root.parent = null;
current = root; current = root;
} }
@ -73,6 +76,7 @@ public class Log {
*/ */
public void removeLast() { public void removeLast() {
if (!isEmpty() && current.parent != null) { if (!isEmpty() && current.parent != null) {
current.parent.variations.remove(current);
current = current.parent; current = current.parent;
activeColor = current.activeColor; activeColor = current.activeColor;
enPassant = current.enPassant; enPassant = current.enPassant;
@ -131,10 +135,10 @@ public class Log {
public final int fullmoveCounter, halfmoveClock; public final int fullmoveCounter, halfmoveClock;
private MoveNode parent; private MoveNode parent;
private List<MoveNode> variations = new ArrayList<>(); private List<MoveNode> variations;
/** /**
* Creates a new {@link MoveNode} * Creates a new {@link MoveNode}.
* *
* @param move The logged {@link Move} * @param move The logged {@link Move}
* @param capturedPiece The {@link Piece} captures by the logged {@link Move} * @param capturedPiece The {@link Piece} captures by the logged {@link Move}
@ -155,19 +159,25 @@ public class Log {
} }
/** /**
* Creates a deep copy of another {@link MoveNode}. * Creates a (deep) copy of another {@link MoveNode}.
* *
* @param other The {@link MoveNode} to copy * @param other The {@link MoveNode} to copy
* @param copyVariations When this is set to {@code true} a deep copy is
* created, which
* considers subsequent variations
*/ */
public MoveNode(MoveNode other) { public MoveNode(MoveNode other, boolean copyVariations) {
this(other.move, other.capturedPiece, other.enPassant, other.activeColor, other.fullmoveCounter, this(other.move, other.capturedPiece, other.enPassant, other.activeColor, other.fullmoveCounter,
other.halfmoveClock); other.halfmoveClock);
if (copyVariations && other.variations != null) {
if (variations == null) variations = new ArrayList<>();
other.variations.forEach(variation -> { other.variations.forEach(variation -> {
MoveNode copy = new MoveNode(variation); MoveNode copy = new MoveNode(variation, true);
copy.parent = this; copy.parent = this;
variations.add(copy); variations.add(copy);
}); });
} }
}
/** /**
* Adds another {@link MoveNode} as a child node. * Adds another {@link MoveNode} as a child node.
@ -175,10 +185,16 @@ public class Log {
* @param variation The {@link MoveNode} to append to this {@link MoveNode} * @param variation The {@link MoveNode} to append to this {@link MoveNode}
*/ */
public void addVariation(MoveNode variation) { public void addVariation(MoveNode variation) {
if (variations == null) variations = new ArrayList<>();
if (!variations.contains(variation)) { if (!variations.contains(variation)) {
variations.add(variation); variations.add(variation);
variation.parent = this; variation.parent = this;
} }
} }
/**
* @return A list of all variations associated with this {@link MoveNode}
*/
public List<MoveNode> getVariations() { return variations; }
} }
} }

View File

@ -77,6 +77,9 @@ public class Game {
boardComponent.repaint(); boardComponent.repaint();
overlayComponent.displayArrow(move); overlayComponent.displayArrow(move);
// Run garbage collection
System.gc();
System.out.printf("%s: %s%n", player.color, move); System.out.printf("%s: %s%n", player.color, move);
System.out.println("FEN: " + board.toFEN()); System.out.println("FEN: " + board.toFEN());
EventBus.getInstance().dispatch(new MoveEvent(move)); EventBus.getInstance().dispatch(new MoveEvent(move));

View File

@ -56,7 +56,7 @@ public class NaturalPlayer extends Player implements MouseListener {
pos = new Position(evt.getPoint().x / overlayComponent.getTileSize(), pos = new Position(evt.getPoint().x / overlayComponent.getTileSize(),
evt.getPoint().y / overlayComponent.getTileSize()); evt.getPoint().y / overlayComponent.getTileSize());
Board board = (Board) NaturalPlayer.this.board.clone(); Board board = new Board(this.board);
if (board.get(pos) != null && board.get(pos).getColor() == color) { if (board.get(pos) != null && board.get(pos).getColor() == color) {
List<Position> positions = board.getMoves(pos) List<Position> positions = board.getMoves(pos)
.stream() .stream()

View File

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

View File

@ -6,10 +6,7 @@ import static org.junit.Assert.assertNotSame;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import dev.kske.chess.board.Board;
import dev.kske.chess.board.Move;
import dev.kske.chess.board.Piece.Color; import dev.kske.chess.board.Piece.Color;
import dev.kske.chess.board.Queen;
/** /**
* Project: <strong>Chess</strong><br> * Project: <strong>Chess</strong><br>
@ -34,7 +31,7 @@ class BoardTest {
*/ */
@Test @Test
void testClone() { void testClone() {
Board clone = (Board) board.clone(); Board clone = new Board(board);
assertNotSame(clone, board); assertNotSame(clone, board);
assertNotSame(clone.getBoardArr(), board.getBoardArr()); assertNotSame(clone.getBoardArr(), board.getBoardArr());

View File

@ -39,10 +39,16 @@ class LogTest {
*/ */
@Test @Test
void testClone() { void testClone() {
Log other = new Log(log); Log other = new Log(log, false);
log.setActiveColor(Color.WHITE); log.setActiveColor(Color.WHITE);
other.setActiveColor(Color.BLACK); other.setActiveColor(Color.BLACK);
assertNotEquals(other.getActiveColor(), log.getActiveColor()); assertNotEquals(log.getActiveColor(), other.getActiveColor());
log.add(Move.fromSAN("a2a4"), null, true);
log.add(Move.fromSAN("a4a5"), null, true);
other.add(Move.fromSAN("a2a4"), null, true);
other.add(Move.fromSAN("a4a5"), null, true);
assertNotEquals(log.getRoot(), other.getRoot());
assertNotEquals(log.getRoot().getVariations(), other.getRoot().getVariations());
} }
/** /**