mutating func addStone()

in MiniGo/GameLib/LibertyTracker.swift [69:155]


    mutating func addStone(at position: Position, withColor color: Color) throws -> Set<Position> {
        precondition(groupIndex(for: position) == nil)

        printDebugInfo(message: "Before adding stone.")

        var capturedStones = Set<Position>()

        // Records neighbors information.
        var emptyNeighbors = Set<Position>()
        var opponentNeighboringGroupIDs = Set<Int>()
        var friendlyNeighboringGroupIDs = Set<Int>()

        for neighbor in position.neighbors(boardSize: gameConfiguration.size) {
            // First, handle the case neighbor has no group.
            guard let neighborGroupID = groupIndex(for: neighbor) else {
                emptyNeighbors.insert(neighbor)
                continue
            }

            guard let neighborGroup = groups[neighborGroupID] else {
                fatalErrorForGroupsInvariance(groupID: neighborGroupID)
            }

            if neighborGroup.color == color {
                friendlyNeighboringGroupIDs.insert(neighborGroupID)
            } else {
                opponentNeighboringGroupIDs.insert(neighborGroupID)
            }
        }

        if gameConfiguration.isVerboseDebuggingEnabled {
            print("empty: \(emptyNeighbors)")
            print("friends: \(friendlyNeighboringGroupIDs)")
            print("opponents: \(opponentNeighboringGroupIDs)")
        }

        // Creates new group and sets its liberty as the empty neighbors at first.
        var newGroupID = makeGroup(
            color: color,
            stone: position,
            liberties: emptyNeighbors
        ).id

        // Merging all friend groups.
        for friendGroupID in friendlyNeighboringGroupIDs {
            newGroupID = mergeGroups(newGroupID, friendGroupID)
        }

        // Calculates captured stones.
        for opponentGroupID in opponentNeighboringGroupIDs {
            guard var opponentGroup = groups[opponentGroupID] else {
                fatalErrorForGroupsInvariance(groupID: opponentGroupID)
            }

            guard opponentGroup.liberties.count > 1 else {
                // The only liberty will be taken by the stone just placed. Delete it.
                capturedStones.formUnion(captureGroup(opponentGroupID))
                continue
            }

            // Updates the liberty taken by the stone just placed.
            opponentGroup.liberties.remove(position)
            // As group is struct, we need to explicitly write it back.
            groups[opponentGroupID] = opponentGroup
            assert(checkLibertyGroupsInvariance())
        }

        if gameConfiguration.isVerboseDebuggingEnabled {
            print("captured stones: \(capturedStones)")
        }

        // Update liberties for existing stones
        updateLibertiesAfterRemovingCapturedStones(capturedStones)

        printDebugInfo(message: "After adding stone.")

        // Suicide is illegal.
        guard let newGroup = groups[newGroupID] else {
            fatalErrorForGroupsInvariance(groupID: newGroupID)
        }

        guard !newGroup.liberties.isEmpty else {
            throw IllegalMove.suicide
        }

        return capturedStones
    }