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