Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/main/java/bwapi/ConnectedUnitCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package bwapi;

import java.util.*;
import java.util.function.Function;

class ConnectedUnitCache {
private int lastUpdate = -1;
private final Map<Unit, List<Unit>> connectedUnits = new HashMap<>();
private final Function<Unit, Unit> condition;
private final Game game;

ConnectedUnitCache(final Game game, final Function<Unit, Unit> condition) {
this.game = game;
this.condition = condition;
}

/**
* Lazily update connectedUnits. Only users of the calls pay for it, and only
* pay once per frame.
* Avoids previous O(n^2) implementation which would be costly for
* lategame carrier fights
*/
List<Unit> getConnected(final Unit unit) {
final int frame = game.getFrameCount();
if (lastUpdate < frame) {
connectedUnits.values().forEach(List::clear);
for (final Unit u : game.getAllUnits()) {
final Unit owner = condition.apply(u);
if (owner != null) {
if (!connectedUnits.containsKey(owner)) {
connectedUnits.put(owner, new ArrayList<>());
}
connectedUnits.get(owner).add(u);
}
}
lastUpdate = frame;
}
if (!connectedUnits.containsKey(unit)) {
return Collections.emptyList();
}
return Collections.unmodifiableList(connectedUnits.get(unit));
}
void reset() {
lastUpdate = -1;
connectedUnits.clear();
}
}
70 changes: 8 additions & 62 deletions src/main/java/bwapi/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ public final class Game {
private static final int REGION_DATA_SIZE = 5000;

private final Set<Integer> visibleUnits = new HashSet<>();
private final Map<Unit, List<Unit>> connectedUnits = new HashMap<>();
private int lastConnectedUnitsUpdate = -1;
private final Map<Unit, List<Unit>> loadedUnits = new HashMap<>();
private int lastLoadedUnitsUpdate = -1;

private List<Unit> allUnits;
private final ClientData clientData;
Expand Down Expand Up @@ -103,6 +99,10 @@ public final class Game {
private BWClientConfiguration configuration = new BWClientConfiguration();
private boolean latcom = true;

final ConnectedUnitCache loadedUnitsCache = new ConnectedUnitCache(this, Unit::getTransport);
final ConnectedUnitCache interceptorsCache = new ConnectedUnitCache(this, Unit::getCarrier);
final ConnectedUnitCache larvaCache = new ConnectedUnitCache(this, Unit::getHatchery);

final SideEffectQueue sideEffects = new SideEffectQueue();

Game() {
Expand Down Expand Up @@ -151,10 +151,10 @@ private static boolean hasPower(final int x, final int y, final UnitType unitTyp
*/
void init() {
visibleUnits.clear();
connectedUnits.clear();
lastConnectedUnitsUpdate = -1;
loadedUnits.clear();
lastLoadedUnitsUpdate = -1;

loadedUnitsCache.reset();
interceptorsCache.reset();
larvaCache.reset();

final int forceCount = gameData().getForceCount();
forces = new Force[forceCount];
Expand Down Expand Up @@ -319,60 +319,6 @@ void onFrame(final int frame) {
getAllUnits().forEach(u -> u.updatePosition(frame));
}

/**
* Lazily update connectedUnits. Only users of the calls pay for it, and only
* pay once per frame.
* Avoids previous O(n^2) implementation which would be costly for
* lategame carrier fights
*/
List<Unit> getConnected(final Unit unit) {
final int frame = getFrameCount();
if (lastConnectedUnitsUpdate < frame) {
connectedUnits.values().forEach(List::clear);
for (final Unit u : getAllUnits()) {
Unit owner = u.getCarrier();
if (owner == null) {
owner = u.getHatchery();
}
if (owner != null) {
if (!connectedUnits.containsKey(owner)) {
connectedUnits.put(owner, new ArrayList<>());
}
connectedUnits.get(owner).add(u);
}
}
lastConnectedUnitsUpdate = frame;
}
if (!connectedUnits.containsKey(unit)) {
return Collections.emptyList();
}
return Collections.unmodifiableList(connectedUnits.get(unit));
}

/**
* @see #getConnected
*/
List<Unit> getLoadedUnits(final Unit unit) {
final int frame = getFrameCount();
if (lastLoadedUnitsUpdate < frame) {
loadedUnits.values().forEach(List::clear);
for (final Unit u : getAllUnits()) {
final Unit owner = u.getTransport();
if (owner != null) {
if (!loadedUnits.containsKey(owner)) {
loadedUnits.put(owner, new ArrayList<>());
}
loadedUnits.get(owner).add(u);
}
}
lastLoadedUnitsUpdate = frame;
}
if (!loadedUnits.containsKey(unit)) {
return Collections.emptyList();
}
return Collections.unmodifiableList(loadedUnits.get(unit));
}

/**
* Retrieves the set of all teams/forces. Forces are commonly seen in @UMS
* game types and some others such as @TvB and the team versions of game types.
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/bwapi/Unit.java
Original file line number Diff line number Diff line change
Expand Up @@ -1172,7 +1172,7 @@ public List<Unit> getLoadedUnits() {
if (getType().spaceProvided() < 1) {
return Collections.emptyList();
}
return game.getLoadedUnits(this);
return game.loadedUnitsCache.getConnected(this);
}

/**
Expand Down Expand Up @@ -1212,7 +1212,7 @@ public List<Unit> getInterceptors() {
if (getType() != Protoss_Carrier && getType() != Hero_Gantrithor) {
return Collections.emptyList();
}
return game.getConnected(this);
return game.interceptorsCache.getConnected(this);
}

/**
Expand Down Expand Up @@ -1240,7 +1240,7 @@ public List<Unit> getLarva() {
if (!getType().producesLarva()) {
return Collections.emptyList();
}
return game.getConnected(this);
return game.larvaCache.getConnected(this);
}

public List<Unit> getUnitsInRadius(final int radius) {
Expand Down