1 #include "CIntel.h"
2
3 #include <ctime>
4
5 #include "CAI.h"
6 #include "CUnit.h"
7 #include "GameMap.hpp"
8
9
CIntel(AIClasses * ai)10 CIntel::CIntel(AIClasses *ai) {
11 this->ai = ai;
12
13 initialized = false;
14 strategyTechUp = false;
15
16 selector.push_back(ASSAULT);
17 selector.push_back(SCOUTER);
18 selector.push_back(SNIPER);
19 selector.push_back(ARTILLERY);
20 selector.push_back(ANTIAIR);
21 selector.push_back(AIR);
22 selector.push_back(SUB);
23 selector.push_back(COMMANDER);
24
25 // NOTE: the order is somewhat target priority
26 targets[ENGAGE].push_back(CategoryMatcher(COMMANDER));
27 targets[ENGAGE].push_back(CategoryMatcher(ATTACKER));
28 targets[ENGAGE].push_back(CategoryMatcher(EMAKER));
29 targets[ENGAGE].push_back(CategoryMatcher(MMAKER));
30 targets[ENGAGE].push_back(CategoryMatcher(DEFENSE));
31 targets[ENGAGE].push_back(CategoryMatcher(BUILDER));
32 targets[ENGAGE].push_back(CategoryMatcher(FACTORY));
33 targets[ENGAGE].push_back(CategoryMatcher(CATS_ANY, ATTACKER));
34
35 targets[SCOUT].push_back(CategoryMatcher(BUILDER, COMMANDER|FACTORY));
36 targets[SCOUT].push_back(CategoryMatcher(MMAKER|MEXTRACTOR));
37 targets[SCOUT].push_back(CategoryMatcher(EMAKER));
38 targets[SCOUT].push_back(CategoryMatcher(CATS_ANY, ATTACKER));
39
40 targets[BOMBER].push_back(CategoryMatcher(DEFENSE));
41 targets[BOMBER].push_back(CategoryMatcher(COMMANDER));
42 targets[BOMBER].push_back(CategoryMatcher(FACTORY));
43 targets[BOMBER].push_back(CategoryMatcher(EMAKER));
44 targets[BOMBER].push_back(CategoryMatcher(MMAKER));
45 targets[BOMBER].push_back(CategoryMatcher(NUKE));
46
47 targets[AIRFIGHTER].push_back(CategoryMatcher(AIR));
48
49 for (TargetCategoryMap::iterator it = targets.begin(); it != targets.end(); ++it) {
50 for (int i = 0; i < it->second.size(); i++) {
51 enemies.registerMatcher(it->second[i]);
52 }
53 }
54 }
55
getEnemyVector()56 float3 CIntel::getEnemyVector() {
57 return enemyvector;
58 }
59
init()60 void CIntel::init() {
61 if (initialized) return;
62
63 resetCounters();
64 updateRoulette();
65
66 updateEnemyVector();
67
68 /* FIXME:
69 I faced situation that on maps with less land there is a direct
70 path to enemy unit, but algo below starts to play a non-land game.
71 I could not think up an appropriate algo to avoid this. I thought about
72 tracing a path in the beginning of the game from my commander to enemy
73 would be ok, but commander is an amphibious unit. It is not trivial
74 stuff without external helpers in config files or terrain analysis.
75 */
76 if(ai->gamemap->IsWaterMap()) {
77 allowedFactories.push_back(NAVAL);
78 allowedFactories.push_back(HOVER);
79 }
80 else {
81 unitCategory nextFactory;
82 if (ai->gamemap->IsKbotMap()) {
83 allowedFactories.push_back(KBOT);
84 nextFactory = VEHICLE;
85 }
86 else {
87 allowedFactories.push_back(VEHICLE);
88 nextFactory = KBOT;
89 }
90
91 if (ai->gamemap->IsHooverMap()) {
92 if (ai->gamemap->GetAmountOfWater() > 0.5) {
93 allowedFactories.push_back(HOVER);
94 }
95 else {
96 allowedFactories.push_back(nextFactory);
97 nextFactory = HOVER;
98 }
99 }
100
101 allowedFactories.push_back(nextFactory);
102 }
103 // TODO: do not build air on too small maps?
104 allowedFactories.push_back(AIRCRAFT);
105
106 // vary first factory among allied AIs...
107 int i = ai->allyIndex;
108 while (i > 1) {
109 allowedFactories.push_back(allowedFactories.front());
110 allowedFactories.pop_front();
111 i--;
112 }
113
114 // FIXME: engineer better decision algo
115 if (ai->gamemap->IsMetalMap())
116 strategyTechUp = true;
117 else
118 // NOTE: clock() gives much better results than rng.RndFloat() (at least under MSVC)
119 strategyTechUp = ((clock() % 3) == 0);
120
121 LOG_II("CIntel::init Tech-up strategy: " << strategyTechUp)
122
123 initialized = true;
124 }
125
update(int frame)126 void CIntel::update(int frame) {
127 resetCounters();
128
129 if (enemyvector == ZeroVector)
130 updateEnemyVector();
131
132 int numUnits = ai->cbc->GetEnemyUnits(&ai->unitIDs[0], MAX_UNITS);
133
134 for (int i = 0; i < numUnits; i++) {
135 const int uid = ai->unitIDs[i];
136 const UnitDef* ud = ai->cbc->GetUnitDef(uid);
137
138 if (ud == NULL)
139 continue;
140
141 unitCategory c = UT(ud->id)->cats;
142
143 if ((c&ATTACKER).any() && (c&MOBILE).any())
144 updateCounters(c);
145 }
146
147 updateRoulette();
148 }
149
counter(unitCategory c)150 unitCategory CIntel::counter(unitCategory c) {
151 // TODO: implement customizable by config counter units
152
153 // NOTE: current algo is not perfect because does not consider
154 // environmental tags
155
156 if (c == AIR) return ANTIAIR;
157 if (c == SUB) return TORPEDO;
158 if (c == ASSAULT) return SNIPER;
159 if (c == SCOUTER) return ASSAULT;
160 if (c == SNIPER) return ARTILLERY;
161 if (c == ARTILLERY) return ASSAULT;
162 if (c == ANTIAIR) return ARTILLERY;
163 if (c == COMMANDER) return ASSAULT;
164
165 return ARTILLERY;
166 }
167
updateCounters(unitCategory ecats)168 void CIntel::updateCounters(unitCategory ecats) {
169 for (size_t i = 0; i < selector.size(); i++) {
170 const unitCategory c = selector[i];
171 if ((c&ecats).any()) {
172 enemyCounter[c]++;
173 counterCounter[counter(c)]++;
174 totalEnemyCount++;
175 totalCounterCount++;
176 }
177 }
178 }
179
updateRoulette()180 void CIntel::updateRoulette() {
181 roulette.clear();
182
183 if (totalCounterCount > 0) {
184 /* Put the counts in a normalized reversed map first and reset counters */
185 for (size_t i = 0; i < selector.size(); i++) {
186 const unitCategory c = selector[i];
187 const float weight = counterCounter[c] / float(totalCounterCount);
188 roulette.insert(std::pair<float, unitCategory>(weight, c));
189 }
190 }
191 }
192
resetCounters()193 void CIntel::resetCounters() {
194 // set equal chance of building military unit for all cats by default...
195 for (size_t i = 0; i < selector.size(); i++) {
196 counterCounter[selector[i]] = 1;
197 }
198 // without need do not build the following unit cats...
199 counterCounter[TORPEDO] = 0;
200 counterCounter[ANTIAIR] = 0;
201 counterCounter[AIR] = 0;
202 counterCounter[SUB] = 0;
203 counterCounter[COMMANDER] = 0;
204 // boost chance of assault units to be built by default
205 counterCounter[ASSAULT] = 3;
206 // adjust scout appearance chance by default...
207 if (ai->difficulty == DIFFICULTY_EASY
208 || ai->military->idleScoutGroupsNum() >= MAX_IDLE_SCOUT_GROUPS)
209 counterCounter[SCOUTER] = 0;
210
211 totalCounterCount = totalEnemyCount = 0;
212 for (size_t i = 0; i < selector.size(); i++) {
213 totalCounterCount += counterCounter[selector[i]];
214 }
215 }
216
enemyInbound()217 bool CIntel::enemyInbound() {
218 // TODO:
219 return false;
220 }
221
onEnemyCreated(int enemy)222 void CIntel::onEnemyCreated(int enemy) {
223 const UnitDef* ud = ai->cbc->GetUnitDef(enemy);
224 if (ud) {
225 LOG_II("CIntel::onEnemyCreated Unit(" << enemy << ")")
226 //assert(ai->cbc->GetUnitTeam(enemy) != ai->team);
227 enemies.addUnit(UT(ud->id), enemy);
228 }
229 }
230
onEnemyDestroyed(int enemy,int attacker)231 void CIntel::onEnemyDestroyed(int enemy, int attacker) {
232 LOG_II("CIntel::onEnemyDestroyed Unit(" << enemy << ")")
233 enemies.removeUnit(enemy);
234 }
235
updateEnemyVector()236 void CIntel::updateEnemyVector() {
237 int numUnits = ai->cbc->GetEnemyUnits(&ai->unitIDs[0], MAX_PLAYERS);
238
239 enemyvector = ZeroVector;
240 if (numUnits == 0)
241 return;
242 for (int i = 0; i < numUnits; i++) {
243 enemyvector += ai->cbc->GetUnitPos(ai->unitIDs[i]);
244 }
245 enemyvector /= numUnits;
246 }
247