Fixed memory leak, improved copy constructors
This commit is contained in:
		@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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; }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -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));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user