/** By Dingding */ private Set<Line.Segment> getRemovableLineSegments( Map<Position, Piece> pieceMap, PieceColor pieceColor) { Set<Line.Segment> removableLines = new HashSet<>(); Set<Line> linesOnTheBoard = Line.getLinesOnTheBoard( this); // Get all the possible lines on the board. Positions don't need to be occupied. for (Line line : linesOnTheBoard) { Position currentPosition = line.getStartPosition(); Position startOfSegment = null; Position endOfSegment = null; Direction direction = line.getDirection(); int consecutivePieces = 0; // We start at a dot position, so we can assume that we don't start in a set of // consecutive pieces boolean isInLineSegment = false; // Break the for-loop if an endOfSegment has been found (because the largest lines only have 7 // positions on the board, there // can't be more than one set of four pieces of the same color (requiring at least 9 // positions) on the board. for (; endOfSegment == null && isPositionOnPlayAreaOrOuterDots(currentPosition); currentPosition = currentPosition.next(direction)) { PieceColor currentPieceColor = pieceMap.containsKey(currentPosition) ? pieceMap.get(currentPosition).getPieceColor() : null; // Update the consecutivePieces if (currentPieceColor == pieceColor) consecutivePieces++; if (consecutivePieces == 4) isInLineSegment = true; if (currentPieceColor != pieceColor) consecutivePieces = 0; if (isInLineSegment) { if (isDotPosition(currentPosition) || currentPieceColor == null) { endOfSegment = currentPosition.previous(direction); } } // Update the startOfSegment if necessary if (startOfSegment == null) { if (currentPieceColor != null) { startOfSegment = currentPosition; } } if (currentPieceColor == null && endOfSegment == null) { startOfSegment = null; } // Add a line segment to the list if we have found one if (endOfSegment != null) { removableLines.add(new Line.Segment(this, startOfSegment, endOfSegment, direction)); } } } return removableLines; }
// TODO: Refactor method private void removeLines( Map<Position, Piece> pieceMap, PieceColor pieceColor, Map<PieceColor, Set<Line.Segment>> linesTakenBy, Map<PieceColor, Set<Position>> piecesBackTo) { Set<Line.Segment> intersectingSegments; Set<Line.Segment> segmentsNotRemoved = new HashSet<>(); do { intersectingSegments = new HashSet<>(); Set<Line.Segment> removableSegmentsThisPlayer = getRemovableLineSegments(pieceMap, pieceColor); for (Line.Segment segment : removableSegmentsThisPlayer) { // Remove the line segments that are not intersecting with other line segments of the set boolean intersectionFound = false; for (Line.Segment otherSegment : removableSegmentsThisPlayer) { if (!segment.equals(otherSegment) && !segmentsNotRemoved.contains(otherSegment)) { if (segment.intersectsWith(otherSegment)) { if (!segmentsNotRemoved.contains(segment)) { intersectingSegments.add(segment); intersectionFound = true; } } } } if (!intersectionFound) { if (!segmentsNotRemoved.contains(segment)) { linesTakenBy.get(pieceColor).add(segment); } } } if (intersectingSegments.size() > 0) { Line.Segment segment = intersectingSegments.iterator().next(); currentRemoveSelection = segment.getOccupiedPositions(pieceMap); int dialogResult = GipfBoardComponent.showConfirmDialog( gipfBoardState.players.current().pieceColor + ", do you want to remove " + segment .getOccupiedPositions(pieceMap) .stream() .map(Position::getName) .sorted() .collect(toList()) + "?", "Remove line segment"); if (dialogResult == JOptionPane.YES_OPTION) { // Remove the line linesTakenBy.get(pieceColor).add(segment); } else if (dialogResult == JOptionPane.NO_OPTION) { // Don't remove the line segmentsNotRemoved.add(segment); } currentRemoveSelection = new HashSet<>(); } for (Line.Segment segment : linesTakenBy.get(pieceColor)) { Predicate<Map.Entry<Position, Piece>> isNormalPiece = entry -> entry.getValue().getPieceType() == NORMAL; Predicate<Map.Entry<Position, Piece>> isCurrentPlayersColor = entry -> entry.getValue().getPieceColor() == pieceColor; Predicate<Map.Entry<Position, Piece>> doesPlayerWantToRemoveGipf = entry -> { currentRemoveSelection.add(entry.getKey()); int dialogResult = GipfBoardComponent.showConfirmDialog( gipfBoardState.players.current().pieceColor + ", do you want to remove the Gipf at " + entry.getKey().getName() + "?", "Remove Gipf"); currentRemoveSelection = new HashSet<>(); return dialogResult == JOptionPane.YES_OPTION; }; Map<Position, Piece> piecesRemovedMap = segment.getOccupiedPositions(pieceMap).stream().collect(toMap(p -> p, pieceMap::get)); piecesBackTo .get(pieceColor) .addAll( piecesRemovedMap .entrySet() .stream() .filter(isCurrentPlayersColor.and(isNormalPiece.or(doesPlayerWantToRemoveGipf))) .map(Map.Entry::getKey) .collect(toSet())); piecesBackTo .get(null) .addAll( piecesRemovedMap .entrySet() .stream() .filter( isCurrentPlayersColor .negate() .and(isNormalPiece.or(doesPlayerWantToRemoveGipf))) .map(Map.Entry::getKey) .collect(toSet())); } piecesBackTo.values().forEach(positionSet -> removePiecesFromPieceMap(pieceMap, positionSet)); } while (intersectingSegments.size() > 0); return; }
/** * This method finds the ordering of all the line segments that are removable from the current * board state in the current turn for each player. * * @param pieceMap * @param currentPlayerColor * @return An example output could be: {set: ["white", "segment from a to b"] ["white", "segment * from b to c"] ["black", "segment from x to y"] ["black", "segment from z to a"; after that * "white", "segment from a to b"] ["black", "segment from d to f"] } */ private Set<List<Pair<PieceColor, Line.Segment>>> getRemovableLineOrderingsSetFromGipfBoard( Map<Position, Piece> pieceMap, PieceColor currentPlayerColor) { Set<List<Pair<PieceColor, Line.Segment>>> removableLineSetOrderingsFromGipfboard = new HashSet<>(); PieceColor opponentColor = currentPlayerColor == WHITE ? BLACK : WHITE; Set<Line.Segment> removableLineSegmentsCurrentPlayer = getRemovableLineSegments(pieceMap, currentPlayerColor); // This loop checks for all line segments that can be removed by the current player. for (Line.Segment lineSegment : removableLineSegmentsCurrentPlayer) { // Create a copy of the piecemap where the removed segment is removed HashMap<Position, Piece> segmentRemovedPieceMap = new HashMap<>(pieceMap); segmentRemovedPieceMap .keySet() .removeAll(lineSegment.getOccupiedPositions(segmentRemovedPieceMap)); List<Pair<PieceColor, Line.Segment>> removableLineSegmentOrdering = Collections.singletonList(new Pair<>(currentPlayerColor, lineSegment)); // - I have to use removableLineSegmentList because the removableLineSegmentOrdering is // already in use. // - If the current player can remove at least 1 more segment, the board state that results // will be checked // after that for more subsequent segments that can be removed Set<List<Pair<PieceColor, Line.Segment>>> removableLineSetOrderingsCurrentPlayer = getRemovableLineOrderingsSetFromGipfBoard(segmentRemovedPieceMap, currentPlayerColor); if (removableLineSetOrderingsCurrentPlayer.size() > 0) { for (List<Pair<PieceColor, Line.Segment>> removableLineSegmentList : removableLineSetOrderingsCurrentPlayer) { List<Pair<PieceColor, Line.Segment>> ordering = new ArrayList<>(removableLineSegmentOrdering); ordering.addAll(removableLineSegmentList); removableLineSetOrderingsFromGipfboard.add(ordering); } } else { removableLineSetOrderingsFromGipfboard.add(removableLineSegmentOrdering); } // Now check for the line segments removable by the opponent // Because if this code is ran, the current player can remove line segments. If this is the // case, the current player // must remove line segments before the opponent gets its turn. for (Line.Segment lineSegmenRemovableByOpponent : getRemovableLineSegments(segmentRemovedPieceMap, opponentColor)) { // See for the comments in the similar lines above HashMap<Position, Piece> opponentSegmentRemovedPieceMap = new HashMap<>(segmentRemovedPieceMap); opponentSegmentRemovedPieceMap .keySet() .removeAll( lineSegmenRemovableByOpponent.getOccupiedPositions(opponentSegmentRemovedPieceMap)); List<Pair<PieceColor, Line.Segment>> opponentRemovableLineSegmentOrdering = Collections.singletonList(new Pair<>(opponentColor, lineSegmenRemovableByOpponent)); Set<List<Pair<PieceColor, Line.Segment>>> removableLineSetOrderingsOpponent = getRemovableLineOrderingsSetFromGipfBoard( opponentSegmentRemovedPieceMap, opponentColor); if (removableLineSetOrderingsOpponent.size() > 0) { for (List<Pair<PieceColor, Line.Segment>> removableLineSegmentOpponentList : removableLineSetOrderingsOpponent) { List<Pair<PieceColor, Line.Segment>> ordering = new ArrayList<>(opponentRemovableLineSegmentOrdering); ordering.addAll(removableLineSegmentOpponentList); removableLineSetOrderingsFromGipfboard.add(ordering); } } else { removableLineSetOrderingsFromGipfboard.add(opponentRemovableLineSegmentOrdering); } } } if (removableLineSegmentsCurrentPlayer.size() == 0) { // The current player can't remove any line segments, so the opponent can try to remove line // segments for (Line.Segment lineSegment : getRemovableLineSegments(pieceMap, opponentColor)) { // See for the comments the similar lines above HashMap<Position, Piece> segmentRemovedPieceMap = new HashMap<>(pieceMap); segmentRemovedPieceMap .keySet() .removeAll(lineSegment.getOccupiedPositions(segmentRemovedPieceMap)); List<Pair<PieceColor, Line.Segment>> removableLineSegmentOrdering = Collections.singletonList(new Pair<>(opponentColor, lineSegment)); Set<List<Pair<PieceColor, Line.Segment>>> removableLineSetOrderingsOpponent = getRemovableLineOrderingsSetFromGipfBoard(segmentRemovedPieceMap, opponentColor); if (removableLineSetOrderingsOpponent.size() > 0) { for (List<Pair<PieceColor, Line.Segment>> removableLineSegmentList : removableLineSetOrderingsOpponent) { List<Pair<PieceColor, Line.Segment>> ordering = new ArrayList<>(removableLineSegmentOrdering); ordering.addAll(removableLineSegmentList); removableLineSetOrderingsFromGipfboard.add(ordering); } } else { removableLineSetOrderingsFromGipfboard.add(removableLineSegmentOrdering); } } } return removableLineSetOrderingsFromGipfboard; }
public Set<Move> getAllowedMoves() { // If there is already a winn if (gipfBoardState.players.winner() != null) { return Collections.emptySet(); } // Create a set of incomplete moves containing the starting positions and directions for the // current piece Set<Move> potentialMoves = getPotentialStartMoves(getCurrentPiece()); // If the current piece is a GIPF piece, the player is also allowed to place normal pieces. if (getCurrentPiece().getPieceType() == GIPF) potentialMoves.addAll( getPotentialStartMoves(Piece.of(NORMAL, getCurrentPiece().getPieceColor()))); // These moves are marked as complete so a temporary game won't ask for user input. potentialMoves.stream().forEach(m -> m.isCompleteMove = true); Set<Move> potentialMovesIncludingLineSegmentRemoval = new HashSet<>(); for (Move potentialMove : potentialMoves) { try { Map<Position, Piece> temporaryPieceMap = new HashMap<>(getGipfBoardState().getPieceMap()); temporaryPieceMap.put(potentialMove.startPos, potentialMove.addedPiece); movePiecesTowards( temporaryPieceMap, potentialMove.getStartingPosition(), potentialMove.getDirection()); Set<List<Pair<PieceColor, Line.Segment>>> RLineOrderingsSet = getRemovableLineOrderingsSetFromGipfBoard( temporaryPieceMap, getCurrentPiece().getPieceColor()); if (RLineOrderingsSet.size() > 0) { for (List<Pair<PieceColor, Line.Segment>> RLineOrdering : RLineOrderingsSet) { Set<Position> piecesToWhite = new HashSet<>(); Set<Position> piecesToBlack = new HashSet<>(); Set<Position> piecesRemoved = new HashSet<>(); for (Pair<PieceColor, Line.Segment> RLine : RLineOrdering) { Line.Segment removedSegment = RLine.getValue(); // The color of the player who removed the line PieceColor colorRemoved = RLine.getKey(); // Determine per segment to whom the pieces are given. Pieces can only be given to the // player // who removed the line, or deleted from the game. Set<Position> occupiedPositions = removedSegment.getOccupiedPositions(temporaryPieceMap); Set<Position> piecesFromSegmentBackToReserve = occupiedPositions .stream() .filter( position -> temporaryPieceMap.get(position).getPieceColor() == colorRemoved) .collect(toSet()); Set<Position> piecesFromSegmentRemoved = occupiedPositions .stream() .filter(position -> !piecesFromSegmentBackToReserve.contains(position)) .collect(toSet()); if (colorRemoved == WHITE) piecesToWhite.addAll(piecesFromSegmentBackToReserve); if (colorRemoved == BLACK) piecesToBlack.addAll(piecesFromSegmentBackToReserve); piecesRemoved.addAll(piecesFromSegmentRemoved); } // And finally add the move // the constructor will define this as a complete move, because all the parameters have // a value. potentialMovesIncludingLineSegmentRemoval.add( new Move( potentialMove.addedPiece, potentialMove.startPos, potentialMove.direction, piecesToWhite, piecesToBlack, piecesRemoved)); } } else { // If no line segments can be removed, just add the original move potentialMovesIncludingLineSegmentRemoval.add(potentialMove); } } catch (InvalidMoveException e) { // We don't consider this move if it is invalid } } return potentialMovesIncludingLineSegmentRemoval; }