package featurecat.lizzie.rules; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import featurecat.lizzie.analysis.MoveData; import org.eclipse.collections.api.set.MutableSet; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; public class BoardData { private ImmutablePair<Integer, Integer> boardSize; private Stone[] stonesOnBoard; private int[] lastMove; private Stone lastMoveColor; private boolean blackToPlay; private Zobrist zobrist; private int moveNumber; private int[] moveNumberListOnBoard; private MutableSet<Coordinates> removedEnemyStoneIndexes; private int blackPrisonersCount; private int whitePrisonersCount; private List<VariationData> variationDataList; public BoardData(ImmutablePair<Integer, Integer> boardSize, Stone[] stonesOnBoard, int[] lastMove, Stone lastMoveColor, boolean blackToPlay, Zobrist zobrist, int moveNumber, int[] moveNumberListOnBoard, List<VariationData> variationDataList, MutableSet<Coordinates> removedEnemyStoneIndexes, int blackPrisonersCount, int whitePrisonersCount) { this.boardSize = boardSize; this.stonesOnBoard = stonesOnBoard; this.lastMove = lastMove; this.lastMoveColor = lastMoveColor; this.blackToPlay = blackToPlay; this.zobrist = zobrist; this.moveNumber = moveNumber; this.moveNumberListOnBoard = moveNumberListOnBoard; this.variationDataList = variationDataList; this.removedEnemyStoneIndexes = removedEnemyStoneIndexes; this.blackPrisonersCount = blackPrisonersCount; this.whitePrisonersCount = whitePrisonersCount; } public BoardData(ImmutablePair<Integer, Integer> boardSize, Stone[] stonesOnBoard, int[] lastMove, Stone lastMoveColor, boolean blackToPlay, Zobrist zobrist, int moveNumber, int[] moveNumberListOnBoard, MutableSet<Coordinates> removedEnemyStoneIndexes, int blackPrisonersCount, int whitePrisonersCount) { this(boardSize, stonesOnBoard, lastMove, lastMoveColor, blackToPlay, zobrist, moveNumber, moveNumberListOnBoard, null, removedEnemyStoneIndexes, blackPrisonersCount, whitePrisonersCount); } public BoardData(Stone[] stonesOnBoard, int[] lastMove, Stone lastMoveColor, boolean blackToPlay, Zobrist zobrist, int moveNumber, int[] moveNumberListOnBoard, MutableSet<Coordinates> removedEnemyStoneIndexes, int blackPrisonersCount, int whitePrisonersCount) { this(ImmutablePair.of(19, 19), stonesOnBoard, lastMove, lastMoveColor, blackToPlay, zobrist, moveNumber, moveNumberListOnBoard, null, removedEnemyStoneIndexes, blackPrisonersCount, whitePrisonersCount); } public ImmutablePair<Integer, Integer> getBoardSize() { return boardSize; } public Stone[] getStonesOnBoard() { return stonesOnBoard; } public void setStonesOnBoard(Stone[] stonesOnBoard) { this.stonesOnBoard = stonesOnBoard; } public int[] getLastMove() { return lastMove; } public void setLastMove(int[] lastMove) { this.lastMove = lastMove; } public Stone getLastMoveColor() { return lastMoveColor; } public void setLastMoveColor(Stone lastMoveColor) { this.lastMoveColor = lastMoveColor; } public boolean isBlackToPlay() { return blackToPlay; } public void setBlackToPlay(boolean blackToPlay) { this.blackToPlay = blackToPlay; } public Zobrist getZobrist() { return zobrist; } public void setZobrist(Zobrist zobrist) { this.zobrist = zobrist; } public int getMoveNumber() { return moveNumber; } public void setMoveNumber(int moveNumber) { this.moveNumber = moveNumber; } public int[] getMoveNumberListOnBoard() { return moveNumberListOnBoard; } public void setMoveNumberListOnBoard(int[] moveNumberListOnBoard) { this.moveNumberListOnBoard = moveNumberListOnBoard; } public List<VariationData> getVariationDataList() { return variationDataList; } public void setVariationDataList(List<VariationData> variationDataList) { this.variationDataList = variationDataList; } public Optional<VariationData> getFirstVariation() { if (CollectionUtils.isEmpty(variationDataList)) { return Optional.empty(); } else { return Optional.ofNullable(variationDataList.get(0)); } } public double getBlackWinrate() { Optional<VariationData> variationData = getFirstVariation(); double winrate = variationData.map(VariationData::getWinrate).orElse(50.0); if (blackToPlay) { return winrate; } else { return 100.0 - winrate; } } public double getWhiteWinrate() { return 100.0 - getBlackWinrate(); } public MutableSet<Coordinates> getRemovedEnemyStoneIndexes() { return removedEnemyStoneIndexes; } public void setRemovedEnemyStoneIndexes(MutableSet<Coordinates> removedEnemyStoneIndexes) { this.removedEnemyStoneIndexes = removedEnemyStoneIndexes; } public int getBlackPrisonersCount() { return blackPrisonersCount; } public void setBlackPrisonersCount(int blackPrisonersCount) { this.blackPrisonersCount = blackPrisonersCount; } public int getWhitePrisonersCount() { return whitePrisonersCount; } public void setWhitePrisonersCount(int whitePrisonersCount) { this.whitePrisonersCount = whitePrisonersCount; } public int getCalculationCount() { Optional<VariationData> variationData = getFirstVariation(); return variationData.map(VariationData::getPlayouts).orElse(0); } public int getTotalCalculationCount() { return CollectionUtils.isEmpty(variationDataList) ? 0 : variationDataList.stream().mapToInt(VariationData::getPlayouts).sum(); } public Optional<Boolean> isBlack() { switch (lastMoveColor) { case BLACK: return Optional.of(true); case WHITE: return Optional.of(false); default: return Optional.empty(); } } public Optional<Boolean> isWhite() { switch (lastMoveColor) { case BLACK: return Optional.of(false); case WHITE: return Optional.of(true); default: return Optional.empty(); } } public boolean isPass() { return lastMove == null || lastMove[0] < 0 || lastMove[0] >= boardSize.getLeft() || lastMove[1] < 0 || lastMove[1] >= boardSize.getRight(); } public void tryUpdateVariationInfo(List<MoveData> newMoveDataList) { if (CollectionUtils.isEmpty(newMoveDataList)) { return; } // FIXME: Due to boardStateCount cannot sync with bestMoves, we may receive previous move's variations // if (CollectionUtils.isEmpty(variationDataList)) { // variationDataList = newMoveDataList.stream().map(VariationData::new).collect(Collectors.toList()); // } else { // int totalCalculation = variationDataList.stream().mapToInt(VariationData::getPlayouts).sum(); // int newTotalCalculation = newMoveDataList.stream().mapToInt(MoveData::getPlayouts).sum(); // if (newTotalCalculation > totalCalculation) { // variationDataList = newMoveDataList.stream().map(VariationData::new).collect(Collectors.toList()); // } // } variationDataList = newMoveDataList.stream().map(VariationData::new).collect(Collectors.toList()); } public int coordsToIndex(int row, int col) { return row * boardSize.getLeft() + col; } public Stone getStoneOnBoard(int row, int col) { int index = coordsToIndex(row, col); try { return stonesOnBoard[index]; } catch (ArrayIndexOutOfBoundsException e) { return null; } } public int getMoveNumberOnBoard(int row, int col) { int index = coordsToIndex(row, col); try { return moveNumberListOnBoard[index]; } catch (ArrayIndexOutOfBoundsException e) { return 0; } } public Optional<int[]> getBestMove() { if (CollectionUtils.isEmpty(variationDataList)) { return Optional.empty(); } VariationData bestVariation = variationDataList.get(0); if (CollectionUtils.isEmpty(bestVariation.getVariation())) { return Optional.empty(); } else { return Optional.ofNullable(bestVariation.getVariation().get(0)); } } }