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
}