Skip to content

Commit 66abc42

Browse files
author
Bytekeeper
committed
Added support for carriers/interceptors, dark swarm and priority targeting.
1 parent 668a705 commit 66abc42

File tree

10 files changed

+173
-36
lines changed

10 files changed

+173
-36
lines changed

src/main/java/org/bk/ass/Agent.java

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package org.bk.ass;
22

3+
import java.util.ArrayList;
34
import java.util.Collection;
4-
import java.util.function.Consumer;
5+
import java.util.Collections;
6+
import java.util.List;
7+
import java.util.function.BiConsumer;
58

69
import static java.lang.Math.max;
710

811
public class Agent {
12+
static final BiConsumer<Agent, Collection<Agent>> CARRIER_DEATH_HANDLER =
13+
(carrier, agents) -> agents.removeAll(carrier.interceptors);
914

1015
private final String name;
16+
TargetingPriority attackTargetPriority = TargetingPriority.HIGHEST;
1117
int armorShifted;
1218
int shieldUpgrades;
1319
Object userObject;
@@ -52,6 +58,7 @@ public class Agent {
5258
boolean isMechanic;
5359
boolean isKiter;
5460
boolean isRepairer;
61+
boolean protectedByDarkSwarm;
5562
boolean burrowed;
5663
boolean burrowedAttacker;
5764
// Visible to the other force
@@ -66,11 +73,14 @@ public class Agent {
6673
Weapon airWeapon;
6774
Weapon groundWeapon;
6875

69-
Agent lastEnemy;
70-
Agent lastAlly;
76+
Agent attackTarget;
77+
// Target for healing/repairing
78+
Agent restoreTarget;
79+
80+
List<Agent> interceptors = Collections.emptyList();
7181

7282
// Allow replacement of units on death (for example bunker -> marines)
73-
Consumer<Collection<Agent>> onDeathReplacer = ignored -> {};
83+
BiConsumer<Agent, Collection<Agent>> onDeathHandler = (ignored1, ignored2) -> {};
7484

7585
public Agent(String name) {
7686
this.name = name;
@@ -262,8 +272,8 @@ public Agent setSize(UnitSize size) {
262272
return this;
263273
}
264274

265-
public Agent setOnDeathReplacer(Consumer<Collection<Agent>> onDeathReplacer) {
266-
this.onDeathReplacer = onDeathReplacer;
275+
public Agent setOnDeathHandler(BiConsumer<Agent, Collection<Agent>> onDeathHandler) {
276+
this.onDeathHandler = onDeathHandler;
267277
return this;
268278
}
269279

@@ -282,6 +292,37 @@ public Agent setGroundWeapon(Weapon groundWeapon) {
282292
return this;
283293
}
284294

295+
/**
296+
* Sets the agents target to attack. Might be overridden by behavior. Ie. if the target is out of
297+
* range in a simulation frame.
298+
*/
299+
public void setAttackTarget(Agent attackTarget) {
300+
this.attackTarget = attackTarget;
301+
}
302+
303+
/** Sets agents that are linked to this one (ie. Carriers<->Interceptors). */
304+
public void setInterceptors(Collection<Agent> childAgents) {
305+
this.interceptors = new ArrayList<>(childAgents);
306+
}
307+
308+
/**
309+
* Change the priority of this agent when other agents try to find a new target. For best
310+
* performance keep most agents on {@link TargetingPriority#HIGHEST}.
311+
*
312+
* <p>Interceptors are assigned a low priority by default, the rest defaults to {@link
313+
* TargetingPriority#HIGHEST}.
314+
*/
315+
public Agent setAttackTargetPriority(TargetingPriority attackTargetPriority) {
316+
this.attackTargetPriority = attackTargetPriority;
317+
return this;
318+
}
319+
320+
/** Mark this agent as being protected by dark swarm (ground unit and under dark swarm). */
321+
public Agent setProtectedByDarkSwarm(boolean protectedByDarkSwarm) {
322+
this.protectedByDarkSwarm = protectedByDarkSwarm;
323+
return this;
324+
}
325+
285326
final Weapon weaponVs(Agent other) {
286327
if (other.isFlyer) {
287328
return airWeapon;
@@ -295,4 +336,10 @@ public Agent setHpConstructionRate(int buildTime) {
295336
max(1, (maxHealthShifted - maxHealthShifted / 10 + buildTime - 1) / buildTime);
296337
return this;
297338
}
339+
340+
public enum TargetingPriority {
341+
LOW,
342+
MEDIUM,
343+
HIGHEST // Intentionally named "highest" as it also speeds up target selection
344+
}
298345
}

src/main/java/org/bk/ass/AgentUtil.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ public static void dealDamage(Agent agent, Weapon wpn, Agent target) {
139139
int remainingDamage = wpn.damageShifted;
140140

141141
if (!agent.isMelee) {
142+
// https://liquipedia.net/starcraft/Dark_Swarm
143+
if (target.protectedByDarkSwarm) return;
144+
142145
// http://www.starcraftai.com/wiki/Chance_to_Hit
143146
if ((agent.elevationLevel >= 0 && agent.elevationLevel < target.elevationLevel)
144147
|| (target.elevationLevel & 1) == 1) {

src/main/java/org/bk/ass/AttackerBehavior.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ public boolean simUnit(
2525
Weapon selectedWeapon = null;
2626
int selectedDistanceSquared = Integer.MAX_VALUE;
2727

28-
if (agent.lastEnemy != null && agent.lastEnemy.healthShifted >= 0) {
29-
int dstSq = distanceSquared(agent, agent.lastEnemy);
30-
selectedWeapon = agent.weaponVs(agent.lastEnemy);
28+
if (agent.attackTarget != null && agent.attackTarget.healthShifted >= 0) {
29+
int dstSq = distanceSquared(agent, agent.attackTarget);
30+
selectedWeapon = agent.weaponVs(agent.attackTarget);
3131
if (dstSq >= selectedWeapon.minRangeSquared && dstSq <= selectedWeapon.maxRangeSquared) {
32-
selectedEnemy = agent.lastEnemy;
32+
selectedEnemy = agent.attackTarget;
3333
selectedDistanceSquared = dstSq;
3434
}
3535
}
@@ -38,25 +38,32 @@ public boolean simUnit(
3838
for (int i = enemies.size() - 1; i >= 0; i--) {
3939
Agent enemy = enemies.get(i);
4040
Weapon wpn = agent.weaponVs(enemy);
41+
int prioCmp =
42+
selectedEnemy == null
43+
? 1
44+
: enemy.attackTargetPriority.compareTo(selectedEnemy.attackTargetPriority);
4145
if (enemy.healthShifted >= 1
4246
&& wpn.damageShifted != 0
4347
&& enemy.detected
44-
&& !enemy.isStasised) {
48+
&& !enemy.isStasised
49+
&& prioCmp >= 0) {
4550
int distanceSquared = distanceSquared(agent, enemy);
46-
if (distanceSquared >= wpn.minRangeSquared && distanceSquared < selectedDistanceSquared) {
51+
if (distanceSquared >= wpn.minRangeSquared
52+
&& (distanceSquared < selectedDistanceSquared || prioCmp > 0)) {
4753
selectedDistanceSquared = distanceSquared;
4854
selectedEnemy = enemy;
4955
selectedWeapon = wpn;
5056

5157
// If we can hit it this frame, we're done searching
52-
if (selectedDistanceSquared <= wpn.maxRangeSquared) {
58+
if (selectedDistanceSquared <= wpn.maxRangeSquared
59+
&& enemy.attackTargetPriority == Agent.TargetingPriority.HIGHEST) {
5360
break;
5461
}
5562
}
5663
}
5764
}
5865
}
59-
agent.lastEnemy = selectedEnemy;
66+
agent.attackTarget = selectedEnemy;
6067

6168
if (selectedEnemy == null) {
6269
return !agent.burrowed && simFlee(agent, enemies);

src/main/java/org/bk/ass/BWAPI4JAgentFactory.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88

99
import java.util.Collection;
1010
import java.util.EnumSet;
11-
import java.util.function.Consumer;
11+
import java.util.function.BiConsumer;
1212

1313
import static java.lang.Math.max;
14+
import static org.bk.ass.Agent.CARRIER_DEATH_HANDLER;
1415

1516
public class BWAPI4JAgentFactory {
1617

@@ -25,8 +26,8 @@ public class BWAPI4JAgentFactory {
2526
UnitType.Terran_Marine, UnitType.Terran_Vulture,
2627
UnitType.Zerg_Mutalisk, UnitType.Protoss_Dragoon);
2728

28-
private Consumer<Collection<Agent>> bunkerReplacer =
29-
agents -> {
29+
private BiConsumer<Agent, Collection<Agent>> bunkerDeathHandler =
30+
(bunker, agents) -> {
3031
agents.add(of(UnitType.Terran_Marine));
3132
agents.add(of(UnitType.Terran_Marine));
3233
agents.add(of(UnitType.Terran_Marine));
@@ -104,10 +105,17 @@ private Agent fromUnitType(
104105

105106
Agent agent =
106107
new Agent(unitType.name())
108+
.setAttackTargetPriority(
109+
unitType == UnitType.Protoss_Interceptor
110+
? Agent.TargetingPriority.LOW
111+
: Agent.TargetingPriority.HIGHEST)
107112
.setFlyer(unitType.isFlyer())
108113
.setHealer(unitType == UnitType.Terran_Medic)
109114
.setMaxHealth(unitType.maxHitPoints())
110-
.setMaxCooldown(max(groundWeapon.damageCooldown(), airWeapon.damageCooldown()))
115+
.setMaxCooldown(
116+
unitType != UnitType.Protoss_Interceptor
117+
? max(groundWeapon.damageCooldown(), airWeapon.damageCooldown())
118+
: 45)
111119
.setAirWeapon(
112120
weapon(
113121
airWeaponUpgrades,
@@ -144,7 +152,9 @@ private Agent fromUnitType(
144152
.setMelee(groundWeapon.damageAmount() > 0 && groundWeapon.maxRange() <= 32);
145153

146154
if (unitType == UnitType.Terran_Bunker) {
147-
agent.setOnDeathReplacer(bunkerReplacer);
155+
agent.setOnDeathHandler(bunkerDeathHandler);
156+
} else if (unitType == UnitType.Protoss_Carrier) {
157+
agent.setOnDeathHandler(CARRIER_DEATH_HANDLER);
148158
}
149159
return agent;
150160
}
@@ -220,6 +230,7 @@ public Agent of(PlayerUnit unit) {
220230
hasEnergyUpgrade(unitType, player));
221231
if (map != null && !unit.isFlying()) {
222232
agent.setElevationLevel(map.getGroundHeight(unit.getTilePosition()));
233+
// agent.setProtectedByDarkSwarm(unit.isUnderDarkSwarm()); Not supported by BWAPI4J currently
223234
}
224235
if (unitType == UnitType.Terran_Marine || unitType == UnitType.Terran_Firebat) {
225236
agent.setCanStim(player.hasResearched(TechType.Stim_Packs));

src/main/java/org/bk/ass/BWMirrorAgentFactory.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
import java.util.Collection;
77
import java.util.EnumSet;
88
import java.util.Set;
9-
import java.util.function.Consumer;
9+
import java.util.function.BiConsumer;
1010

1111
import static java.lang.Math.max;
12+
import static org.bk.ass.Agent.CARRIER_DEATH_HANDLER;
1213

1314
/**
1415
* Be aware that for fogged units, BWMirror returns invalid coordinates. You'll have to adjust for
@@ -29,8 +30,8 @@ public class BWMirrorAgentFactory {
2930
UnitType.Zerg_Mutalisk,
3031
UnitType.Protoss_Dragoon);
3132

32-
private Consumer<Collection<Agent>> bunkerReplacer =
33-
agents -> {
33+
private BiConsumer<Agent, Collection<Agent>> bunkerReplacer =
34+
(bunker, agents) -> {
3435
agents.add(of(UnitType.Terran_Marine));
3536
agents.add(of(UnitType.Terran_Marine));
3637
agents.add(of(UnitType.Terran_Marine));
@@ -108,10 +109,17 @@ private Agent fromUnitType(
108109

109110
Agent agent =
110111
new Agent(unitType.toString())
112+
.setAttackTargetPriority(
113+
unitType == UnitType.Protoss_Interceptor
114+
? Agent.TargetingPriority.LOW
115+
: Agent.TargetingPriority.HIGHEST)
111116
.setFlyer(unitType.isFlyer())
112117
.setHealer(unitType == UnitType.Terran_Medic)
113118
.setMaxHealth(unitType.maxHitPoints())
114-
.setMaxCooldown(max(groundWeapon.damageCooldown(), airWeapon.damageCooldown()))
119+
.setMaxCooldown(
120+
unitType != UnitType.Protoss_Interceptor
121+
? max(groundWeapon.damageCooldown(), airWeapon.damageCooldown())
122+
: 45)
115123
.setAirWeapon(
116124
weapon(
117125
airWeaponUpgrades,
@@ -148,7 +156,9 @@ private Agent fromUnitType(
148156
.setMelee(groundWeapon.damageAmount() > 0 && groundWeapon.maxRange() <= 32);
149157

150158
if (unitType == UnitType.Terran_Bunker) {
151-
agent.setOnDeathReplacer(bunkerReplacer);
159+
agent.setOnDeathHandler(bunkerReplacer);
160+
} else if (unitType == UnitType.Protoss_Carrier) {
161+
agent.setOnDeathHandler(CARRIER_DEATH_HANDLER);
152162
}
153163
return agent;
154164
}
@@ -225,6 +235,7 @@ public Agent of(Unit unit) {
225235
hasEnergyUpgrade(unitType, player));
226236
if (game != null && !unit.isFlying()) {
227237
agent.setElevationLevel(game.getGroundHeight(unit.getTilePosition()));
238+
agent.setProtectedByDarkSwarm(unit.isUnderDarkSwarm());
228239
}
229240
if (unitType == UnitType.Terran_Marine || unitType == UnitType.Terran_Firebat) {
230241
agent.setCanStim(player.hasResearched(TechType.Stim_Packs));

src/main/java/org/bk/ass/Evaluator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ public Evaluator() {
3030
*/
3131
public double evaluate(Collection<Agent> agentsA, Collection<Agent> agentsB) {
3232
List<Agent> finalAgentsA = new ArrayList<>();
33-
agentsA.forEach(a -> a.onDeathReplacer.accept(finalAgentsA));
33+
agentsA.forEach(a -> a.onDeathHandler.accept(a, finalAgentsA));
3434
List<Agent> finalAgentsB = new ArrayList<>();
35-
agentsB.forEach(a -> a.onDeathReplacer.accept(finalAgentsB));
35+
agentsB.forEach(a -> a.onDeathHandler.accept(a, finalAgentsB));
3636
finalAgentsA.addAll(agentsA);
3737
finalAgentsB.addAll(agentsB);
3838
int damageToA = new DamageBoard(finalAgentsB).sumDamageTo(finalAgentsA);

src/main/java/org/bk/ass/HealerBehavior.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ public boolean simUnit(
2020
Agent selectedAlly = null;
2121
int selectedDistanceSquared = Integer.MAX_VALUE;
2222

23-
if (agent.lastAlly != null
24-
&& !agent.lastAlly.healedThisFrame
23+
if (agent.restoreTarget != null
24+
&& !agent.restoreTarget.healedThisFrame
2525
&& agent.healthShifted < agent.maxHealthShifted) {
26-
int dstSq = distanceSquared(agent, agent.lastAlly);
26+
int dstSq = distanceSquared(agent, agent.restoreTarget);
2727
if (dstSq <= MEDICS_HEAL_RANGE_SQUARED) {
28-
selectedAlly = agent.lastAlly;
28+
selectedAlly = agent.restoreTarget;
2929
selectedDistanceSquared = dstSq;
3030
}
3131
}
@@ -52,7 +52,7 @@ public boolean simUnit(
5252
}
5353
}
5454
}
55-
agent.lastAlly = selectedAlly;
55+
agent.restoreTarget = selectedAlly;
5656

5757
if (selectedAlly == null) {
5858
return false;

src/main/java/org/bk/ass/RepairerBehavior.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ public boolean simUnit(
1717
Agent selectedAlly = null;
1818
int selectedDistanceSquared = Integer.MAX_VALUE;
1919

20-
if (agent.lastAlly != null && agent.healthShifted < agent.maxHealthShifted) {
21-
int dstSq = distanceSquared(agent, agent.lastAlly);
20+
if (agent.restoreTarget != null && agent.healthShifted < agent.maxHealthShifted) {
21+
int dstSq = distanceSquared(agent, agent.restoreTarget);
2222
if (dstSq <= SCV_REPAIR_RANGE_SQUARED) {
23-
selectedAlly = agent.lastAlly;
23+
selectedAlly = agent.restoreTarget;
2424
selectedDistanceSquared = dstSq;
2525
}
2626
}
@@ -46,7 +46,7 @@ public boolean simUnit(
4646
}
4747
}
4848
}
49-
agent.lastAlly = selectedAlly;
49+
agent.restoreTarget = selectedAlly;
5050

5151
if (selectedAlly == null) {
5252
return false;

src/main/java/org/bk/ass/Simulator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ private void removeDead(UnorderedCollection<Agent> agents) {
170170
if (agents.get(i).healthShifted < 1) {
171171
Agent agent = agents.removeAt(i);
172172
if (!agent.isFlyer) collision[colindex(agent.x, agent.y)]--;
173-
agent.onDeathReplacer.accept(agents);
173+
agent.onDeathHandler.accept(agent, agents);
174174
} else {
175175
i++;
176176
}

0 commit comments

Comments
 (0)