Skip to content

Commit fa80c4b

Browse files
author
Jasper Geurtz
committed
Optimize Unit.getDistance, Unit.updatePosition, getUnitsInRectangle, and canBuildHere
1 parent 18448a1 commit fa80c4b

3 files changed

Lines changed: 133 additions & 59 deletions

File tree

‎src/main/java/bwapi/Game.java‎

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -692,16 +692,16 @@ public List<Unit> getUnitsOnTile(final int tileX, final int tileY) {
692692
* given build tile.
693693
*/
694694
public List<Unit> getUnitsOnTile(final int tileX, final int tileY, final UnitFilter pred) {
695-
final TilePosition tile = new TilePosition(tileX, tileY);
696-
if (!tile.isValid(this)) {
695+
final int x = tileX * TilePosition.SIZE_IN_PIXELS;
696+
final int y = tileY * TilePosition.SIZE_IN_PIXELS;
697+
if (tileX < 0 || tileY < 0 || x >= mapPixelWidth() || y >= mapPixelHeight()) {
697698
return Collections.emptyList();
698699
}
699-
final Position p = tile.toPosition();
700-
return getUnitsInRectangle(p.x, p.y, p.x + 32, p.y + 32, pred);
700+
return getUnitsInRectangle(x, y, x + 32, y + 32, pred);
701701
}
702702

703703
public List<Unit> getUnitsInRectangle(final int left, final int top, final int right, final int bottom) {
704-
return getUnitsInRectangle(left, top, right, bottom, u -> true);
704+
return getUnitsInRectangle(left, top, right, bottom, null);
705705
}
706706

707707
/**
@@ -716,13 +716,17 @@ public List<Unit> getUnitsInRectangle(final int left, final int top, final int r
716716
* given rectangle bounds.
717717
*/
718718
public List<Unit> getUnitsInRectangle(final int left, final int top, final int right, final int bottom, final UnitFilter pred) {
719-
return getAllUnits().stream()
720-
.filter(u -> left <= u.getRight() && top <= u.getBottom() && right >= u.getLeft() && bottom >= u.getTop() && pred.test(u))
721-
.collect(Collectors.toList());
719+
final List<Unit> result = new ArrayList<>();
720+
for (final Unit u : getAllUnits()) {
721+
if (Unit.overlapsRectangle(u, left, top, right, bottom) && (pred == null || pred.test(u))) {
722+
result.add(u);
723+
}
724+
}
725+
return result;
722726
}
723727

724728
public List<Unit> getUnitsInRectangle(final Position leftTop, final Position rightBottom) {
725-
return getUnitsInRectangle(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y, u -> true);
729+
return getUnitsInRectangle(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y, null);
726730
}
727731

728732
public List<Unit> getUnitsInRectangle(final Position leftTop, final Position rightBottom, final UnitFilter pred) {
@@ -1105,69 +1109,80 @@ public boolean canBuildHere(final TilePosition position, final UnitType type) {
11051109
*/
11061110
public boolean canBuildHere(final TilePosition position, final UnitType type, final Unit builder, final boolean checkExplored) {
11071111
// lt = left top, rb = right bottom
1108-
final TilePosition lt = builder != null && type.isAddon() ?
1109-
position.add(new TilePosition(4, 1)) : // addon build offset
1110-
position;
1111-
final TilePosition rb = lt.add(type.tileSize());
1112+
final int ltX = builder != null && type.isAddon() ? position.x + 4 : position.x;
1113+
final int ltY = builder != null && type.isAddon() ? position.y + 1 : position.y;
1114+
final int rbX = ltX + type.tileWidth();
1115+
final int rbY = ltY + type.tileHeight();
11121116

11131117
// Map limit check
1114-
if (!lt.isValid(this) || !(rb.toPosition().subtract(new Position(1, 1)).isValid(this))) {
1118+
if (!isValidTile(ltX, ltY) || !isValidPosition(rbX * TilePosition.SIZE_IN_PIXELS - 1, rbY * TilePosition.SIZE_IN_PIXELS - 1)) {
11151119
return false;
11161120
}
11171121

11181122
//if the getUnit is a refinery, we just need to check the set of geysers to see if the position
11191123
//matches one of them (and the type is still vespene geyser)
11201124
if (type.isRefinery()) {
11211125
for (final Unit g : getGeysers()) {
1122-
if (g.getTilePosition().equals(lt)) {
1126+
final TilePosition tp = g.getTilePosition();
1127+
if (tp.x == ltX && tp.y == ltY) {
11231128
return !g.isVisible() || g.getType() == Resource_Vespene_Geyser;
11241129
}
11251130
}
11261131
return false;
11271132
}
11281133

11291134
// Tile buildability check
1130-
for (int x = lt.x; x < rb.x; ++x) {
1131-
for (int y = lt.y; y < rb.y; ++y) {
1135+
for (int x = ltX; x < rbX; ++x) {
1136+
for (int y = ltY; y < rbY; ++y) {
11321137
// Check if tile is buildable/unoccupied and explored.
11331138
if (!isBuildable(x, y) || (checkExplored && !isExplored(x, y))) {
11341139
return false;
11351140
}
11361141
}
11371142
}
11381143

1144+
final int targPosX = ltX * TilePosition.SIZE_IN_PIXELS + (type.tileWidth() * TilePosition.SIZE_IN_PIXELS) / 2;
1145+
final int targPosY = ltY * TilePosition.SIZE_IN_PIXELS + (type.tileHeight() * TilePosition.SIZE_IN_PIXELS) / 2;
1146+
11391147
// Check if builder is capable of reaching the building site
11401148
if (builder != null) {
1141-
if (!builder.getType().isBuilding()) {
1142-
if (!builder.hasPath(lt.toPosition().add(type.tileSize().toPosition().divide(2)))) {
1149+
final UnitType builderType = builder.getType();
1150+
if (!builderType.isBuilding()) {
1151+
if (!builder.hasPath(new Position(targPosX, targPosY))) {
11431152
return false;
11441153
}
1145-
} else if (!builder.getType().isFlyingBuilding() && type != Zerg_Nydus_Canal && !type.isFlagBeacon()) {
1154+
} else if (!builderType.isFlyingBuilding() && type != Zerg_Nydus_Canal && !type.isFlagBeacon()) {
11461155
return false;
11471156
}
11481157
}
11491158

11501159
// Ground getUnit dimension check
11511160
if (type != Special_Start_Location) {
1152-
final Position targPos = lt.toPosition().add(type.tileSize().toPosition().divide(2));
1153-
final List<Unit> unitsInRect = getUnitsInRectangle(
1154-
targPos.subtract(new Position(type.dimensionLeft(), type.dimensionUp())),
1155-
targPos.add(new Position(type.dimensionRight(), type.dimensionDown())),
1156-
u -> !u.isFlying() && !u.isLoaded() && (builder != u || type == Zerg_Nydus_Canal));
1157-
1158-
for (final Unit u : unitsInRect) {
1159-
// Addons can be placed over units that can move, pushing them out of the way
1160-
if (!(type.isAddon() && u.getType().canMove())) {
1161-
return false;
1161+
final int rectLeft = targPosX - type.dimensionLeft();
1162+
final int rectTop = targPosY - type.dimensionUp();
1163+
final int rectRight = targPosX + type.dimensionRight();
1164+
final int rectBottom = targPosY + type.dimensionDown();
1165+
final boolean isAddon = type.isAddon();
1166+
final boolean isNydus = type == Zerg_Nydus_Canal;
1167+
1168+
for (final Unit u : getAllUnits()) {
1169+
if (Unit.overlapsRectangle(u, rectLeft, rectTop, rectRight, rectBottom)
1170+
&& !u.isFlying()
1171+
&& !u.isLoaded()
1172+
&& (builder != u || isNydus)) {
1173+
// Addons can be placed over units that can move, pushing them out of the way
1174+
if (!(isAddon && u.getType().canMove())) {
1175+
return false;
1176+
}
11621177
}
11631178
}
11641179

11651180
// Creep Check
11661181
// Note: Zerg structures that don't require creep can still be placed on creep
11671182
final boolean needsCreep = type.requiresCreep();
11681183
if (type.getRace() != Zerg || needsCreep) {
1169-
for (int x = lt.x; x < rb.x; ++x) {
1170-
for (int y = lt.y; y < rb.y; ++y) {
1184+
for (int x = ltX; x < rbX; ++x) {
1185+
for (int y = ltY; y < rbY; ++y) {
11711186
if (needsCreep != hasCreep(x, y)) {
11721187
return false;
11731188
}
@@ -1176,7 +1191,7 @@ public boolean canBuildHere(final TilePosition position, final UnitType type, fi
11761191
}
11771192

11781193
// Power Check
1179-
if (type.requiresPsi() && !hasPower(lt, type)) {
1194+
if (type.requiresPsi() && !hasPower(ltX, ltY, type)) {
11801195
return false;
11811196
}
11821197

@@ -1189,19 +1204,19 @@ public boolean canBuildHere(final TilePosition position, final UnitType type, fi
11891204
if ((isVisible(tp) || isVisible(tp.x + 1, tp.y)) && !m.exists()) {
11901205
continue; // tile position is visible, but mineral is not => mineral does not exist
11911206
}
1192-
if (tp.x > lt.x - 5 &&
1193-
tp.y > lt.y - 4 &&
1194-
tp.x < lt.x + 7 &&
1195-
tp.y < lt.y + 6) {
1207+
if (tp.x > ltX - 5 &&
1208+
tp.y > ltY - 4 &&
1209+
tp.x < ltX + 7 &&
1210+
tp.y < ltY + 6) {
11961211
return false;
11971212
}
11981213
}
11991214
for (final Unit g : getStaticGeysers()) {
12001215
final TilePosition tp = g.getInitialTilePosition();
1201-
if (tp.x > lt.x - 7 &&
1202-
tp.y > lt.y - 5 &&
1203-
tp.x < lt.x + 7 &&
1204-
tp.y < lt.y + 6) {
1216+
if (tp.x > ltX - 7 &&
1217+
tp.y > ltY - 5 &&
1218+
tp.x < ltX + 7 &&
1219+
tp.y < ltY + 6) {
12051220
return false;
12061221
}
12071222
}
@@ -1211,7 +1226,7 @@ public boolean canBuildHere(final TilePosition position, final UnitType type, fi
12111226
// then lands at the new location before building the addon), so we need to do similar checks for the
12121227
// location that the building will be when it builds the addon.
12131228
if (builder != null && !builder.getType().isAddon() && type.isAddon()) {
1214-
return canBuildHere(lt.subtract(new TilePosition(4, 1)), builder.getType(), builder, checkExplored);
1229+
return canBuildHere(new TilePosition(ltX - 4, ltY - 1), builder.getType(), builder, checkExplored);
12151230
}
12161231

12171232
//if the build site passes all these tests, return true.

‎src/main/java/bwapi/Point.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public double getDistance(T point) {
3535
return Math.sqrt(dx * dx + dy * dy);
3636
}
3737

38-
private static int getApproxDistance(final int x1, final int y1, final int x2, final int y2) {
38+
static int getApproxDistance(final int x1, final int y1, final int x2, final int y2) {
3939
int max = Math.abs(x1 - x2);
4040
int min = Math.abs(y1 - y2);
4141
if (max < min) {

‎src/main/java/bwapi/Unit.java‎

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ boolean hasLatCom() {
8080
this.unitData = unitData;
8181
this.game = game;
8282

83-
updatePosition(0);
83+
position = new Position(unitData.getPositionX(), unitData.getPositionY());
84+
lastPositionUpdate = 0;
8485

8586
initialType = getType();
8687
initialResources = getResources();
@@ -395,26 +396,41 @@ public int getDistance(final Position target) {
395396
}
396397
/////// Compute distance
397398

399+
final UnitType type = getType();
400+
final Position pos = this.position != null ? this.position : getPosition();
401+
final int left, right, top, bottom;
402+
if (type != null && pos != null) {
403+
left = pos.x - type.dimensionLeft();
404+
right = pos.x + type.dimensionRight();
405+
top = pos.y - type.dimensionUp();
406+
bottom = pos.y + type.dimensionDown();
407+
} else {
408+
left = getLeft();
409+
right = getRight();
410+
top = getTop();
411+
bottom = getBottom();
412+
}
413+
398414
// compute x distance
399-
int xDist = getLeft() - target.x;
415+
int xDist = left - target.x;
400416
if (xDist < 0) {
401-
xDist = target.x - (getRight() + 1);
417+
xDist = target.x - (right + 1);
402418
if (xDist < 0) {
403419
xDist = 0;
404420
}
405421
}
406422

407423
// compute y distance
408-
int yDist = getTop() - target.y;
424+
int yDist = top - target.y;
409425
if (yDist < 0) {
410-
yDist = target.y - (getBottom() + 1);
426+
yDist = target.y - (bottom + 1);
411427
if (yDist < 0) {
412428
yDist = 0;
413429
}
414430
}
415431

416432
// compute actual distance
417-
return Position.Origin.getApproxDistance(new Position(xDist, yDist));
433+
return Point.getApproxDistance(0, 0, xDist, yDist);
418434
}
419435

420436
public int getDistance(final Unit target) {
@@ -430,32 +446,57 @@ public int getDistance(final Unit target) {
430446

431447
/////// Compute distance
432448

449+
final UnitType thisType = getType();
450+
final Position thisPos = this.position != null ? this.position : getPosition();
451+
final int thisLeft, thisRight, thisTop, thisBottom;
452+
if (thisType != null && thisPos != null) {
453+
thisLeft = thisPos.x - thisType.dimensionLeft();
454+
thisRight = thisPos.x + thisType.dimensionRight();
455+
thisTop = thisPos.y - thisType.dimensionUp();
456+
thisBottom = thisPos.y + thisType.dimensionDown();
457+
} else {
458+
thisLeft = getLeft();
459+
thisRight = getRight();
460+
thisTop = getTop();
461+
thisBottom = getBottom();
462+
}
463+
433464
// retrieve left/top/right/bottom values for calculations
434-
final int left = target.getLeft() - 1;
435-
final int top = target.getTop() - 1;
436-
final int right = target.getRight() + 1;
437-
final int bottom = target.getBottom() + 1;
465+
final UnitType targetType = target.getType();
466+
final Position targetPos = target.position != null ? target.position : target.getPosition();
467+
final int left, top, right, bottom;
468+
if (targetType != null && targetPos != null) {
469+
left = targetPos.x - targetType.dimensionLeft() - 1;
470+
top = targetPos.y - targetType.dimensionUp() - 1;
471+
right = targetPos.x + targetType.dimensionRight() + 1;
472+
bottom = targetPos.y + targetType.dimensionDown() + 1;
473+
} else {
474+
left = target.getLeft() - 1;
475+
top = target.getTop() - 1;
476+
right = target.getRight() + 1;
477+
bottom = target.getBottom() + 1;
478+
}
438479

439480
// compute x distance
440-
int xDist = getLeft() - right;
481+
int xDist = thisLeft - right;
441482
if (xDist < 0) {
442-
xDist = left - getRight();
483+
xDist = left - thisRight;
443484
if (xDist < 0) {
444485
xDist = 0;
445486
}
446487
}
447488

448489
// compute y distance
449-
int yDist = getTop() - bottom;
490+
int yDist = thisTop - bottom;
450491
if (yDist < 0) {
451-
yDist = top - getBottom();
492+
yDist = top - thisBottom;
452493
if (yDist < 0) {
453494
yDist = 0;
454495
}
455496
}
456497

457498
// compute actual distance
458-
return Position.Origin.getApproxDistance(new Position(xDist, yDist));
499+
return Point.getApproxDistance(0, 0, xDist, yDist);
459500
}
460501

461502
/**
@@ -6413,10 +6454,28 @@ public int compareTo(final Unit other) {
64136454
return id - other.id;
64146455
}
64156456

6457+
static boolean overlapsRectangle(final Unit u, final int rectLeft, final int rectTop, final int rectRight, final int rectBottom) {
6458+
final Position pos = u.position;
6459+
if (pos != null) {
6460+
final UnitType type = u.getType();
6461+
if (type != null) {
6462+
return rectLeft <= pos.x + type.dimensionRight()
6463+
&& rectTop <= pos.y + type.dimensionDown()
6464+
&& rectRight >= pos.x - type.dimensionLeft()
6465+
&& rectBottom >= pos.y - type.dimensionUp();
6466+
}
6467+
}
6468+
return rectLeft <= u.getRight() && rectTop <= u.getBottom() && rectRight >= u.getLeft() && rectBottom >= u.getTop();
6469+
}
6470+
64166471
void updatePosition(final int frame) {
64176472
if (frame > lastPositionUpdate) {
64186473
lastPositionUpdate = frame;
6419-
position = new Position(unitData.getPositionX(), unitData.getPositionY());
6474+
final int newX = unitData.getPositionX();
6475+
final int newY = unitData.getPositionY();
6476+
if (((position.x ^ newX) | (position.y ^ newY)) != 0) {
6477+
position = new Position(newX, newY);
6478+
}
64206479
}
64216480
}
64226481
}

0 commit comments

Comments
 (0)