1 // -------------------------------------------------------------------------
2 // AAI
3 //
4 // A skirmish AI for the Spring engine.
5 // Copyright Alexander Seizinger
6 //
7 // Released under GPL license: see LICENSE.html for more information.
8 // -------------------------------------------------------------------------
9
10 #include "AAI.h"
11 #include "AAIBrain.h"
12 #include "AAIBuildTable.h"
13 #include "AAIExecute.h"
14 #include "AAIUnitTable.h"
15 #include "AAIConfig.h"
16 #include "AAIMap.h"
17 #include "AAIGroup.h"
18 #include "AAISector.h"
19
20 #include "LegacyCpp/UnitDef.h"
21 using namespace springLegacyAI;
22
AAIBrain(AAI * ai)23 AAIBrain::AAIBrain(AAI *ai)
24 {
25 this->ai = ai;
26 freeBaseSpots = false;
27 expandable = true;
28
29 max_distance = ai->Getmap()->xSectors + ai->Getmap()->ySectors - 2;
30 sectors.resize(max_distance);
31
32 base_center = ZeroVector;
33
34 max_combat_units_spotted.resize(AAIBuildTable::ass_categories, 0);
35 attacked_by.resize(AAIBuildTable::combat_categories, 0);
36 defence_power_vs.resize(AAIBuildTable::ass_categories, 0);
37
38 enemy_pressure_estimation = 0;
39 }
40
~AAIBrain(void)41 AAIBrain::~AAIBrain(void)
42 {
43 }
44
45
GetAttackDest(bool land,bool water)46 AAISector* AAIBrain::GetAttackDest(bool land, bool water)
47 {
48 float best_rating = 0.0f, my_rating = 0.0f;
49 AAISector *dest = 0, *sector;
50
51 //int side = ai->Getside()-1;
52
53 float ground = 1.0f;
54 float air = 1.0f;
55 float hover = 1.0f;
56 float sea = 1.0f;
57 float submarine = 1.0f;
58
59 float def_power;
60
61 // TODO: improve destination sector selection
62 for(int x = 0; x < ai->Getmap()->xSectors; ++x)
63 {
64 for(int y = 0; y < ai->Getmap()->ySectors; ++y)
65 {
66 sector = &ai->Getmap()->sector[x][y];
67
68 if(sector->distance_to_base == 0 || sector->enemy_structures == 0)
69 my_rating = 0;
70 else
71 {
72 if(land && sector->water_ratio < 0.4)
73 {
74 def_power = sector->GetEnemyDefencePower(ground, air, hover, sea, submarine);
75
76 if(def_power)
77 my_rating = sector->enemy_structures / sector->GetEnemyDefencePower(ground, air, hover, sea, submarine);
78
79 my_rating = sector->enemy_structures / (2 * sector->GetEnemyDefencePower(ground, air, hover, sea, submarine) + pow(sector->GetLostUnits(ground, air, hover, sea, submarine) + 1, 1.5f) + 1);
80 my_rating /= (8 + sector->distance_to_base);
81 }
82 else if(water && sector->water_ratio > 0.6)
83 {
84 my_rating = sector->enemy_structures / (2 * sector->GetEnemyDefencePower(ground, air, hover, sea, submarine) + pow(sector->GetLostUnits(ground, air, hover, sea, submarine) + 1, 1.5f) + 1);
85 my_rating /= (8 + sector->distance_to_base);
86 }
87 }
88
89 if(my_rating > best_rating)
90 {
91 dest = sector;
92 best_rating = my_rating;
93 }
94 }
95 }
96
97 return dest;
98 }
99
GetNextAttackDest(AAISector * current_sector,bool land,bool water)100 AAISector* AAIBrain::GetNextAttackDest(AAISector *current_sector, bool land, bool water)
101 {
102 float best_rating = 0, my_rating, dist;
103 AAISector *dest = 0, *sector;
104
105 //int side = ai->Getside()-1;
106
107 float ground = 1.0f;
108 float air = 1.0f;
109 float hover = 1.0f;
110 float sea = 1.0f;
111 float submarine = 1.0f;
112
113 // TODO: improve destination sector selection
114 for(int x = 0; x < ai->Getmap()->xSectors; x++)
115 {
116 for(int y = 0; y < ai->Getmap()->ySectors; y++)
117 {
118 sector = &ai->Getmap()->sector[x][y];
119
120 if(sector->distance_to_base == 0 || sector->enemy_structures < 0.001f)
121 my_rating = 0;
122 else
123 {
124 if(land && sector->water_ratio < 0.35)
125 {
126 dist = sqrt( pow((float)sector->x - current_sector->x, 2) + pow((float)sector->y - current_sector->y , 2) );
127
128 my_rating = 1.0f / (1.0f + pow(sector->GetEnemyDefencePower(ground, air, hover, sea, submarine), 2.0f) + pow(sector->GetLostUnits(ground, air, hover, sea, submarine) + 1, 1.5f));
129
130 }
131 else if(water && sector->water_ratio > 0.65)
132 {
133 dist = sqrt( pow((float)(sector->x - current_sector->x), 2) + pow((float)(sector->y - current_sector->y), 2) );
134
135 my_rating = 1.0f / (1.0f + pow(sector->GetEnemyDefencePower(ground, air, hover, sea, submarine), 2.0f) + pow(sector->GetLostUnits(ground, air, hover, sea, submarine) + 1, 1.5f));
136 my_rating /= (1.0f + dist);
137 }
138 else
139 my_rating = 0;
140 }
141
142 if(my_rating > best_rating)
143 {
144 dest = sector;
145 best_rating = my_rating;
146 }
147 }
148 }
149
150 return dest;
151 }
152
GetNewScoutDest(float3 * dest,int scout)153 void AAIBrain::GetNewScoutDest(float3 *dest, int scout)
154 {
155 *dest = ZeroVector;
156
157 // TODO: take scouts pos into account
158 float my_rating, best_rating = 0;
159 AAISector *scout_sector = 0, *sector;
160
161 const UnitDef *def = ai->Getcb()->GetUnitDef(scout);
162 unsigned int scout_movement_type = ai->Getbt()->units_static[def->id].movement_type;
163
164 float3 pos = ai->Getcb()->GetUnitPos(scout);
165
166 // get continent
167 int continent = ai->Getmap()->GetSmartContinentID(&pos, scout_movement_type);//ai->Getmap()->GetContinentID(&pos);
168
169 for(int x = 0; x < ai->Getmap()->xSectors; ++x)
170 {
171 for(int y = 0; y < ai->Getmap()->ySectors; ++y)
172 {
173 sector = &ai->Getmap()->sector[x][y];
174
175 if(sector->distance_to_base > 0 && scout_movement_type & sector->allowed_movement_types)
176 {
177 if(enemy_pressure_estimation > 0.01f && sector->distance_to_base < 2)
178 my_rating = sector->importance_this_game * sector->last_scout * (1.0f + sector->enemy_combat_units[5]);
179 else
180 my_rating = sector->importance_this_game * sector->last_scout;
181
182 ++sector->last_scout;
183
184 if(my_rating > best_rating)
185 {
186 // possible scout dest, try to find pos in sector
187 pos = ZeroVector;
188
189 sector->GetMovePosOnContinent(&pos, scout_movement_type, continent);
190
191 if(pos.x > 0)
192 {
193 best_rating = my_rating;
194 scout_sector = sector;
195 *dest = pos;
196 }
197 }
198 }
199 }
200 }
201
202 // set dest sector as visited
203 if(dest->x > 0)
204 scout_sector->last_scout = 1;
205 }
206
MetalForConstr(int unit,int workertime)207 bool AAIBrain::MetalForConstr(int unit, int workertime)
208 {
209
210 int metal = (ai->Getbt()->GetUnitDef(unit).buildTime/workertime) * (ai->Getcb()->GetMetalIncome()-(ai->Getcb()->GetMetalUsage()) + ai->Getcb()->GetMetal());
211 int total_cost = ai->Getbt()->GetUnitDef(unit).metalCost;
212
213 if(metal > total_cost)
214 return true;
215
216 return false;
217 }
218
EnergyForConstr(int unit,int)219 bool AAIBrain::EnergyForConstr(int unit, int /*wokertime*/)
220 {
221
222 // check energy
223 // int energy = ai->Getbt()->unitList[unit-1]->buildTime * (ai->Getcb()->GetEnergyIncome()-(ai->Getcb()->GetEnergyUsage()/2));
224
225 // TODO: FIXME: add code here
226
227 return true;
228 }
229
RessourcesForConstr(int,int)230 bool AAIBrain::RessourcesForConstr(int /*unit*/, int /*wokertime*/)
231 {
232 // check metal and energy
233 /*if(MetalForConstr(unit) && EnergyForConstr(unit))
234 return true;
235
236 return false;*/
237 return true;
238 }
239
AddSector(AAISector * sector)240 void AAIBrain::AddSector(AAISector *sector)
241 {
242 sectors[0].push_back(sector);
243
244 sector->SetBase(true);
245
246 // update base land/water ratio
247 baseLandRatio = 0;
248 baseWaterRatio = 0;
249
250 for(list<AAISector*>::iterator s = sectors[0].begin(); s != sectors[0].end(); ++s)
251 {
252 baseLandRatio += (*s)->GetFlatRatio();
253 baseWaterRatio += (*s)->GetWaterRatio();
254 }
255
256 baseLandRatio /= (float)sectors[0].size();
257 baseWaterRatio /= (float)sectors[0].size();
258 }
259
RemoveSector(AAISector * sector)260 void AAIBrain::RemoveSector(AAISector *sector)
261 {
262 sectors[0].remove(sector);
263
264 sector->SetBase(false);
265
266 // update base land/water ratio
267 baseLandRatio = 0;
268 baseWaterRatio = 0;
269
270 if(sectors[0].size() > 0)
271 {
272 for(list<AAISector*>::iterator s = sectors[0].begin(); s != sectors[0].end(); ++s)
273 {
274 baseLandRatio += (*s)->GetFlatRatio();
275 baseWaterRatio += (*s)->GetWaterRatio();
276 }
277
278 baseLandRatio /= (float)sectors[0].size();
279 baseWaterRatio /= (float)sectors[0].size();
280 }
281 }
282
283
DefendCommander(int)284 void AAIBrain::DefendCommander(int /*attacker*/)
285 {
286 // float3 pos = ai->Getcb()->GetUnitPos(ai->Getut()->cmdr);
287 //float importance = 120;
288 Command c;
289
290 // evacuate cmdr
291 // TODO: FIXME: check/fix?/implement me?
292 /*if(ai->cmdr->task != BUILDING)
293 {
294 AAISector *sector = GetSafestSector();
295
296 if(sector != 0)
297 {
298 pos = sector->GetCenter();
299
300 if(pos.x > 0 && pos.z > 0)
301 {
302 pos.y = ai->Getcb()->GetElevation(pos.x, pos.z);
303 ai->Getexecute()->MoveUnitTo(ai->cmdr->unit_id, &pos);
304 }
305 }
306 }*/
307 }
308
UpdateBaseCenter()309 void AAIBrain::UpdateBaseCenter()
310 {
311 base_center = ZeroVector;
312
313 for(list<AAISector*>::iterator sector = sectors[0].begin(); sector != sectors[0].end(); ++sector)
314 {
315 base_center.x += (0.5 + (*sector)->x) * ai->Getmap()->xSectorSize;
316 base_center.z += (0.5 + (*sector)->y) * ai->Getmap()->ySectorSize;
317 }
318
319 base_center.x /= sectors[0].size();
320 base_center.z /= sectors[0].size();
321 }
322
UpdateNeighbouringSectors()323 void AAIBrain::UpdateNeighbouringSectors()
324 {
325 int x,y,neighbours;
326
327 // delete old values
328 for(x = 0; x < ai->Getmap()->xSectors; ++x)
329 {
330 for(y = 0; y < ai->Getmap()->ySectors; ++y)
331 {
332 if(ai->Getmap()->sector[x][y].distance_to_base > 0)
333 ai->Getmap()->sector[x][y].distance_to_base = -1;
334 }
335 }
336
337 for(int i = 1; i < max_distance; ++i)
338 {
339 // delete old sectors
340 sectors[i].clear();
341 neighbours = 0;
342
343 for(list<AAISector*>::iterator sector = sectors[i-1].begin(); sector != sectors[i-1].end(); ++sector)
344 {
345 x = (*sector)->x;
346 y = (*sector)->y;
347
348 // check left neighbour
349 if(x > 0 && ai->Getmap()->sector[x-1][y].distance_to_base == -1)
350 {
351 ai->Getmap()->sector[x-1][y].distance_to_base = i;
352 sectors[i].push_back(&ai->Getmap()->sector[x-1][y]);
353 ++neighbours;
354 }
355 // check right neighbour
356 if(x < (ai->Getmap()->xSectors - 1) && ai->Getmap()->sector[x+1][y].distance_to_base == -1)
357 {
358 ai->Getmap()->sector[x+1][y].distance_to_base = i;
359 sectors[i].push_back(&ai->Getmap()->sector[x+1][y]);
360 ++neighbours;
361 }
362 // check upper neighbour
363 if(y > 0 && ai->Getmap()->sector[x][y-1].distance_to_base == -1)
364 {
365 ai->Getmap()->sector[x][y-1].distance_to_base = i;
366 sectors[i].push_back(&ai->Getmap()->sector[x][y-1]);
367 ++neighbours;
368 }
369 // check lower neighbour
370 if(y < (ai->Getmap()->ySectors - 1) && ai->Getmap()->sector[x][y+1].distance_to_base == -1)
371 {
372 ai->Getmap()->sector[x][y+1].distance_to_base = i;
373 sectors[i].push_back(&ai->Getmap()->sector[x][y+1]);
374 ++neighbours;
375 }
376
377 if(i == 1 && !neighbours)
378 (*sector)->interior = true;
379 }
380 }
381
382 //ai->Log("Base has now %i direct neighbouring sectors\n", sectors[1].size());
383 }
384
SectorInList(list<AAISector * > mylist,AAISector * sector)385 bool AAIBrain::SectorInList(list<AAISector*> mylist, AAISector *sector)
386 {
387 // check if sector already added to list
388 for(list<AAISector*>::iterator t = mylist.begin(); t != mylist.end(); ++t)
389 {
390 if(*t == sector)
391 return true;
392 }
393 return false;
394 }
395
GetBaseBuildspaceRatio(unsigned int building_move_type)396 float AAIBrain::GetBaseBuildspaceRatio(unsigned int building_move_type)
397 {
398 if(building_move_type & MOVE_TYPE_STATIC_LAND)
399 {
400 if(baseLandRatio > 0.1f)
401 return baseLandRatio;
402 else
403 return 0;
404 }
405 else if(building_move_type & MOVE_TYPE_STATIC_WATER)
406 {
407 if(baseWaterRatio > 0.1f)
408 return baseWaterRatio;
409 else
410 return 0;
411 }
412 else
413 return 1.0f;
414 }
415
CommanderAllowedForConstructionAt(AAISector * sector,float3 * pos)416 bool AAIBrain::CommanderAllowedForConstructionAt(AAISector *sector, float3 *pos)
417 {
418 // commander is always allowed in base
419 if(sector->distance_to_base <= 0)
420 return true;
421 // allow construction close to base for small bases
422 else if(sectors[0].size() < 3 && sector->distance_to_base <= 1)
423 return true;
424 // allow construction on islands close to base on water maps
425 else if(ai->Getmap()->map_type == WATER_MAP && ai->Getcb()->GetElevation(pos->x, pos->z) >= 0 && sector->distance_to_base <= 3)
426 return true;
427 else
428 return false;
429 }
430
MexConstructionAllowedInSector(AAISector * sector)431 bool AAIBrain::MexConstructionAllowedInSector(AAISector *sector)
432 {
433 return sector->freeMetalSpots && IsSafeSector(sector) && (ai->Getmap()->team_sector_map[sector->x][sector->y] == -1 || ai->Getmap()->team_sector_map[sector->x][sector->y] == ai->Getcb()->GetMyAllyTeam());
434 }
435
ExpandBase(SectorType sectorType)436 bool AAIBrain::ExpandBase(SectorType sectorType)
437 {
438 if(sectors[0].size() >= cfg->MAX_BASE_SIZE)
439 return false;
440
441 // now targets should contain all neighbouring sectors that are not currently part of the base
442 // only once; select the sector with most metalspots and least danger
443 AAISector *best_sector = NULL;
444 float best_rating = 0, my_rating;
445 int spots;
446 float dist;
447
448 int max_search_dist = 1;
449
450 // if aai is looking for a water sector to expand into ocean, allow greater search_dist
451 if(sectorType == WATER_SECTOR && baseWaterRatio < 0.1)
452 max_search_dist = 3;
453
454 for(int search_dist = 1; search_dist <= max_search_dist; ++search_dist)
455 {
456 for(list<AAISector*>::iterator t = sectors[search_dist].begin(); t != sectors[search_dist].end(); ++t)
457 {
458 // dont expand if enemy structures in sector && check for allied buildings
459 if(IsSafeSector(*t) && (*t)->allied_structures < 3 && ai->Getmap()->team_sector_map[(*t)->x][(*t)->y] == -1)
460 {
461 // rate current sector
462 spots = (*t)->GetNumberOfMetalSpots();
463
464 my_rating = 1.0f + (float)spots;
465
466 if(sectorType == LAND_SECTOR)
467 // prefer flat sectors without water
468 my_rating += ((*t)->flat_ratio - (*t)->water_ratio) * 16.0f;
469 else if(sectorType == WATER_SECTOR)
470 {
471 // check for continent size (to prevent aai to expand into little ponds instead of big ocean)
472 if((*t)->water_ratio > 0.1 && (*t)->ConnectedToOcean())
473 my_rating += 8.0f * (*t)->water_ratio;
474 else
475 my_rating = 0;
476 }
477 else // LAND_WATER_SECTOR
478 my_rating += ((*t)->flat_ratio + (*t)->water_ratio) * 8.0f;
479
480 // minmize distance between sectors
481 dist = 0.1f;
482
483 for(list<AAISector*>::iterator sector = sectors[0].begin(); sector != sectors[0].end(); ++sector) {
484 dist += fastmath::apxsqrt( ((*t)->x - (*sector)->x) * ((*t)->x - (*sector)->x) + ((*t)->y - (*sector)->y) * ((*t)->y - (*sector)->y) );
485 }
486
487 float3 center = (*t)->GetCenter();
488 my_rating /= (dist * fastmath::apxsqrt(ai->Getmap()->GetEdgeDistance( ¢er )) );
489
490 // choose higher rated sector
491 if(my_rating > best_rating)
492 {
493 best_rating = my_rating;
494 best_sector = *t;
495 }
496 }
497 }
498 }
499
500 if(best_sector)
501 {
502 // add this sector to base
503 AddSector(best_sector);
504
505 // debug purposes:
506 if(sectorType == LAND_SECTOR)
507 {
508 ai->Log("\nAdding land sector %i,%i to base; base size: " _STPF_, best_sector->x, best_sector->y, sectors[0].size());
509 ai->Log("\nNew land : water ratio within base: %f : %f\n\n", baseLandRatio, baseWaterRatio);
510 }
511 else
512 {
513 ai->Log("\nAdding water sector %i,%i to base; base size: " _STPF_, best_sector->x, best_sector->y, sectors[0].size());
514 ai->Log("\nNew land : water ratio within base: %f : %f\n\n", baseLandRatio, baseWaterRatio);
515 }
516
517 // update neighbouring sectors
518 UpdateNeighbouringSectors();
519 UpdateBaseCenter();
520
521 // check if further expansion possible
522 if(sectors[0].size() == cfg->MAX_BASE_SIZE)
523 expandable = false;
524
525 freeBaseSpots = true;
526
527 return true;
528 }
529
530
531 return false;
532 }
533
UpdateMaxCombatUnitsSpotted(vector<unsigned short> & units_spotted)534 void AAIBrain::UpdateMaxCombatUnitsSpotted(vector<unsigned short>& units_spotted)
535 {
536 for(int i = 0; i < AAIBuildTable::ass_categories; ++i)
537 {
538 // decrease old values
539 max_combat_units_spotted[i] *= 0.996f;
540
541 // check for new max values
542 if((float)units_spotted[i] > max_combat_units_spotted[i])
543 max_combat_units_spotted[i] = (float)units_spotted[i];
544 }
545 }
546
UpdateAttackedByValues()547 void AAIBrain::UpdateAttackedByValues()
548 {
549 for(int i = 0; i < AAIBuildTable::ass_categories; ++i)
550 {
551 attacked_by[i] *= 0.96f;
552 }
553 }
554
AttackedBy(int combat_category_id)555 void AAIBrain::AttackedBy(int combat_category_id)
556 {
557 // update counter for current game
558 attacked_by[combat_category_id] += 1;
559
560 // update counter for memory dependent on playtime
561 ai->Getbt()->attacked_by_category_current[GetGamePeriod()][combat_category_id] += 1.0f;
562 }
563
UpdateDefenceCapabilities()564 void AAIBrain::UpdateDefenceCapabilities()
565 {
566 for(int i = 0; i < ai->Getbt()->assault_categories.size(); ++i)
567 defence_power_vs[i] = 0;
568 fill(defence_power_vs.begin(), defence_power_vs.end(), 0.0f);
569
570 if(cfg->AIR_ONLY_MOD)
571 {
572 for(list<UnitCategory>::iterator category = ai->Getbt()->assault_categories.begin(); category != ai->Getbt()->assault_categories.end(); ++category)
573 {
574 for(list<AAIGroup*>::iterator group = ai->Getgroup_list()[*category].begin(); group != ai->Getgroup_list()[*category].end(); ++group)
575 {
576 defence_power_vs[0] += (*group)->GetCombatPowerVsCategory(0);
577 defence_power_vs[1] += (*group)->GetCombatPowerVsCategory(1);
578 defence_power_vs[2] += (*group)->GetCombatPowerVsCategory(2);
579 defence_power_vs[3] += (*group)->GetCombatPowerVsCategory(3);
580 }
581 }
582 }
583 else
584 {
585 // anti air power
586 for(list<UnitCategory>::iterator category = ai->Getbt()->assault_categories.begin(); category != ai->Getbt()->assault_categories.end(); ++category)
587 {
588 for(list<AAIGroup*>::iterator group = ai->Getgroup_list()[*category].begin(); group != ai->Getgroup_list()[*category].end(); ++group)
589 {
590 if((*group)->group_unit_type == ASSAULT_UNIT)
591 {
592 if((*group)->category == GROUND_ASSAULT)
593 {
594 defence_power_vs[0] += (*group)->GetCombatPowerVsCategory(0);
595 defence_power_vs[2] += (*group)->GetCombatPowerVsCategory(2);
596 }
597 else if((*group)->category == HOVER_ASSAULT)
598 {
599 defence_power_vs[0] += (*group)->GetCombatPowerVsCategory(0);
600 defence_power_vs[2] += (*group)->GetCombatPowerVsCategory(2);
601 defence_power_vs[3] += (*group)->GetCombatPowerVsCategory(3);
602 }
603 else if((*group)->category == SEA_ASSAULT)
604 {
605 defence_power_vs[2] += (*group)->GetCombatPowerVsCategory(2);
606 defence_power_vs[3] += (*group)->GetCombatPowerVsCategory(3);
607 defence_power_vs[4] += (*group)->GetCombatPowerVsCategory(4);
608 }
609 else if((*group)->category == SUBMARINE_ASSAULT)
610 {
611 defence_power_vs[3] += (*group)->GetCombatPowerVsCategory(3);
612 defence_power_vs[4] += (*group)->GetCombatPowerVsCategory(4);
613 }
614 }
615 else if((*group)->group_unit_type == ANTI_AIR_UNIT)
616 defence_power_vs[1] += (*group)->GetCombatPowerVsCategory(1);
617 }
618 }
619 }
620
621 // debug
622 /*ai->Log("Defence capabilities:\n");
623
624 for(int i = 0; i < ai->Getbt()->assault_categories.size(); ++i)
625 ai->Log("%-20s %f\n" , ai->Getbt()->GetCategoryString2(ai->Getbt()->GetAssaultCategoryOfID(i)),defence_power_vs[i]);
626 */
627 }
628
AddDefenceCapabilities(int def_id,UnitCategory category)629 void AAIBrain::AddDefenceCapabilities(int def_id, UnitCategory category)
630 {
631 if(cfg->AIR_ONLY_MOD)
632 {
633 defence_power_vs[0] += ai->Getbt()->units_static[def_id].efficiency[0];
634 defence_power_vs[1] += ai->Getbt()->units_static[def_id].efficiency[1];
635 defence_power_vs[2] += ai->Getbt()->units_static[def_id].efficiency[2];
636 defence_power_vs[3] += ai->Getbt()->units_static[def_id].efficiency[3];
637 }
638 else
639 {
640 if(ai->Getbt()->GetUnitType(def_id) == ASSAULT_UNIT)
641 {
642 if(category == GROUND_ASSAULT)
643 {
644 defence_power_vs[0] += ai->Getbt()->units_static[def_id].efficiency[0];
645 defence_power_vs[2] += ai->Getbt()->units_static[def_id].efficiency[2];
646 }
647 else if(category == HOVER_ASSAULT)
648 {
649 defence_power_vs[0] += ai->Getbt()->units_static[def_id].efficiency[0];
650 defence_power_vs[2] += ai->Getbt()->units_static[def_id].efficiency[2];
651 defence_power_vs[3] += ai->Getbt()->units_static[def_id].efficiency[3];
652 }
653 else if(category == SEA_ASSAULT)
654 {
655 defence_power_vs[2] += ai->Getbt()->units_static[def_id].efficiency[2];
656 defence_power_vs[3] += ai->Getbt()->units_static[def_id].efficiency[3];
657 defence_power_vs[4] += ai->Getbt()->units_static[def_id].efficiency[4];
658 }
659 else if(category == SUBMARINE_ASSAULT)
660 {
661 defence_power_vs[3] += ai->Getbt()->units_static[def_id].efficiency[3];
662 defence_power_vs[4] += ai->Getbt()->units_static[def_id].efficiency[4];
663 }
664 }
665 else if(ai->Getbt()->GetUnitType(def_id) == ANTI_AIR_UNIT)
666 defence_power_vs[1] += ai->Getbt()->units_static[def_id].efficiency[1];
667 }
668 }
669
670 /*
671 void AAIBrain::SubtractDefenceCapabilities(int def_id, UnitCategory category)
672 {
673 }
674 */
675
Affordable()676 float AAIBrain::Affordable()
677 {
678 return 25.0f /(ai->Getcb()->GetMetalIncome() + 5.0f);
679 }
680
BuildUnits()681 void AAIBrain::BuildUnits()
682 {
683 //int side = ai->Getside()-1;
684 bool urgent = false;
685 int k;
686 unsigned int allowed_move_type = 0;
687
688 float cost = 1.0f + Affordable()/12.0f;
689
690 float ground_eff = 0;
691 float air_eff = 0;
692 float hover_eff = 0;
693 float sea_eff = 0;
694 float stat_eff = 0;
695 float submarine_eff = 0;
696
697 int anti_air_urgency;
698 int anti_sea_urgency;
699 int anti_ground_urgency;
700 int anti_hover_urgency;
701 int anti_submarine_urgency;
702
703 // determine elapsed game time
704 int game_period = GetGamePeriod();
705
706 float ground = GetAttacksBy(0, game_period);
707 float air = GetAttacksBy(0, game_period);
708 float hover = GetAttacksBy(0, game_period);
709 float sea = GetAttacksBy(0, game_period);
710 float submarine = GetAttacksBy(0, game_period);
711
712 if(cfg->AIR_ONLY_MOD)
713 {
714 // determine effectiveness vs several other units
715 anti_ground_urgency = (int)( 2 + (0.05f + ground) * (2.0f * attacked_by[0] + 1.0f) * (4.0f * max_combat_units_spotted[0] + 0.2f) / (4.0f * defence_power_vs[0] + 1));
716 anti_air_urgency = (int)( 2 + (0.05f + air) * (2.0f * attacked_by[1] + 1.0f) * (4.0f * max_combat_units_spotted[1] + 0.2f) / (4.0f * defence_power_vs[1] + 1));
717 anti_hover_urgency = (int)( 2 + (0.05f + hover) * (2.0f * attacked_by[2] + 1.0f) * (4.0f * max_combat_units_spotted[2] + 0.2f) / (4.0f * defence_power_vs[2] + 1));
718 anti_sea_urgency = (int) (2 + (0.05f + sea) * (2.0f * attacked_by[3] + 1.0f) * (4.0f * max_combat_units_spotted[3] + 0.2f) / (4.0f * defence_power_vs[3] + 1));
719
720 for(int i = 0; i < ai->Getexecute()->unitProductionRate; ++i)
721 {
722 ground_eff = 0;
723 air_eff = 0;
724 hover_eff = 0;
725 sea_eff = 0;
726
727 k = rand()%(anti_ground_urgency + anti_air_urgency + anti_hover_urgency + anti_sea_urgency);
728
729 if(k < anti_ground_urgency)
730 {
731 ground_eff = 4;
732 }
733 else if(k < anti_ground_urgency + anti_air_urgency)
734 {
735 air_eff = 4;
736 }
737 else if(k < anti_ground_urgency + anti_air_urgency + anti_hover_urgency)
738 {
739 hover_eff = 4;
740 }
741 else
742 {
743 sea_eff = 4;
744 }
745
746 BuildUnitOfMovementType(MOVE_TYPE_AIR, cost, ground_eff, air_eff, hover_eff, sea_eff, submarine_eff, stat_eff, urgent);
747 }
748 }
749 else
750 {
751 // choose unit category dependend on map type
752 if(ai->Getmap()->map_type == LAND_MAP)
753 {
754 // determine effectiveness vs several other units
755 anti_ground_urgency = (int)( 2 + (0.1f + ground) * (attacked_by[0] + 1.0f) * (4.0f * max_combat_units_spotted[0] + 0.2f) / (4.0f * defence_power_vs[0] + 1));
756 anti_air_urgency = (int)( 2 + (0.1f + air) * (attacked_by[1] + 1.0f) * (4.0f * max_combat_units_spotted[1] + 0.2f) / (4.0f * defence_power_vs[1] + 1));
757 anti_hover_urgency = (int)( 2 + (0.1f + hover) * (attacked_by[2] + 1.0f) * (4.0f * max_combat_units_spotted[2] + 0.2f) / (4.0f * defence_power_vs[2] + 1));
758
759 for(int i = 0; i < ai->Getexecute()->unitProductionRate; ++i)
760 {
761 ground_eff = 0;
762 air_eff = 0;
763 hover_eff = 0;
764 stat_eff = 0;
765
766 // determine unit type
767 if(rand()%(cfg->AIRCRAFT_RATE * 100) < 100)
768 {
769 allowed_move_type |= MOVE_TYPE_AIR;
770
771 k = rand()%1024;
772
773 if(k < 384)
774 {
775 ground_eff = 4;
776 hover_eff = 1;
777 }
778 else if(k < 640)
779 {
780 air_eff = 4;
781 }
782 else
783 {
784 stat_eff = 4;
785 }
786 }
787 else
788 {
789 allowed_move_type |= MOVE_TYPE_GROUND;
790 allowed_move_type |= MOVE_TYPE_HOVER;
791
792 k = rand()%(anti_ground_urgency + anti_air_urgency + anti_hover_urgency);
793
794 if(k < anti_ground_urgency)
795 {
796 stat_eff = 2;
797 ground_eff = 5;
798 hover_eff = 1;
799 }
800 else if(k < anti_ground_urgency + anti_air_urgency)
801 {
802 air_eff = 4;
803
804 if(anti_air_urgency > 2.0f * anti_ground_urgency)
805 urgent = true;
806 }
807 else
808 {
809 ground_eff = 1;
810 hover_eff = 4;
811 }
812 }
813
814 BuildUnitOfMovementType(allowed_move_type, cost, ground_eff, air_eff, hover_eff, sea_eff, submarine_eff, stat_eff, urgent);
815 }
816 }
817 else if(ai->Getmap()->map_type == LAND_WATER_MAP)
818 {
819 // determine effectiveness vs several other units
820 anti_ground_urgency = (int)( 2 + (0.1f + ground) * (2.0f * attacked_by[0] + 1.0f) * (4.0f * max_combat_units_spotted[0] + 0.2f) / (4.0f * defence_power_vs[0] + 1));
821 anti_air_urgency = (int)( 2 + (0.1f + air) * (2.0f * attacked_by[1] + 1.0f) * (4.0f * max_combat_units_spotted[1] + 0.2f) / (4.0f * defence_power_vs[1] + 1));
822 anti_hover_urgency = (int)( 2 + (0.1f + hover) * (2.0f * attacked_by[2] + 1.0f) * (4.0f * max_combat_units_spotted[2] + 0.2f) / (4.0f * defence_power_vs[2] + 1));
823 anti_sea_urgency = (int) (2 + (0.1f + sea) * (2.0f * attacked_by[3] + 1.0f) * (4.0f * max_combat_units_spotted[3] + 0.2f) / (4.0f * defence_power_vs[3] + 1));
824 anti_submarine_urgency = (int)( 2 + (0.1f + submarine) * (2.0f * attacked_by[4] + 1.0f) * (4.0f * max_combat_units_spotted[4] + 0.2f) / (4.0f * defence_power_vs[4] + 1));
825
826 for(int i = 0; i < ai->Getexecute()->unitProductionRate; ++i)
827 {
828 ground_eff = 0;
829 air_eff = 0;
830 hover_eff = 0;
831 sea_eff = 0;
832 submarine_eff = 0;
833 stat_eff = 0;
834
835
836 if(rand()%(cfg->AIRCRAFT_RATE * 100) < 100)
837 {
838 allowed_move_type |= MOVE_TYPE_AIR;
839
840 if(rand()%1000 < 333)
841 {
842 ground_eff = 4;
843 hover_eff = 1;
844 sea_eff = 2;
845 }
846 else if(rand()%1000 < 333)
847 {
848 air_eff = 4;
849 }
850 else
851 {
852 stat_eff = 4;
853 }
854
855 }
856 else
857 {
858 allowed_move_type |= MOVE_TYPE_HOVER;
859
860 k = rand()%(anti_ground_urgency + anti_air_urgency + anti_hover_urgency + anti_sea_urgency + anti_submarine_urgency);
861
862 if(k < anti_ground_urgency)
863 {
864 stat_eff = 2;
865 ground_eff = 5;
866 hover_eff = 1;
867 allowed_move_type |= MOVE_TYPE_GROUND;
868 }
869 else if(k < anti_ground_urgency + anti_air_urgency)
870 {
871 allowed_move_type |= MOVE_TYPE_GROUND;
872 allowed_move_type |= MOVE_TYPE_SEA;
873
874 air_eff = 4;
875
876 if(anti_air_urgency > 2.0f * anti_ground_urgency)
877 urgent = true;
878 }
879 else if(k < anti_ground_urgency + anti_air_urgency + anti_hover_urgency)
880 {
881 allowed_move_type |= MOVE_TYPE_GROUND;
882 allowed_move_type |= MOVE_TYPE_SEA;
883
884 ground_eff = 1;
885 hover_eff = 4;
886 }
887 else if(k < anti_ground_urgency + anti_air_urgency + anti_hover_urgency + anti_sea_urgency)
888 {
889 allowed_move_type |= MOVE_TYPE_SEA;
890 hover_eff = 1;
891 sea_eff = 4;
892 submarine_eff = 1;
893 }
894 else
895 {
896 allowed_move_type |= MOVE_TYPE_SEA;
897 submarine_eff = 4;
898 sea_eff = 1;
899 }
900 }
901
902 BuildUnitOfMovementType(allowed_move_type, cost, ground_eff, air_eff, hover_eff, sea_eff, submarine_eff,stat_eff, urgent);
903 }
904 }
905 else if(ai->Getmap()->map_type == WATER_MAP)
906 {
907 // determine effectiveness vs several other units
908 anti_air_urgency = (int)( 2 + (0.1f + air) * (2.0f * attacked_by[1] + 1.0f) * (4.0f * max_combat_units_spotted[1] + 0.2f) / (4.0f * defence_power_vs[1] + 1));
909 anti_hover_urgency = (int)( 2 + (0.1f + hover) * (2.0f * attacked_by[2] + 1.0f) * (4.0f * max_combat_units_spotted[2] + 0.2f) / (4.0f * defence_power_vs[2] + 1));
910 anti_sea_urgency = (int) (2 + (0.1f + sea) * (2.0f * attacked_by[3] + 1.0f) * (4.0f * max_combat_units_spotted[3] + 0.2f) / (4.0f * defence_power_vs[3] + 1));
911 anti_submarine_urgency = (int)( 2 + (0.1f + submarine) * (2.0f * attacked_by[4] + 1.0f) * (4.0f * max_combat_units_spotted[4] + 0.2f) / (4.0f * defence_power_vs[4] + 1));
912
913 for(int i = 0; i < ai->Getexecute()->unitProductionRate; ++i)
914 {
915 air_eff = 0;
916 hover_eff = 0;
917 sea_eff = 0;
918 submarine_eff = 0;
919 stat_eff = 0;
920
921 if(rand()%(cfg->AIRCRAFT_RATE * 100) < 100)
922 {
923 allowed_move_type |= MOVE_TYPE_AIR;
924
925 if(rand()%1000 < 333)
926 {
927 sea_eff = 4;
928 hover_eff = 2;
929 }
930 else if(rand()%1000 < 333)
931 air_eff = 4;
932 else
933 stat_eff = 4;
934 }
935 else
936 {
937 allowed_move_type |= MOVE_TYPE_HOVER;
938 allowed_move_type |= MOVE_TYPE_SEA;
939
940 k = rand()%(anti_sea_urgency + anti_air_urgency + anti_hover_urgency + anti_submarine_urgency);
941
942 if(k < anti_air_urgency)
943 {
944 air_eff = 4;
945
946 if(anti_air_urgency > anti_sea_urgency)
947 urgent = true;
948 }
949 else if(k < anti_hover_urgency + anti_air_urgency)
950 {
951 sea_eff = 1;
952 hover_eff = 5;
953 }
954 else if(k < anti_hover_urgency + anti_air_urgency + anti_submarine_urgency)
955 {
956 submarine_eff = 4;
957 sea_eff = 1;
958 }
959 else
960 {
961 hover_eff = 1;
962 sea_eff = 5;
963 submarine_eff = 1;
964 }
965 }
966
967 BuildUnitOfMovementType(allowed_move_type, cost, ground_eff, air_eff, hover_eff, sea_eff, submarine_eff,stat_eff, urgent);
968 }
969 }
970 }
971 }
972
BuildUnitOfMovementType(unsigned int allowed_move_type,float cost,float ground_eff,float air_eff,float hover_eff,float sea_eff,float submarine_eff,float stat_eff,bool urgent)973 void AAIBrain::BuildUnitOfMovementType(unsigned int allowed_move_type, float cost, float ground_eff, float air_eff, float hover_eff, float sea_eff, float submarine_eff, float stat_eff, bool urgent)
974 {
975 float speed = 0;
976 float range = 0;
977 float power = 2;
978 float eff = 2;
979
980 // determine speed, range & eff
981 if(rand()%cfg->FAST_UNITS_RATE == 1)
982 {
983 if(rand()%2 == 1)
984 speed = 1;
985 else
986 speed = 2;
987 }
988 else
989 speed = 0.1f;
990
991 if(rand()%cfg->HIGH_RANGE_UNITS_RATE == 1)
992 {
993 const int t = rand()%1000;
994
995 if(t < 350)
996 range = 0.75;
997 else if(t == 700)
998 range = 1.3f;
999 else
1000 range = 1.7f;
1001 }
1002 else
1003 range = 0.1f;
1004
1005 if(rand()%3 == 1)
1006 power = 4;
1007
1008 if(rand()%3 == 1)
1009 eff = 4;
1010
1011 // start selection
1012 int unit = 0, ground = 0, hover = 0;
1013
1014 if(allowed_move_type & MOVE_TYPE_AIR)
1015 {
1016 if(rand()%cfg->LEARN_RATE == 1)
1017 unit = ai->Getbt()->GetRandomUnit(ai->Getbt()->units_of_category[AIR_ASSAULT][ai->Getside()-1]);
1018 else
1019 {
1020 unit = ai->Getbt()->GetAirAssault(ai->Getside(), power, ground_eff, air_eff, hover_eff, sea_eff, stat_eff, eff, speed, range, cost, 9, false);
1021
1022 if(unit && ai->Getbt()->units_dynamic[unit].constructorsAvailable + ai->Getbt()->units_dynamic[unit].constructorsRequested <= 0)
1023 {
1024 ai->Getbt()->BuildFactoryFor(unit);
1025 unit = ai->Getbt()->GetAirAssault(ai->Getside(), power, ground_eff, air_eff, hover_eff, sea_eff, stat_eff, eff, speed, range, cost, 9, true);
1026 }
1027 }
1028 }
1029
1030 if(allowed_move_type & MOVE_TYPE_GROUND)
1031 {
1032 // choose random unit (to learn more)
1033 if(rand()%cfg->LEARN_RATE == 1)
1034 ground = ai->Getbt()->GetRandomUnit(ai->Getbt()->units_of_category[GROUND_ASSAULT][ai->Getside()-1]);
1035 else
1036 {
1037 ground = ai->Getbt()->GetGroundAssault(ai->Getside(), power, ground_eff, air_eff, hover_eff, sea_eff, stat_eff, eff, speed, range, cost, 15, false);
1038
1039 if(ground && ai->Getbt()->units_dynamic[ground].constructorsAvailable + ai->Getbt()->units_dynamic[ground].constructorsRequested <= 0)
1040 {
1041 ai->Getbt()->BuildFactoryFor(ground);
1042 ground = ai->Getbt()->GetGroundAssault(ai->Getside(), power, ground_eff, air_eff, hover_eff, sea_eff, stat_eff, eff, speed, range, cost, 15, true);
1043 }
1044 }
1045 }
1046
1047 if(allowed_move_type & MOVE_TYPE_HOVER)
1048 {
1049 if(rand()%cfg->LEARN_RATE == 1)
1050 hover = ai->Getbt()->GetRandomUnit(ai->Getbt()->units_of_category[HOVER_ASSAULT][ai->Getside()-1]);
1051 else
1052 {
1053 hover = ai->Getbt()->GetHoverAssault(ai->Getside(), power, ground_eff, air_eff, hover_eff, sea_eff, stat_eff, eff, speed, range, cost, 9, false);
1054
1055 if(hover && ai->Getbt()->units_dynamic[hover].constructorsAvailable + ai->Getbt()->units_dynamic[hover].constructorsRequested <= 0)
1056 {
1057 ai->Getbt()->BuildFactoryFor(hover);
1058 hover = ai->Getbt()->GetHoverAssault(ai->Getside(), power, ground_eff, air_eff, hover_eff, sea_eff, stat_eff, eff, speed, range, cost, 9, true);
1059 }
1060 }
1061 }
1062
1063 if(allowed_move_type & MOVE_TYPE_SEA)
1064 {
1065 int ship, submarine;
1066
1067 if(rand()%cfg->LEARN_RATE == 1)
1068 {
1069 ship = ai->Getbt()->GetRandomUnit(ai->Getbt()->units_of_category[SEA_ASSAULT][ai->Getside()-1]);
1070 submarine = ai->Getbt()->GetRandomUnit(ai->Getbt()->units_of_category[SUBMARINE_ASSAULT][ai->Getside()-1]);
1071 }
1072 else
1073 {
1074 ship = ai->Getbt()->GetSeaAssault(ai->Getside(), power, ground_eff, air_eff, hover_eff, sea_eff, submarine_eff, stat_eff, eff, speed, range, cost, 9, false);
1075
1076 if(ship && ai->Getbt()->units_dynamic[ship].constructorsAvailable + ai->Getbt()->units_dynamic[ship].constructorsRequested <= 0)
1077 {
1078 ai->Getbt()->BuildFactoryFor(ship);
1079 ship = ai->Getbt()->GetSeaAssault(ai->Getside(), power, ground_eff, air_eff, hover_eff, sea_eff, submarine_eff, stat_eff, eff, speed, range, cost, 9, false);
1080 }
1081
1082 submarine = ai->Getbt()->GetSubmarineAssault(ai->Getside(), power, sea_eff, submarine_eff, stat_eff, eff, speed, range, cost, 9, false);
1083
1084 if(submarine && ai->Getbt()->units_dynamic[submarine].constructorsAvailable + ai->Getbt()->units_dynamic[submarine].constructorsRequested <= 0)
1085 {
1086 ai->Getbt()->BuildFactoryFor(submarine);
1087 submarine = ai->Getbt()->GetSubmarineAssault(ai->Getside(), power, sea_eff, submarine_eff, stat_eff, eff, speed, range, cost, 9, false);
1088 }
1089 }
1090
1091 // determine better unit for desired purpose
1092 if(ship)
1093 {
1094 if(submarine)
1095 unit = ai->Getbt()->DetermineBetterUnit(ship, submarine, 0, air_eff, hover_eff, sea_eff, submarine_eff, speed, range, cost);
1096 else
1097 unit = ship;
1098 }
1099 else if(submarine)
1100 unit = submarine;
1101 }
1102
1103 // ground and hover assault
1104 if(ground)
1105 {
1106 if(hover)
1107 unit = ai->Getbt()->DetermineBetterUnit(ground, hover, ground_eff, air_eff, hover_eff, sea_eff, 0, speed, range, cost);
1108 else
1109 unit = ground;
1110 }
1111 // hover and sea
1112 else if(hover)
1113 {
1114 if(unit)
1115 unit = ai->Getbt()->DetermineBetterUnit(unit, hover, 0, air_eff, hover_eff, sea_eff, submarine_eff, speed, range, cost);
1116 else
1117 unit = hover;
1118 }
1119
1120 if(unit)
1121 {
1122 if(ai->Getbt()->units_dynamic[unit].constructorsAvailable > 0)
1123 {
1124 if(ai->Getbt()->units_static[unit].cost < cfg->MAX_COST_LIGHT_ASSAULT * ai->Getbt()->max_cost[ai->Getbt()->units_static[unit].category][ai->Getside()-1])
1125 {
1126 if(ai->Getexecute()->AddUnitToBuildqueue(unit, 3, urgent))
1127 {
1128 ai->Getbt()->units_dynamic[unit].requested += 3;
1129 ai->Getut()->UnitRequested(ai->Getbt()->units_static[unit].category, 3);
1130 }
1131 }
1132 else if(ai->Getbt()->units_static[unit].cost < cfg->MAX_COST_MEDIUM_ASSAULT * ai->Getbt()->max_cost[ai->Getbt()->units_static[unit].category][ai->Getside()-1])
1133 {
1134 if(ai->Getexecute()->AddUnitToBuildqueue(unit, 2, urgent))
1135 ai->Getbt()->units_dynamic[unit].requested += 2;
1136 ai->Getut()->UnitRequested(ai->Getbt()->units_static[unit].category, 2);
1137 }
1138 else
1139 {
1140 if(ai->Getexecute()->AddUnitToBuildqueue(unit, 1, urgent))
1141 ai->Getbt()->units_dynamic[unit].requested += 1;
1142 ai->Getut()->UnitRequested(ai->Getbt()->units_static[unit].category);
1143 }
1144 }
1145 else if(ai->Getbt()->units_dynamic[unit].constructorsRequested <= 0)
1146 ai->Getbt()->BuildFactoryFor(unit);
1147 }
1148 }
1149
1150
GetGamePeriod()1151 int AAIBrain::GetGamePeriod()
1152 {
1153 int tick = ai->Getcb()->GetCurrentFrame();
1154
1155 if(tick < 18000)
1156 return 0;
1157 else if(tick < 36000)
1158 return 1;
1159 else if(tick < 72000)
1160 return 2;
1161 else
1162 return 3;
1163
1164 }
1165
IsSafeSector(AAISector * sector)1166 bool AAIBrain::IsSafeSector(AAISector *sector)
1167 {
1168 // TODO: improve criteria
1169 return (sector->lost_units[MOBILE_CONSTRUCTOR-COMMANDER] < 0.5
1170 && sector->enemy_combat_units[5] < 0.1 && sector->enemy_structures < 0.01
1171 && sector->enemies_on_radar == 0);
1172 }
1173
GetAttacksBy(int combat_category,int game_period)1174 float AAIBrain::GetAttacksBy(int combat_category, int game_period)
1175 {
1176 return (ai->Getbt()->attacked_by_category_current[game_period][combat_category] + ai->Getbt()->attacked_by_category_learned[ai->Getmap()->map_type][game_period][combat_category]) / 2.0f;
1177 }
1178
UpdatePressureByEnemy()1179 void AAIBrain::UpdatePressureByEnemy()
1180 {
1181 enemy_pressure_estimation = 0;
1182
1183 // check base and neighbouring sectors for enemies
1184 for(list<AAISector*>::iterator s = sectors[0].begin(); s != sectors[0].end(); ++s)
1185 enemy_pressure_estimation += 0.1f * (*s)->enemy_combat_units[5];
1186
1187 for(list<AAISector*>::iterator s = sectors[1].begin(); s != sectors[1].end(); ++s)
1188 enemy_pressure_estimation += 0.1f * (*s)->enemy_combat_units[5];
1189
1190 if(enemy_pressure_estimation > 1.0f)
1191 enemy_pressure_estimation = 1.0f;
1192 }
1193