-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathControlBattlecruisers.cpp
408 lines (347 loc) · 12 KB
/
ControlBattlecruisers.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
#include "BasicSc2Bot.h"
using namespace sc2;
// ------------------ Helper Functions ------------------
// Retreat function for Battlecruisers
void BasicSc2Bot::Retreat(const Unit* unit) {
if (!unit) { // Null check
return;
}
// Retreat location for Battlecruisers
battlecruiser_retreat_location[unit] = retreat_location;
battlecruiser_retreating[unit] = true;
if (Distance2D(unit->pos, retreat_location) >= 5.0f) {
Actions()->UnitCommand(unit, ABILITY_ID::MOVE_MOVE, retreat_location);
}
}
// Calculate the threat level for the Battlecruisers
int BasicSc2Bot::CalculateThreatLevel(const Unit* unit) {
if (!unit) { // Null check
return 0;
}
int threat_level = 0;
// Detect radius for Battlecruisers
const float defense_check_radius = 14.0f;
for (const auto& enemy_unit :
Observation()->GetUnits(Unit::Alliance::Enemy)) {
auto threat = threat_levels.find(enemy_unit->unit_type);
if (threat != threat_levels.end()) {
float distance = Distance2D(unit->pos, enemy_unit->pos);
if (distance < defense_check_radius) {
threat_level += threat->second;
}
}
}
return threat_level;
}
// Get the closest threat to the Battlecruisers
const Unit* BasicSc2Bot::GetClosestThreat(const Unit* unit) {
if (!unit) { // Null check
return nullptr;
}
const Unit* target = nullptr;
constexpr float max_distance = std::numeric_limits<float>::max();
float min_distance = max_distance;
float min_hp = std::numeric_limits<float>::max();
// Find the closest threat to the Battlecruisers
for (const auto& enemy_unit :
Observation()->GetUnits(Unit::Alliance::Enemy)) {
// Ensure the enemy unit is alive
if (!enemy_unit->is_alive) {
continue;
}
auto threat = threat_levels.find(enemy_unit->unit_type);
if (threat != threat_levels.end()) {
float distance = Distance2D(unit->pos, enemy_unit->pos);
if (distance < min_distance ||
(distance == min_distance && enemy_unit->health < min_hp)) {
min_distance = distance;
min_hp = enemy_unit->health;
target = enemy_unit;
}
}
}
return target;
}
// Calculate the kite vector for the Battlecruisers
Point2D BasicSc2Bot::GetKiteVector(const Unit* unit, const Unit* target) {
if (!unit || !target) { // Null check
return Point2D(0.0f, 0.0f);
}
Point2D nearest_corner;
float min_corner_distance = std::numeric_limits<float>::max();
;
// Find nearest corner to the enemy start location
for (const auto& corner : map_corners) {
float corner_distance = DistanceSquared2D(unit->pos, corner);
if (corner_distance < min_corner_distance) {
min_corner_distance = corner_distance;
nearest_corner = corner;
}
}
// Calculate the direction away from the target
Point2D kite_direction = unit->pos - target->pos;
// Normalize the kite direction vector
float kite_length = std::sqrt(kite_direction.x * kite_direction.x +
kite_direction.y * kite_direction.y);
if (kite_length > 0) {
kite_direction /= kite_length;
}
// Define the kiting distance (range of Battlecruiser)
float kiting_distance = 8.0f;
// Calculate the avoidance direction from the enemy vertex
Point2D avoidance_direction = unit->pos - nearest_corner;
float avoidance_length =
std::sqrt(avoidance_direction.x * avoidance_direction.x +
avoidance_direction.y * avoidance_direction.y);
if (avoidance_length > 0) {
avoidance_direction /= avoidance_length;
}
// Define weights for combining directions
// Weight for kiting direction
float kite_weight = 0.3f;
// Weight for avoiding enemy vertex
float avoidance_weight = 0.7f;
// Combine directions to get the final direction
Point2D final_direction = (kite_direction * kite_weight) +
(avoidance_direction * avoidance_weight);
// Normalize the final direction
float final_length = std::sqrt(final_direction.x * final_direction.x +
final_direction.y * final_direction.y);
if (final_length > 0) {
final_direction /= final_length;
}
// Calculate the final kite position
Point2D kite_position = unit->pos + final_direction * kiting_distance;
return kite_position;
}
// ------------------ Main Functions ------------------
// Main function to control Battlecruisers
void BasicSc2Bot::ControlBattlecruisers() {
Jump();
TargetBattlecruisers();
RetreatCheck();
}
// Use Tactical Jump to attack the enemy base
void BasicSc2Bot::Jump() {
const Unit* main_base = GetMainBase();
const ObservationInterface* obs = Observation();
// Check if the main base is under attack; don't use Tactical Jump in that
// case
if (!main_base || EnemyNearby(main_base->pos, true, 25)) {
return;
}
// Check if any Battlecruiser is still retreating
for (const auto& unit : Observation()->GetUnits(Unit::Alliance::Self)) {
if (unit->unit_type == UNIT_TYPEID::TERRAN_BATTLECRUISER &&
battlecruiser_retreating[unit]) {
// Wait until all retreating Battlecruisers finish their retreat
return;
}
}
// No retreating Battlecruisers, proceed with Tactical Jump logic
for (const auto& unit : Observation()->GetUnits(Unit::Alliance::Self)) {
// Check if the unit is a Battlecruiser with full health and not retreating
if (unit->unit_type == UNIT_TYPEID::TERRAN_BATTLECRUISER &&
unit->health >= unit->health_max &&
Distance2D(unit->pos, enemy_start_location) > 40.0f &&
HasAbility(unit, ABILITY_ID::EFFECT_TACTICALJUMP)) {
Actions()->UnitCommand(unit, ABILITY_ID::EFFECT_TACTICALJUMP, enemy_start_location);
}
}
}
// Target mechanics for Battlecruisers
void BasicSc2Bot::TargetBattlecruisers() {
// Maximum distance to consider for targetting
const float max_distace_for_target = 20.0f;
// Get Battlecruisers
const Units battlecruisers = Observation()->GetUnits(
Unit::Alliance::Self, IsUnit(UNIT_TYPEID::TERRAN_BATTLECRUISER));
// Exit when there are no battlecruisers
if (battlecruisers.empty()) {
return;
}
if (current_gameloop % 23 != 0) {
return;
}
// Number of Battlecruisers in combat
int num_battlecruisers_in_combat = UnitsInCombat(UNIT_TYPEID::TERRAN_BATTLECRUISER);
// Threshold for "kiting" behavior
const int threat_threshold = 10 * num_battlecruisers_in_combat;
for (const auto& battlecruiser : battlecruisers) {
// Disables targetting while Jumping
if (!battlecruiser->orders.empty()) {
const AbilityID current_ability =
battlecruiser->orders.front().ability_id;
if (current_ability == ABILITY_ID::EFFECT_TACTICALJUMP) {
continue;
}
}
// Retreat Immediately if the Battlecruiser is below 150 health
if ((battlecruiser->health <= 150.0f)) {
Retreat(battlecruiser);
return;
}
int total_threat = CalculateThreatLevel(battlecruiser);
// Determine whether to retreat based on the threat level
// retreat if the total threat level is above the threshold
if (total_threat >= threat_threshold && (total_threat != 0) &&
(threat_threshold != 0)) {
float health_threshold = 0.0f;
// Extremly Strong anti air units -> Retreat when hp = 400
if (total_threat >= 2.0 * threat_threshold) {
health_threshold = 450.0f;
}
// Strong anti air units -> Retreat when hp = 400
else if (total_threat >= 1.5 * threat_threshold &&
total_threat <= 2.0 * threat_threshold) {
health_threshold = 350.0f;
}
// Moderate anti air units -> Retreat when hp = 200
else if (total_threat > threat_threshold &&
total_threat <= 1.5 * threat_threshold) {
health_threshold = 250.0f;
}
if (battlecruiser->health <= health_threshold) {
Retreat(battlecruiser);
}
else {
const Unit* target = GetClosestThreat(battlecruiser);
// Kite enemy units
if (target) {
// Skip kiting if the target is too far away
if (Distance2D(battlecruiser->pos, target->pos) > 12.0f) {
continue;
}
else {
Actions()->UnitCommand(battlecruiser, ABILITY_ID::MOVE_MOVE, GetKiteVector(battlecruiser, target));
}
}
}
}
// Do not kite if the total threat level is below the threshold
else {
const Unit* target = nullptr;
float min_distance = std::numeric_limits<float>::max();
float min_hp = std::numeric_limits<float>::max();
auto UpdateTarget = [&](const Unit* enemy_unit,
float max_distance) {
float distance =
Distance2D(battlecruiser->pos, enemy_unit->pos);
if (!enemy_unit->is_alive || distance > max_distance) {
return;
}
if (distance < min_distance ||
(distance == min_distance && enemy_unit->health < min_hp)) {
min_distance = distance;
min_hp = enemy_unit->health;
target = enemy_unit;
}
};
// Count turrets
int num_turrets = 0;
for (const auto& enemy_unit :
Observation()->GetUnits(Unit::Alliance::Enemy)) {
if (std::find(turret_types.begin(), turret_types.end(),
enemy_unit->unit_type) != turret_types.end()) {
num_turrets++;
}
}
// Prioritize targets based on rules
auto PrioritizeTargets = [&](const std::vector<UNIT_TYPEID>& types,
float max_distance) {
for (const auto& enemy_unit :
Observation()->GetUnits(Unit::Alliance::Enemy)) {
if (std::find(types.begin(), types.end(),
enemy_unit->unit_type) != types.end()) {
UpdateTarget(enemy_unit, max_distance);
}
}
};
// 1st Priority: Enemy units (excluding turrets based on turret
// conditions)
for (const auto& enemy_unit :
Observation()->GetUnits(Unit::Alliance::Enemy)) {
auto threat = threat_levels.find(enemy_unit->unit_type);
if (threat != threat_levels.end()) {
if (std::find(turret_types.begin(), turret_types.end(),
enemy_unit->unit_type) !=
turret_types.end()) {
// Avoid turrets when conditions apply
if (num_turrets >= 2 * num_battlecruisers_in_combat ||
total_threat - (3 * num_turrets) != 0) {
continue;
}
}
UpdateTarget(enemy_unit, max_distace_for_target);
}
}
if (!target) {
PrioritizeTargets(worker_types, max_distace_for_target);
}
// 3rd Priority -> Turrets
if (!target) {
PrioritizeTargets(turret_types, max_distace_for_target);
}
// 4th Priority -> Any units that are not structures
if (!target) {
for (const auto& enemy_unit :
Observation()->GetUnits(Unit::Alliance::Enemy)) {
const UnitTypeData& unit_type_data =
Observation()->GetUnitTypeData().at(
enemy_unit->unit_type);
if (!std::any_of(unit_type_data.attributes.begin(),
unit_type_data.attributes.end(),
[](Attribute attr) {
return attr == Attribute::Structure;
}) &&
enemy_unit->unit_type != UNIT_TYPEID::ZERG_LARVA &&
enemy_unit->unit_type != UNIT_TYPEID::ZERG_EGG) {
UpdateTarget(enemy_unit, max_distace_for_target);
}
}
}
// 5th Priority -> Supply structures
if (!target) {
PrioritizeTargets(resource_units, max_distace_for_target);
}
// 6th Priority -> Any structures
if (!target) {
for (const auto& enemy_unit :
Observation()->GetUnits(Unit::Alliance::Enemy)) {
const UnitTypeData& unit_type_data =
Observation()->GetUnitTypeData().at(
enemy_unit->unit_type);
if (std::any_of(unit_type_data.attributes.begin(),
unit_type_data.attributes.end(),
[](Attribute attr) {
return attr == Attribute::Structure;
})) {
UpdateTarget(enemy_unit, max_distace_for_target);
}
}
}
// Attack the selected target
if (target && target->NotCloaked) {
// No turret nearby or turret is the target or there are only
// turrets in threat radius -> Attack
Actions()->UnitCommand(battlecruiser, ABILITY_ID::ATTACK_ATTACK,
target);
}
}
}
}
void BasicSc2Bot::RetreatCheck() {
// Distance threshold for arrival
const float arrival_threshold = 5.0f;
for (const auto& battlecruiser : Observation()->GetUnits(
Unit::Alliance::Self, IsUnit(UNIT_TYPEID::TERRAN_BATTLECRUISER))) {
// Check if the Battlecruiser has reached the retreat location
if (battlecruiser_retreating[battlecruiser] &&
Distance2D(battlecruiser->pos, retreat_location) <=
arrival_threshold &&
battlecruiser->health >= 550.0f) {
battlecruiser_retreat_location.erase(battlecruiser);
battlecruiser_retreating[battlecruiser] = false;
}
}
}