/** 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; }
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; }