Skip to content

Commit 55a5be7

Browse files
committed
use arrays for faster lookup & fix regionAt implementation
1 parent f3d84d5 commit 55a5be7

File tree

2 files changed

+123
-57
lines changed

2 files changed

+123
-57
lines changed

src/main/java/bwapi/EventHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public EventHandler(final BWListener eventListener, final Client.GameData data)
1818
public void operation(Client.GameData.Event event) {
1919
switch (event.type()) {
2020
case 0: //MatchStart
21-
game.reset();
21+
game.init();
2222
eventListener.onStart();
2323
break;
2424
case 1: //MatchEnd

src/main/java/bwapi/Game.java

Lines changed: 122 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,27 @@ public class Game {
2020
private final GameData gameData;
2121

2222
// CONSTANT
23-
private final Map<Integer, Player> players = new HashMap<>();
24-
private final Map<Integer, Region> regions = new HashMap<>();
25-
private final Map<Integer, Force> forces = new HashMap<>();
26-
private final Map<Integer, Bullet> bullets = new HashMap<>();
23+
private Player[] players;
24+
private Region[] regions;
25+
private Force[] forces;
26+
private Bullet[] bullets;
27+
28+
private Set<Force> forceSet;
29+
private Set<Player> playerSet;
30+
private Set<Region> regionSet;
2731

2832
private final Set<Unit> staticMinerals = new HashSet<>();
2933
private final Set<Unit> staticGeysers = new HashSet<>();
3034
private final Set<Unit> staticNeutralUnits = new HashSet<>();
3135

3236
// CHANGING
33-
private final Map<Integer, Unit> units = new HashMap<>();
37+
private Unit[] units;
3438
private final Set<Integer> visibleUnits = new HashSet<>();
3539

3640
//CACHED
3741
private int randomSeed;
3842
private int revision;
39-
private boolean debug;
43+
private boolean debug;
4044
private Player self;
4145
private Player enemy;
4246
private Player neutral;
@@ -53,6 +57,12 @@ public class Game {
5357

5458
private boolean[][] buildable;
5559
private boolean[][] walkable;
60+
private int[][] groundHeight;
61+
62+
private short[][] mapTileRegionID;
63+
private short[] mapSplitTilesMiniTileMask;
64+
private short[] mapSplitTilesRegion1;
65+
private short[] mapSplitTilesRegion2;
5666

5767
// USER DEFINED
5868
private TextSize textSize = TextSize.Default;
@@ -63,46 +73,52 @@ public Game(final GameData gameData) {
6373

6474
/*
6575
Call this method in EventHander::OnMatchStart
66-
*/
67-
void reset() {
68-
clear();
69-
init();
70-
}
71-
72-
private void clear() {
73-
players.clear();
74-
regions.clear();
75-
forces.clear();
76+
*/
77+
void init() {
7678
staticMinerals.clear();
7779
staticGeysers.clear();
7880
staticNeutralUnits.clear();
79-
units.clear();
8081
visibleUnits.clear();
81-
bullets.clear();
82-
}
8382

84-
private void init() {
85-
for (int id=0; id < gameData.getForceCount(); id++) {
86-
forces.put(id, new Force(gameData.getForce(id), this));
83+
final int forceCount = gameData.getForceCount();
84+
forces = new Force[forceCount];
85+
for (int id=0; id < forceCount; id++) {
86+
forces[id] = new Force(gameData.getForce(id), this);
87+
}
88+
89+
forceSet = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(forces)));
90+
91+
final int playerCount = gameData.getPlayerCount();
92+
players = new Player[playerCount];
93+
for (int id=0; id < playerCount; id++) {
94+
players[id] = new Player(gameData.getPlayer(id), this);
8795
}
88-
for (int id=0; id < gameData.getPlayerCount(); id++) {
89-
players.put(id, new Player(gameData.getPlayer(id), this));
96+
97+
playerSet = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(players)));
98+
99+
final int bulletCount = gameData.bulletCount();
100+
bullets = new Bullet[bulletCount];
101+
for (int id=0; id < bulletCount; id++) {
102+
bullets[id] = new Bullet(gameData.getBullet(id), this);
90103
}
91104

92-
for (int id=0; id < gameData.bulletCount(); id++) {
93-
bullets.put(id, new Bullet(gameData.getBullet(id), this));
105+
final int regionCount = gameData.regionCount();
106+
regions = new Region[regionCount];
107+
for (int id=0; id < regionCount; id++) {
108+
regions[id] = new Region(gameData.getRegion(id), this);
94109
}
95110

96-
for (int id=0; id < gameData.regionCount(); id++) {
97-
regions.put(id, new Region(gameData.getRegion(id), this));
111+
for (final Region region : regions) {
112+
region.updateNeighbours();
98113
}
99114

100-
regions.values().forEach(Region::updateNeighbours);
115+
regionSet = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(regions)));
101116

117+
units = new Unit[1000];
102118
for (int id=0; id < gameData.getInitialUnitCount(); id++) {
103119
final Unit unit = new Unit(gameData.getUnit(id), this);
104120

105-
units.put(id, unit);
121+
units[id] = unit;
106122

107123
if (unit.getType().isMineralField()) {
108124
staticMinerals.add(unit);
@@ -118,9 +134,9 @@ private void init() {
118134
randomSeed = gameData.randomSeed();
119135
revision = gameData.getRevision();
120136
debug = gameData.isDebug();
121-
self = players.get(gameData.self());
122-
enemy = players.get(gameData.enemy());
123-
neutral = players.get(gameData.neutral());
137+
self = players[gameData.self()];
138+
enemy = players[gameData.enemy()];
139+
neutral = players[gameData.neutral()];
124140
replay = gameData.isReplay();
125141
multiplayer = gameData.isMultiplayer();
126142
battleNet = gameData.isBattleNet();
@@ -135,9 +151,13 @@ private void init() {
135151
mapHash = gameData.mapHash();
136152

137153
buildable = new boolean[mapWidth][mapHeight];
138-
for (int i=0; i < mapWidth; i++) {
139-
for (int j=0; j < mapHeight; j++) {
140-
buildable[i][j] = gameData.buildable(i, j);
154+
groundHeight = new int[mapWidth][mapHeight];
155+
mapTileRegionID = new short[mapWidth][mapHeight];
156+
for (int x=0; x < mapWidth; x++) {
157+
for (int y=0; y < mapHeight; y++) {
158+
buildable[x][y] = gameData.buildable(x, y);
159+
groundHeight[x][y] = gameData.groundHeight(x, y);
160+
mapTileRegionID[x][y] = gameData.mapTileRegionID(x, y);
141161
}
142162
}
143163
walkable = new boolean[mapWidth*4][mapHeight*4];
@@ -146,11 +166,26 @@ private void init() {
146166
walkable[i][j] = gameData.walkable(i, j);
147167
}
148168
}
169+
170+
mapSplitTilesMiniTileMask = new short[5000];
171+
mapSplitTilesRegion1 = new short[5000];
172+
mapSplitTilesRegion2 = new short[5000];
173+
for (int i=0; i < 5000; i++) {
174+
mapSplitTilesMiniTileMask[i] = gameData.mapSplitTilesMiniTileMask(i);
175+
mapSplitTilesRegion1[i] = gameData.mapSplitTilesRegion1(i);
176+
mapSplitTilesRegion2[i] = gameData.mapSplitTilesRegion2(i);
177+
}
149178
}
150179

151180
void unitShow(final int id) {
152-
if (!units.containsKey(id)) {
153-
units.put(id, new Unit(gameData.getUnit(id), this));
181+
if (id > units.length) {
182+
//rescale unit array if needed
183+
final Unit[] largerUnitsArray = new Unit[2*units.length];
184+
System.arraycopy(units, 0, largerUnitsArray, 0, units.length);
185+
units = largerUnitsArray;
186+
}
187+
if (units[id] == null) {
188+
units[id] = new Unit(gameData.getUnit(id), this);
154189
}
155190
visibleUnits.add(id);
156191
}
@@ -171,21 +206,28 @@ void addShape(final int type, final int coordType, final int x1, final int y1, f
171206
gameData.addShape(new Client.Shape(type, coordType, x1, y1, x2, y2, extra1, extra2, color, isSolid));
172207
}
173208

209+
174210
public Set<Force> getForces() {
175-
return new HashSet<>(forces.values());
211+
return forceSet;
176212
}
177213

178214
public Set<Player> getPlayers() {
179-
return new HashSet<>(players.values());
215+
return playerSet;
180216
}
181217

182218

183219
public Set<Unit> getAllUnits() {
184220
if (getFrameCount() == 0) {
185-
return new HashSet<>(units.values());
221+
final HashSet<Unit> us = new HashSet<>();
222+
for (final Unit u : units) {
223+
if (u != null) {
224+
us.add(u);
225+
}
226+
}
227+
return us;
186228
}
187229
return visibleUnits.stream()
188-
.map(units::get)
230+
.map(i -> units[i])
189231
.collect(Collectors.toSet());
190232
}
191233

@@ -220,9 +262,13 @@ public Set<Unit> getStaticNeutralUnits() {
220262
}
221263

222264
public Set<Bullet> getBullets() {
223-
return bullets.values().stream()
224-
.filter(Bullet::exists)
225-
.collect(Collectors.toSet());
265+
final Set<Bullet> bs = new HashSet<>();
266+
for (final Bullet bullet : bullets) {
267+
if (bullet.exists()) {
268+
bs.add(bullet);
269+
}
270+
}
271+
return bs;
226272
}
227273

228274
public Set<Position> getNukeDots() {
@@ -233,19 +279,22 @@ public Set<Position> getNukeDots() {
233279

234280

235281
public Force getForce(final int forceID) {
236-
return forces.get(forceID);
282+
return forces[forceID];
237283
}
238284

239285
public Player getPlayer(final int playerID) {
240-
return players.get(playerID);
286+
return players[playerID];
241287
}
242288

243289
public Unit getUnit(final int unitID) {
244-
return units.get(unitID);
290+
if (unitID < 0) {
291+
return null;
292+
}
293+
return units[unitID];
245294
}
246295

247296
public Region getRegion(final int regionID) {
248-
return regions.get(regionID);
297+
return regions[regionID];
249298
}
250299

251300
public GameType getGameType() {
@@ -395,7 +444,7 @@ public int getGroundHeight(final TilePosition position) {
395444
if (!position.isValid(this)) {
396445
return -1;
397446
}
398-
return gameData.groundHeight(position.x, position.y);
447+
return groundHeight[position.x][position.y];
399448
}
400449

401450
public boolean isBuildable(final int tileX, final int tileY) {
@@ -954,7 +1003,7 @@ public boolean issueCommand(final Collection<Unit> units, final UnitCommand comm
9541003

9551004
public Set<Unit> getSelectedUnits() {
9561005
return IntStream.range(0, gameData.selectedUnitCount())
957-
.mapToObj(i -> units.get(gameData.selectedUnit(i)))
1006+
.mapToObj(i -> units[gameData.selectedUnit(i)])
9581007
.collect(Collectors.toSet());
9591008
}
9601009

@@ -1396,15 +1445,34 @@ public int countdownTimer() {
13961445
}
13971446

13981447
public Set<Region> getAllRegions() {
1399-
return new HashSet<>(regions.values());
1448+
return regionSet;
14001449
}
14011450

14021451
public Region getRegionAt(final int x, final int y) {
1403-
return regions.get((int)gameData.mapTileRegionID(x, y));
1452+
return getRegionAt(new Position(x, y));
14041453
}
14051454

14061455
public Region getRegionAt(final Position position) {
1407-
return getRegionAt(position.x, position.y);
1456+
if ( !position.isValid(this)) {
1457+
return null;
1458+
}
1459+
final short idx = mapTileRegionID[position.x/32][position.y/32];
1460+
if ( (idx & 0x2000) != 0) {
1461+
final int minitileShift = ((position.x&0x1F)/8) + ((position.y&0x1F)/8) * 4;
1462+
final int index = idx & 0x1FFF;
1463+
1464+
if (index >= 5000) {
1465+
return null;
1466+
}
1467+
1468+
if (((mapSplitTilesMiniTileMask[index] >> minitileShift) & 1) != 0) {
1469+
return getRegion(mapSplitTilesRegion2[index]);
1470+
}
1471+
else {
1472+
return getRegion(mapSplitTilesRegion1[index]);
1473+
}
1474+
}
1475+
return getRegion(idx);
14081476
}
14091477

14101478

@@ -1416,8 +1484,6 @@ public TilePosition getBuildLocation(final UnitType type, final TilePosition des
14161484
return getBuildLocation(type, desiredPosition, 64);
14171485
}
14181486

1419-
1420-
14211487
public TilePosition getBuildLocation(final UnitType type, TilePosition desiredPosition, final int maxRange, final boolean creep) {
14221488
return BuildingPlacer.getBuildLocation(type, desiredPosition, maxRange, creep, this);
14231489
}

0 commit comments

Comments
 (0)