From e3e679549dad2f6dba8e10c00434fa1d27023a8a Mon Sep 17 00:00:00 2001 From: JasperGeurtz Date: Thu, 11 Mar 2021 19:44:53 +0100 Subject: [PATCH 1/3] cache calls to connected&loaded units per frame --- src/main/java/bwapi/Game.java | 63 +++++++++++++++++++++++++++++++++++ src/main/java/bwapi/Unit.java | 12 ++----- 2 files changed, 66 insertions(+), 9 deletions(-) diff --git a/src/main/java/bwapi/Game.java b/src/main/java/bwapi/Game.java index 045b794c..19fbe672 100644 --- a/src/main/java/bwapi/Game.java +++ b/src/main/java/bwapi/Game.java @@ -43,6 +43,11 @@ public final class Game { private static final int REGION_DATA_SIZE = 5000; private final Set visibleUnits = new HashSet<>(); + private final Map> connectedUnits = new HashMap<>(); + private int lastConnectedUnitsUpdate = -1; + private final Map> loadedUnits = new HashMap<>(); + private int lastLoadedUnitsUpdate = -1; + private List allUnits; private final ClientData clientData; @@ -146,6 +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; final int forceCount = gameData().getForceCount(); forces = new Force[forceCount]; @@ -310,6 +319,60 @@ 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 + */ + public List getConnected(final Unit unit) { + final int frame = getFrameCount(); + if (lastConnectedUnitsUpdate < frame) { + connectedUnits.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 new ArrayList<>(connectedUnits.get(unit)); + } + + /** + * @see #getConnected + */ + public List getLoadedUnits(final Unit unit) { + final int frame = getFrameCount(); + if (lastLoadedUnitsUpdate < frame) { + loadedUnits.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 new ArrayList<>(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. diff --git a/src/main/java/bwapi/Unit.java b/src/main/java/bwapi/Unit.java index 50c9114f..5da18e92 100644 --- a/src/main/java/bwapi/Unit.java +++ b/src/main/java/bwapi/Unit.java @@ -1157,9 +1157,7 @@ public List getLoadedUnits() { if (getType().spaceProvided() < 1) { return Collections.emptyList(); } - return game.getAllUnits().stream() - .filter(u -> equals(u.getTransport())) - .collect(Collectors.toList()); + return game.getLoadedUnits(this); } /** @@ -1199,9 +1197,7 @@ public List getInterceptors() { if (getType() != Protoss_Carrier && getType() != Hero_Gantrithor) { return Collections.emptyList(); } - return game.getAllUnits().stream() - .filter(u -> equals(u.getCarrier())) - .collect(Collectors.toList()); + return game.getConnected(this); } /** @@ -1229,9 +1225,7 @@ public List getLarva() { if (!getType().producesLarva()) { return Collections.emptyList(); } - return game.getAllUnits().stream() - .filter(u -> equals(u.getHatchery())) - .collect(Collectors.toList()); + return game.getConnected(this); } public List getUnitsInRadius(final int radius) { From 754223e0bf574bf1411bf178084fd72403865c33 Mon Sep 17 00:00:00 2001 From: JasperGeurtz Date: Thu, 11 Mar 2021 20:56:44 +0100 Subject: [PATCH 2/3] use a view of the array instead of allocating a new one --- src/main/java/bwapi/Game.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/bwapi/Game.java b/src/main/java/bwapi/Game.java index 19fbe672..729d0b02 100644 --- a/src/main/java/bwapi/Game.java +++ b/src/main/java/bwapi/Game.java @@ -325,7 +325,7 @@ void onFrame(final int frame) { * Avoids previous O(n^2) implementation which would be costly for * lategame carrier fights */ - public List getConnected(final Unit unit) { + List getConnected(final Unit unit) { final int frame = getFrameCount(); if (lastConnectedUnitsUpdate < frame) { connectedUnits.clear(); @@ -346,13 +346,13 @@ public List getConnected(final Unit unit) { if (!connectedUnits.containsKey(unit)) { return Collections.emptyList(); } - return new ArrayList<>(connectedUnits.get(unit)); + return Collections.unmodifiableList(connectedUnits.get(unit)); } /** * @see #getConnected */ - public List getLoadedUnits(final Unit unit) { + List getLoadedUnits(final Unit unit) { final int frame = getFrameCount(); if (lastLoadedUnitsUpdate < frame) { loadedUnits.clear(); @@ -370,7 +370,7 @@ public List getLoadedUnits(final Unit unit) { if (!loadedUnits.containsKey(unit)) { return Collections.emptyList(); } - return new ArrayList<>(loadedUnits.get(unit)); + return Collections.unmodifiableList(loadedUnits.get(unit)); } /** From cf6d0515a9a04c5a2d6dc6f42e2cad0e4eae030c Mon Sep 17 00:00:00 2001 From: JasperGeurtz Date: Sat, 13 Mar 2021 18:53:17 +0100 Subject: [PATCH 3/3] clear individual lists instead of the whole map --- src/main/java/bwapi/Game.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/bwapi/Game.java b/src/main/java/bwapi/Game.java index 729d0b02..4a30a1d5 100644 --- a/src/main/java/bwapi/Game.java +++ b/src/main/java/bwapi/Game.java @@ -328,7 +328,7 @@ void onFrame(final int frame) { List getConnected(final Unit unit) { final int frame = getFrameCount(); if (lastConnectedUnitsUpdate < frame) { - connectedUnits.clear(); + connectedUnits.values().forEach(List::clear); for (final Unit u : getAllUnits()) { Unit owner = u.getCarrier(); if (owner == null) { @@ -355,7 +355,7 @@ List getConnected(final Unit unit) { List getLoadedUnits(final Unit unit) { final int frame = getFrameCount(); if (lastLoadedUnitsUpdate < frame) { - loadedUnits.clear(); + loadedUnits.values().forEach(List::clear); for (final Unit u : getAllUnits()) { final Unit owner = u.getTransport(); if (owner != null) {