Skip to content

Commit

Permalink
Rewrite findTargets for better performance.
Browse files Browse the repository at this point in the history
- Avoids O(n^2) destroyer checks.
- Avoids an expensive removeAll() call.
- Uses a single stream filter to process, removing an unnecessary intermediate collection in the common case when no destroyers are present.

Note: This was showing up in performance profiles.
  • Loading branch information
asvitkine committed Jul 10, 2023
1 parent fa68931 commit 6608d25
Showing 1 changed file with 17 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -44,13 +43,13 @@ public Collection<Unit> getTargetUnits(final Collection<Unit> units) {
*/
public static List<TargetGroup> newTargetGroups(
final Collection<Unit> units, final Collection<Unit> enemyUnits) {

final Set<UnitType> unitTypes = units.stream().map(Unit::getType).collect(Collectors.toSet());
final boolean destroyerPresent = unitTypes.stream().anyMatch(Matches.unitTypeIsDestroyer());
final Set<UnitType> enemyUnitTypes =
enemyUnits.stream().map(Unit::getType).collect(Collectors.toSet());
final List<TargetGroup> targetGroups = new ArrayList<>();
for (final UnitType unitType : unitTypes) {
final Set<UnitType> targets = findTargets(unitType, unitTypes, enemyUnitTypes);
final Set<UnitType> targets = findTargets(unitType, destroyerPresent, enemyUnitTypes);
if (targets.isEmpty()) {
continue;
}
Expand All @@ -65,15 +64,21 @@ public static List<TargetGroup> newTargetGroups(
}

private static Set<UnitType> findTargets(
final UnitType unitType, final Set<UnitType> unitTypes, final Set<UnitType> enemyUnitTypes) {
final Set<UnitType> targets = new HashSet<>(enemyUnitTypes);
targets.removeAll(unitType.getUnitAttachment().getCanNotTarget());
return unitTypes.stream().anyMatch(Matches.unitTypeIsDestroyer())
? targets
: targets.stream()
.filter(
target -> !target.getUnitAttachment().getCanNotBeTargetedBy().contains(unitType))
.collect(Collectors.toSet());
UnitType unitType, boolean destroyerPresent, Set<UnitType> enemyUnitTypes) {
Set<UnitType> cannotTarget = unitType.getUnitAttachment().getCanNotTarget();
// Note: uses a single stream instead of a sequence of removeAll() calls for performance.
return enemyUnitTypes.stream()
.filter(
ut -> {
if (cannotTarget.contains(ut)) {
return false;
}
if (destroyerPresent) {
return true;
}
return !ut.getUnitAttachment().getCanNotBeTargetedBy().contains(unitType);
})
.collect(Collectors.toSet());
}

private static Optional<TargetGroup> findTargetsInTargetGroups(
Expand Down

0 comments on commit 6608d25

Please sign in to comment.