Fixed memory leak, improved copy constructors

This commit is contained in:
Kai S. K. Engelbart 2019-09-19 21:31:24 +02:00
parent 06ab01f585
commit 69261303c5
Signed by: kske
GPG Key ID: 8BEB13EC5DF7EF13
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>
* Author: <strong>Kai S. K. Engelbart</strong>
*/
public class Board implements Cloneable {
public class Board {
private Piece[][] boardArr = new Piece[8][8];
private Map<Color, Position> kingPos = new HashMap<>();
@ -75,6 +75,30 @@ public class Board implements Cloneable {
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.
*
@ -523,33 +547,6 @@ public class Board implements Cloneable {
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
* @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
* the current node.
* Creates a (partially deep) copy of another {@link Log} instance which begins
* 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;
activeColor = other.activeColor;
fullmoveCounter = other.fullmoveCounter;
@ -37,7 +40,7 @@ public class Log {
// The new root is the current node of the copied instance
if (!other.isEmpty()) {
root = new MoveNode(other.current);
root = new MoveNode(other.current, copyVariations);
root.parent = null;
current = root;
}
@ -73,6 +76,7 @@ public class Log {
*/
public void removeLast() {
if (!isEmpty() && current.parent != null) {
current.parent.variations.remove(current);
current = current.parent;
activeColor = current.activeColor;
enPassant = current.enPassant;
@ -131,10 +135,10 @@ public class Log {
public final int fullmoveCounter, halfmoveClock;
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 capturedPiece The {@link Piece} captures by the logged {@link Move}
@ -155,18 +159,24 @@ 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,
other.halfmoveClock);
other.variations.forEach(variation -> {
MoveNode copy = new MoveNode(variation);
copy.parent = this;
variations.add(copy);
});
if (copyVariations && other.variations != null) {
if (variations == null) variations = new ArrayList<>();
other.variations.forEach(variation -> {
MoveNode copy = new MoveNode(variation, true);
copy.parent = this;
variations.add(copy);
});
}
}
/**
@ -175,10 +185,16 @@ public class Log {
* @param variation The {@link MoveNode} to append to this {@link MoveNode}
*/
public void addVariation(MoveNode variation) {
if (variations == null) variations = new ArrayList<>();
if (!variations.contains(variation)) {
variations.add(variation);
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();
overlayComponent.displayArrow(move);
// Run garbage collection
System.gc();
System.out.printf("%s: %s%n", player.color, move);
System.out.println("FEN: " + board.toFEN());
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(),
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) {
List<Position> positions = board.getMoves(pos)
.stream()

View File

@ -50,7 +50,7 @@ public class AIPlayer extends Player {
/*
* 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);
/*
@ -64,7 +64,7 @@ public class AIPlayer extends Player {
for (int i = 0; i < numThreads; i++) {
if (rem-- > 0) ++endIndex;
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));
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.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.Queen;
/**
* Project: <strong>Chess</strong><br>
@ -34,7 +31,7 @@ class BoardTest {
*/
@Test
void testClone() {
Board clone = (Board) board.clone();
Board clone = new Board(board);
assertNotSame(clone, board);
assertNotSame(clone.getBoardArr(), board.getBoardArr());

View File

@ -39,10 +39,16 @@ class LogTest {
*/
@Test
void testClone() {
Log other = new Log(log);
Log other = new Log(log, false);
log.setActiveColor(Color.WHITE);
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());
}
/**