1 // ==============================================================
2 //	This file is part of Glest (www.glest.org)
3 //
4 //	Copyright (C) 2001-2008 Martiño Figueroa
5 //
6 //	You can redistribute this code and/or modify it under
7 //	the terms of the GNU General Public License as published
8 //	by the Free Software Foundation; either version 2 of the
9 //	License, or (at your option) any later version
10 // ==============================================================
11 
12 #include "ai.h"
13 #include "ai_interface.h"
14 #include "ai_rule.h"
15 #include "unit_type.h"
16 #include "unit.h"
17 #include "map.h"
18 #include "faction_type.h"
19 #include "leak_dumper.h"
20 
21 using namespace Shared::Graphics;
22 using namespace Shared::Util;
23 
24 namespace Glest { namespace Game {
25 
Task()26 Task::Task() {
27 	taskClass = tcProduce;
28 }
29 
30 //void Task::saveGame(XmlNode *rootNode) const {
31 //	std::map<string,string> mapTagReplacements;
32 //	XmlNode *taskNode = rootNode->addChild("Task");
33 //}
34 
35 // =====================================================
36 // 	class ProduceTask
37 // =====================================================
ProduceTask()38 ProduceTask::ProduceTask() : Task() {
39 	taskClass= tcProduce;
40 	unitType= NULL;
41 	resourceType= NULL;
42 	unitClass = ucWarrior;
43 }
44 
ProduceTask(UnitClass unitClass)45 ProduceTask::ProduceTask(UnitClass unitClass) : Task() {
46 	taskClass= tcProduce;
47 	this->unitClass= unitClass;
48 	unitType= NULL;
49 	resourceType= NULL;
50 }
51 
ProduceTask(const UnitType * unitType)52 ProduceTask::ProduceTask(const UnitType *unitType) : Task() {
53 	taskClass= tcProduce;
54 	this->unitType= unitType;
55 	resourceType= NULL;
56 	unitClass = ucWarrior;
57 }
58 
ProduceTask(const ResourceType * resourceType)59 ProduceTask::ProduceTask(const ResourceType *resourceType) : Task() {
60 	taskClass= tcProduce;
61 	unitType= NULL;
62 	unitClass = ucWarrior;
63 	this->resourceType= resourceType;
64 }
65 
toString() const66 string ProduceTask::toString() const{
67 	string str= "Produce ";
68 	if(unitType!=NULL){
69 		str+= unitType->getName(false);
70 	}
71 	return str;
72 }
73 
saveGame(XmlNode * rootNode) const74 void ProduceTask::saveGame(XmlNode *rootNode) const {
75 	std::map<string,string> mapTagReplacements;
76 	XmlNode *taskNode = rootNode->addChild("Task");
77 	taskNode->addAttribute("taskClass",intToStr(taskClass), mapTagReplacements);
78 	XmlNode *produceTaskNode = taskNode->addChild("ProduceTask");
79 
80 //	UnitClass unitClass;
81 	produceTaskNode->addAttribute("unitClass",intToStr(unitClass), mapTagReplacements);
82 //	const UnitType *unitType;
83 	if(unitType != NULL) {
84 		produceTaskNode->addAttribute("unitType",unitType->getName(false), mapTagReplacements);
85 	}
86 //	const ResourceType *resourceType;
87 	if(resourceType != NULL) {
88 		produceTaskNode->addAttribute("resourceType",resourceType->getName(false), mapTagReplacements);
89 	}
90 }
91 
loadGame(const XmlNode * rootNode,Faction * faction)92 ProduceTask * ProduceTask::loadGame(const XmlNode *rootNode, Faction *faction) {
93 	const XmlNode *produceTaskNode = rootNode->getChild("ProduceTask");
94 
95 	ProduceTask *newTask = new ProduceTask();
96 	//	UnitClass unitClass;
97 	newTask->unitClass = static_cast<UnitClass>(produceTaskNode->getAttribute("unitClass")->getIntValue());
98 	//	const UnitType *unitType;
99 	if(produceTaskNode->hasAttribute("unitType")) {
100 		string unitTypeName = produceTaskNode->getAttribute("unitType")->getValue();
101 		newTask->unitType = faction->getType()->getUnitType(unitTypeName);
102 	}
103 	//	const ResourceType *resourceType;
104 	if(produceTaskNode->hasAttribute("resourceType")) {
105 		string resourceTypeName = produceTaskNode->getAttribute("resourceType")->getValue();
106 		newTask->resourceType = faction->getTechTree()->getResourceType(resourceTypeName);
107 	}
108 
109 	return newTask;
110 }
111 
112 // =====================================================
113 // 	class BuildTask
114 // =====================================================
BuildTask()115 BuildTask::BuildTask() {
116 	taskClass= tcBuild;
117 	this->unitType= NULL;
118 	resourceType= NULL;
119 	forcePos= false;
120 }
121 
BuildTask(const UnitType * unitType)122 BuildTask::BuildTask(const UnitType *unitType){
123 	taskClass= tcBuild;
124 	this->unitType= unitType;
125 	resourceType= NULL;
126 	forcePos= false;
127 }
128 
BuildTask(const ResourceType * resourceType)129 BuildTask::BuildTask(const ResourceType *resourceType){
130 	taskClass= tcBuild;
131 	unitType= NULL;
132 	this->resourceType= resourceType;
133 	forcePos= false;
134 }
135 
BuildTask(const UnitType * unitType,const Vec2i & pos)136 BuildTask::BuildTask(const UnitType *unitType, const Vec2i &pos){
137 	taskClass= tcBuild;
138 	this->unitType= unitType;
139 	resourceType= NULL;
140 	forcePos= true;
141 	this->pos= pos;
142 }
143 
toString() const144 string BuildTask::toString() const{
145 	string str= "Build ";
146 	if(unitType!=NULL){
147 		str+= unitType->getName(false);
148 	}
149 	return str;
150 }
151 
saveGame(XmlNode * rootNode) const152 void BuildTask::saveGame(XmlNode *rootNode) const {
153 	std::map<string,string> mapTagReplacements;
154 	XmlNode *taskNode = rootNode->addChild("Task");
155 	taskNode->addAttribute("taskClass",intToStr(taskClass), mapTagReplacements);
156 	XmlNode *buildTaskNode = taskNode->addChild("BuildTask");
157 
158 //	const UnitType *unitType;
159 	if(unitType != NULL) {
160 		buildTaskNode->addAttribute("unitType",unitType->getName(false), mapTagReplacements);
161 	}
162 //	const ResourceType *resourceType;
163 	if(resourceType != NULL) {
164 		buildTaskNode->addAttribute("resourceType",resourceType->getName(), mapTagReplacements);
165 	}
166 //	bool forcePos;
167 	buildTaskNode->addAttribute("forcePos",intToStr(forcePos), mapTagReplacements);
168 //	Vec2i pos;
169 	buildTaskNode->addAttribute("pos",pos.getString(), mapTagReplacements);
170 }
171 
loadGame(const XmlNode * rootNode,Faction * faction)172 BuildTask * BuildTask::loadGame(const XmlNode *rootNode, Faction *faction) {
173 	const XmlNode *buildTaskNode = rootNode->getChild("BuildTask");
174 
175 	BuildTask *newTask = new BuildTask();
176 	if(buildTaskNode->hasAttribute("unitType")) {
177 		string unitTypeName = buildTaskNode->getAttribute("unitType")->getValue();
178 		newTask->unitType = faction->getType()->getUnitType(unitTypeName);
179 	}
180 	if(buildTaskNode->hasAttribute("resourceType")) {
181 		string resourceTypeName = buildTaskNode->getAttribute("resourceType")->getValue();
182 		newTask->resourceType = faction->getTechTree()->getResourceType(resourceTypeName);
183 	}
184 
185 	newTask->forcePos = buildTaskNode->getAttribute("forcePos")->getIntValue() != 0;
186 	newTask->pos = Vec2i::strToVec2(buildTaskNode->getAttribute("pos")->getValue());
187 
188 	return newTask;
189 }
190 
191 // =====================================================
192 // 	class UpgradeTask
193 // =====================================================
UpgradeTask()194 UpgradeTask::UpgradeTask() {
195 	taskClass= tcUpgrade;
196 	this->upgradeType= NULL;
197 }
198 
UpgradeTask(const UpgradeType * upgradeType)199 UpgradeTask::UpgradeTask(const UpgradeType *upgradeType){
200 	taskClass= tcUpgrade;
201 	this->upgradeType= upgradeType;
202 }
203 
toString() const204 string UpgradeTask::toString() const{
205 	string str= "Build ";
206 	if(upgradeType!=NULL){
207 		str+= upgradeType->getName();
208 	}
209 	return str;
210 }
211 
saveGame(XmlNode * rootNode) const212 void UpgradeTask::saveGame(XmlNode *rootNode) const {
213 	std::map<string,string> mapTagReplacements;
214 	XmlNode *taskNode = rootNode->addChild("Task");
215 	taskNode->addAttribute("taskClass",intToStr(taskClass), mapTagReplacements);
216 	XmlNode *upgradeTaskNode = taskNode->addChild("UpgradeTask");
217 
218 	if(upgradeType != NULL) {
219 		//upgradeType->saveGame(upgradeTaskNode);
220 		upgradeTaskNode->addAttribute("upgradeType",upgradeType->getName(), mapTagReplacements);
221 	}
222 }
223 
loadGame(const XmlNode * rootNode,Faction * faction)224 UpgradeTask * UpgradeTask::loadGame(const XmlNode *rootNode, Faction *faction) {
225 	const XmlNode *upgradeTaskNode = rootNode->getChild("UpgradeTask");
226 
227 	UpgradeTask *newTask = new UpgradeTask();
228 	if(upgradeTaskNode->hasAttribute("upgradeType")) {
229 		string upgradeTypeName = upgradeTaskNode->getAttribute("upgradeType")->getValue();
230 		newTask->upgradeType = faction->getType()->getUpgradeType(upgradeTypeName);
231 	}
232 	return newTask;
233 }
234 
235 // =====================================================
236 // 	class Ai
237 // =====================================================
238 
init(AiInterface * aiInterface,int useStartLocation)239 void Ai::init(AiInterface *aiInterface, int useStartLocation) {
240 	this->aiInterface= aiInterface;
241 
242 	Faction *faction = this->aiInterface->getMyFaction();
243 	if(faction->getAIBehaviorStaticOverideValue(aibsvcMaxBuildRadius) != INT_MAX) {
244 		maxBuildRadius = faction->getAIBehaviorStaticOverideValue(aibsvcMaxBuildRadius);
245 		//printf("Discovered overriden static value for AI, maxBuildRadius = %d\n",maxBuildRadius);
246 	}
247 	if(faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriors) != INT_MAX) {
248 		minMinWarriors = faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriors);
249 		//printf("Discovered overriden static value for AI, minMinWarriors = %d\n",minMinWarriors);
250 	}
251 	if(faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuEasy) != INT_MAX) {
252 		minMinWarriorsExpandCpuEasy = faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuEasy);
253 		//printf("Discovered overriden static value for AI, minMinWarriorsExpandCpuEasy = %d\n",minMinWarriorsExpandCpuEasy);
254 	}
255 	if(faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuMega) != INT_MAX) {
256 		minMinWarriorsExpandCpuMega = faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuMega);
257 		//printf("Discovered overriden static value for AI, minMinWarriorsExpandCpuMega = %d\n",minMinWarriorsExpandCpuMega);
258 	}
259 	if(faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuUltra) != INT_MAX) {
260 		minMinWarriorsExpandCpuUltra = faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuUltra);
261 		//printf("Discovered overriden static value for AI, minMinWarriorsExpandCpuUltra = %d\n",minMinWarriorsExpandCpuUltra);
262 	}
263 	if(faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuNormal) != INT_MAX) {
264 		minMinWarriorsExpandCpuNormal = faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuNormal);
265 		//printf("Discovered overriden static value for AI, minMinWarriorsExpandCpuNormal = %d\n",minMinWarriorsExpandCpuNormal);
266 	}
267 	if(faction->getAIBehaviorStaticOverideValue(aibsvcMaxMinWarriors) != INT_MAX) {
268 		maxMinWarriors = faction->getAIBehaviorStaticOverideValue(aibsvcMaxMinWarriors);
269 		//printf("Discovered overriden static value for AI, maxMinWarriors = %d\n",maxMinWarriors);
270 	}
271 	if(faction->getAIBehaviorStaticOverideValue(aibsvcMaxExpansions) != INT_MAX) {
272 		maxExpansions = faction->getAIBehaviorStaticOverideValue(aibsvcMaxExpansions);
273 		//printf("Discovered overriden static value for AI, maxExpansions = %d\n",maxExpansions);
274 	}
275 	if(faction->getAIBehaviorStaticOverideValue(aibsvcVillageRadius) != INT_MAX) {
276 		villageRadius = faction->getAIBehaviorStaticOverideValue(aibsvcVillageRadius);
277 		//printf("Discovered overriden static value for AI, villageRadius = %d\n",villageRadius);
278 	}
279 	if(faction->getAIBehaviorStaticOverideValue(aibsvcScoutResourceRange) != INT_MAX) {
280 		scoutResourceRange = faction->getAIBehaviorStaticOverideValue(aibsvcScoutResourceRange);
281 		//printf("Discovered overriden static value for AI, scoutResourceRange = %d\n",scoutResourceRange);
282 	}
283 	if(faction->getAIBehaviorStaticOverideValue(aibsvcMinWorkerAttackersHarvesting) != INT_MAX) {
284 		minWorkerAttackersHarvesting = faction->getAIBehaviorStaticOverideValue(aibsvcMinWorkerAttackersHarvesting);
285 		//printf("Discovered overriden static value for AI, scoutResourceRange = %d\n",scoutResourceRange);
286 	}
287 	if(faction->getAIBehaviorStaticOverideValue(aibsvcMinBuildSpacing) != INT_MAX) {
288 		minBuildSpacing = faction->getAIBehaviorStaticOverideValue(aibsvcMinBuildSpacing);
289 		//printf("Discovered overriden static value for AI, scoutResourceRange = %d\n",scoutResourceRange);
290 	}
291 
292 	if(useStartLocation == -1) {
293 		startLoc = random.randRange(0, aiInterface->getMapMaxPlayers()-1);
294 	}
295 	else {
296 		startLoc = useStartLocation;
297 	}
298 	minWarriors= minMinWarriors;
299 	randomMinWarriorsReached= false;
300 	//add ai rules
301 	aiRules.clear();
302 	aiRules.push_back(new AiRuleWorkerHarvest(this));
303 	aiRules.push_back(new AiRuleRefreshHarvester(this));
304 	aiRules.push_back(new AiRuleScoutPatrol(this));
305 	aiRules.push_back(new AiRuleUnBlock(this));
306 	aiRules.push_back(new AiRuleReturnBase(this));
307 	aiRules.push_back(new AiRuleMassiveAttack(this));
308 	aiRules.push_back(new AiRuleAddTasks(this));
309 	aiRules.push_back(new AiRuleProduceResourceProducer(this));
310 	aiRules.push_back(new AiRuleBuildOneFarm(this));
311 	aiRules.push_back(new AiRuleProduce(this));
312 	aiRules.push_back(new AiRuleBuild(this));
313 	aiRules.push_back(new AiRuleUpgrade(this));
314 	aiRules.push_back(new AiRuleExpand(this));
315 	aiRules.push_back(new AiRuleRepair(this));
316 	aiRules.push_back(new AiRuleRepair(this));
317 }
318 
~Ai()319 Ai::~Ai() {
320 	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] deleting AI aiInterface [%p]\n",__FILE__,__FUNCTION__,__LINE__,aiInterface);
321 	deleteValues(tasks.begin(), tasks.end());
322 	tasks.clear();
323 	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] deleting AI aiInterface [%p]\n",__FILE__,__FUNCTION__,__LINE__,aiInterface);
324 
325 	deleteValues(aiRules.begin(), aiRules.end());
326 	aiRules.clear();
327 	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] deleting AI aiInterface [%p]\n",__FILE__,__FUNCTION__,__LINE__,aiInterface);
328 
329 	aiInterface = NULL;
330 }
331 
getRandom()332 RandomGen* Ai::getRandom() {
333 //	if(Thread::isCurrentThreadMainThread() == false) {
334 //		throw megaglest_runtime_error("Invalid access to AI random from outside main thread current id = " +
335 //				intToStr(Thread::getCurrentThreadId()) + " main = " + intToStr(Thread::getMainThreadId()));
336 //	}
337 	return &random;
338 }
339 
update()340 void Ai::update() {
341 
342 	Chrono chrono;
343 	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start();
344 
345 	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
346 
347 	if(aiInterface->getMyFaction()->getFirstSwitchTeamVote() != NULL) {
348 		const SwitchTeamVote *vote = aiInterface->getMyFaction()->getFirstSwitchTeamVote();
349 		aiInterface->getMyFaction()->setCurrentSwitchTeamVoteFactionIndex(vote->factionIndex);
350 
351 		factionSwitchTeamRequestCount[vote->factionIndex]++;
352 		int factionSwitchTeamRequestCountCurrent = factionSwitchTeamRequestCount[vote->factionIndex];
353 
354 		//int allowJoinTeam = random.randRange(0, 100);
355 		//srand(time(NULL) + aiInterface->getMyFaction()->getIndex());
356 		Chrono seed(true);
357 		srand((unsigned int)seed.getCurTicks() + aiInterface->getMyFaction()->getIndex());
358 
359 		int allowJoinTeam = rand() % 100;
360 
361 		SwitchTeamVote *voteResult = aiInterface->getMyFaction()->getSwitchTeamVote(vote->factionIndex);
362 		voteResult->voted = true;
363 		voteResult->allowSwitchTeam = false;
364 
365 		const GameSettings *settings =  aiInterface->getWorld()->getGameSettings();
366 
367 		// If AI player already lost game they cannot vote
368 		if(aiInterface->getWorld()->factionLostGame(aiInterface->getFactionIndex()) == true) {
369 			voteResult->allowSwitchTeam = true;
370 		}
371 		else {
372 			// Can only ask the AI player 2 times max per game
373 			if(factionSwitchTeamRequestCountCurrent <= 2) {
374 				// x% chance the AI will answer yes
375 				if(settings->getAiAcceptSwitchTeamPercentChance() >= 100) {
376 					voteResult->allowSwitchTeam = true;
377 				}
378 				else if(settings->getAiAcceptSwitchTeamPercentChance() <= 0) {
379 					voteResult->allowSwitchTeam = false;
380 				}
381 				else {
382 					voteResult->allowSwitchTeam = (allowJoinTeam >= (100 - settings->getAiAcceptSwitchTeamPercentChance()));
383 				}
384 			}
385 		}
386 
387 		char szBuf[8096]="";
388 		snprintf(szBuf,8096,"AI for faction# %d voted %s [%d] CountCurrent [%d] PercentChance [%d]",aiInterface->getMyFaction()->getIndex(),(voteResult->allowSwitchTeam ? "Yes" : "No"),allowJoinTeam,factionSwitchTeamRequestCountCurrent,settings->getAiAcceptSwitchTeamPercentChance());
389 		if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] %s\n",__FILE__,__FUNCTION__,__LINE__,szBuf);
390 
391 		aiInterface->printLog(3, szBuf);
392 
393 		aiInterface->giveCommandSwitchTeamVote(aiInterface->getMyFaction(),voteResult);
394 	}
395 
396 	//process ai rules
397 	for(unsigned int ruleIdx = 0; ruleIdx < aiRules.size(); ++ruleIdx) {
398 		AiRule *rule = aiRules[ruleIdx];
399 		if(rule == NULL) {
400 			throw megaglest_runtime_error("rule == NULL");
401 		}
402 
403 		if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [ruleIdx = %d]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis(),ruleIdx);
404 
405 		if((aiInterface->getTimer() % (rule->getTestInterval() * GameConstants::updateFps / 1000)) == 0) {
406 
407 			if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [ruleIdx = %d, before rule->test()]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis(),ruleIdx);
408 
409 			//printf("Testing AI Faction # %d RULE Name[%s]\n",aiInterface->getFactionIndex(),rule->getName().c_str());
410 
411 			if(rule->test()) {
412 				if(outputAIBehaviourToConsole()) printf("\n\nYYYYY Executing AI Faction # %d RULE Name[%s]\n\n",aiInterface->getFactionIndex(),rule->getName().c_str());
413 
414 				aiInterface->printLog(3, intToStr(1000 * aiInterface->getTimer() / GameConstants::updateFps) + ": Executing rule: " + rule->getName() + '\n');
415 
416 				if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [ruleIdx = %d, before rule->execute() [%s]]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis(),ruleIdx,rule->getName().c_str());
417 
418 				rule->execute();
419 
420 				if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [ruleIdx = %d, after rule->execute() [%s]]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis(),ruleIdx,rule->getName().c_str());
421 			}
422 		}
423 	}
424 
425 	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [END]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
426 }
427 
428 
429 // ==================== state requests ====================
430 
getCountOfType(const UnitType * ut)431 int Ai::getCountOfType(const UnitType *ut){
432     int count= 0;
433 	for(int i=0; i<aiInterface->getMyUnitCount(); ++i){
434 		if(ut == aiInterface->getMyUnit(i)->getType()){
435 			count++;
436 		}
437 	}
438     return count;
439 }
440 
getCountOfClass(UnitClass uc,UnitClass * additionalUnitClassToExcludeFromCount)441 int Ai::getCountOfClass(UnitClass uc,UnitClass *additionalUnitClassToExcludeFromCount) {
442     int count= 0;
443     for(int i = 0; i < aiInterface->getMyUnitCount(); ++i) {
444 		if(aiInterface->getMyUnit(i)->getType()->isOfClass(uc)) {
445 			// Skip unit if it ALSO contains the exclusion unit class type
446 			if(additionalUnitClassToExcludeFromCount != NULL) {
447 				if(aiInterface->getMyUnit(i)->getType()->isOfClass(*additionalUnitClassToExcludeFromCount)) {
448 					continue;
449 				}
450 			}
451             ++count;
452 		}
453     }
454     return count;
455 }
456 
getRatioOfClass(UnitClass uc,UnitClass * additionalUnitClassToExcludeFromCount)457 float Ai::getRatioOfClass(UnitClass uc,UnitClass *additionalUnitClassToExcludeFromCount) {
458 	if(aiInterface->getMyUnitCount() == 0) {
459 		return 0;
460 	}
461 	else {
462 		//return static_cast<float>(getCountOfClass(uc,additionalUnitClassToExcludeFromCount)) / aiInterface->getMyUnitCount();
463 		return truncateDecimal<float>(static_cast<float>(getCountOfClass(uc,additionalUnitClassToExcludeFromCount)) / aiInterface->getMyUnitCount(),6);
464 	}
465 }
466 
getNeededResource(int unitIndex)467 const ResourceType *Ai::getNeededResource(int unitIndex) {
468 	int amount = INT_MAX;
469 	const ResourceType *neededResource= NULL;
470     const TechTree *tt= aiInterface->getTechTree();
471     const Unit *unit = aiInterface->getMyUnit(unitIndex);
472 
473     for(int i = 0; i < tt->getResourceTypeCount(); ++i) {
474         const ResourceType *rt= tt->getResourceType(i);
475         const Resource *r= aiInterface->getResource(rt);
476 
477 		if( rt->getClass() != rcStatic && rt->getClass() != rcConsumable) {
478 			char szBuf[8096]="";
479 			snprintf(szBuf,8096,"Examining resource [%s] amount [%d] (previous amount [%d]",rt->getName().c_str(),r->getAmount(),amount);
480 			aiInterface->printLog(3, szBuf);
481 		}
482 
483 		if( rt->getClass() != rcStatic && rt->getClass() != rcConsumable &&
484 			r->getAmount() < amount) {
485 
486 			// Only have up to x units going for this resource so we can focus
487 			// on other needed resources for other units
488 			const int maxUnitsToHarvestResource = 5;
489 
490 			vector<int> unitsGettingResource = findUnitsHarvestingResourceType(rt);
491 			if((int)unitsGettingResource.size() <= maxUnitsToHarvestResource) {
492 				// Now MAKE SURE the unit has a harvest command for this resource
493 				// AND that the resource is within eye-sight to avoid units
494 				// standing around doing nothing.
495 				const HarvestCommandType *hct= unit->getType()->getFirstHarvestCommand(rt,unit->getFaction());
496 				Vec2i resPos;
497 				if(hct != NULL && aiInterface->getNearestSightedResource(rt, aiInterface->getHomeLocation(), resPos, false)) {
498 					amount= r->getAmount();
499 					neededResource= rt;
500 				}
501 			}
502         }
503     }
504 
505     char szBuf[8096]="";
506     snprintf(szBuf,8096,"Unit [%d - %s] looking for resources (not static or consumable)",unit->getId(),unit->getType()->getName(false).c_str());
507     aiInterface->printLog(3, szBuf);
508     snprintf(szBuf,8096,"[resource type count %d] Needed resource [%s].",tt->getResourceTypeCount(),(neededResource != NULL ? neededResource->getName().c_str() : "<none>"));
509     aiInterface->printLog(3, szBuf);
510 
511     return neededResource;
512 }
513 
beingAttacked(Vec2i & pos,Field & field,int radius)514 bool Ai::beingAttacked(Vec2i &pos, Field &field, int radius){
515 	const Unit *enemy = aiInterface->getFirstOnSightEnemyUnit(pos, field, radius);
516 	return (enemy != NULL);
517 }
518 
isStableBase()519 bool Ai::isStableBase() {
520 	UnitClass ucWorkerType = ucWorker;
521     if(getCountOfClass(ucWarrior,&ucWorkerType) > minWarriors) {
522         char szBuf[8096]="";
523         snprintf(szBuf,8096,"Base is stable [minWarriors = %d found = %d]",minWarriors,ucWorkerType);
524         aiInterface->printLog(4, szBuf);
525 
526         return true;
527     }
528     else{
529         char szBuf[8096]="";
530         snprintf(szBuf,8096,"Base is NOT stable [minWarriors = %d found = %d]",minWarriors,ucWorkerType);
531         aiInterface->printLog(4, szBuf);
532 
533         return false;
534     }
535 }
536 
findAbleUnit(int * unitIndex,CommandClass ability,bool idleOnly)537 bool Ai::findAbleUnit(int *unitIndex, CommandClass ability, bool idleOnly){
538 	vector<int> units;
539 
540 	*unitIndex= -1;
541 	for(int i=0; i<aiInterface->getMyUnitCount(); ++i){
542 		const Unit *unit= aiInterface->getMyUnit(i);
543 		if(unit->getType()->isCommandable() && unit->getType()->hasCommandClass(ability)){
544 			if(!idleOnly || !unit->anyCommand() || unit->getCurrCommand()->getCommandType()->getClass()==ccStop){
545 				units.push_back(i);
546 			}
547 		}
548 	}
549 
550 	if(units.empty()){
551 		return false;
552 	}
553 	else{
554 		*unitIndex= units[random.randRange(0, (int)units.size()-1)];
555 		return true;
556 	}
557 }
558 
findUnitsHarvestingResourceType(const ResourceType * rt)559 vector<int> Ai::findUnitsHarvestingResourceType(const ResourceType *rt) {
560 	vector<int> units;
561 
562 	Map *map= aiInterface->getMap();
563 	for(int i = 0; i < aiInterface->getMyUnitCount(); ++i) {
564 		const Unit *unit= aiInterface->getMyUnit(i);
565 		if(unit->getType()->isCommandable()) {
566 			if(unit->getType()->hasCommandClass(ccHarvest)) {
567 				if(unit->anyCommand() && unit->getCurrCommand()->getCommandType()->getClass() == ccHarvest) {
568 					Command *command= unit->getCurrCommand();
569 					const HarvestCommandType *hct= dynamic_cast<const HarvestCommandType*>(command->getCommandType());
570 					if(hct != NULL) {
571 						const Vec2i unitTargetPos = unit->getTargetPos();
572 						SurfaceCell *sc= map->getSurfaceCell(Map::toSurfCoords(unitTargetPos));
573 						Resource *r= sc->getResource();
574 						if (r != NULL && r->getType() == rt) {
575 							units.push_back(i);
576 						}
577 					}
578 				}
579 			}
580 			else if(unit->getType()->hasCommandClass(ccProduce)) {
581 				if(unit->anyCommand() && unit->getCurrCommand()->getCommandType()->getClass() == ccProduce) {
582 					Command *command= unit->getCurrCommand();
583 					const ProduceCommandType *pct= dynamic_cast<const ProduceCommandType*>(command->getCommandType());
584 					if(pct != NULL) {
585 						const UnitType *ut = pct->getProducedUnit();
586 						if(ut != NULL) {
587 							const Resource *r = ut->getCost(rt);
588 							if(r != NULL) {
589 								if (r != NULL && r->getAmount() < 0) {
590 									units.push_back(i);
591 								}
592 							}
593 						}
594 					}
595 				}
596 			}
597 			else if(unit->getType()->hasCommandClass(ccBuild)) {
598 				if(unit->anyCommand() && unit->getCurrCommand()->getCommandType()->getClass() == ccBuild) {
599 					Command *command= unit->getCurrCommand();
600 					const BuildCommandType *bct= dynamic_cast<const BuildCommandType*>(command->getCommandType());
601 					if(bct != NULL) {
602 						for(int j = 0; j < bct->getBuildingCount(); ++j) {
603 							const UnitType *ut = bct->getBuilding(j);
604 							if(ut != NULL) {
605 								const Resource *r = ut->getCost(rt);
606 								if(r != NULL) {
607 									if (r != NULL && r->getAmount() < 0) {
608 										units.push_back(i);
609 										break;
610 									}
611 								}
612 							}
613 						}
614 					}
615 				}
616 			}
617 		}
618 	}
619 
620 	return units;
621 }
622 
findUnitsDoingCommand(CommandClass currentCommand)623 vector<int> Ai::findUnitsDoingCommand(CommandClass currentCommand) {
624 	vector<int> units;
625 
626 	for(int i = 0; i < aiInterface->getMyUnitCount(); ++i) {
627 		const Unit *unit= aiInterface->getMyUnit(i);
628 		if(unit->getType()->isCommandable() && unit->getType()->hasCommandClass(currentCommand)) {
629 			if(unit->anyCommand() && unit->getCurrCommand()->getCommandType()->getClass() == currentCommand) {
630 				units.push_back(i);
631 			}
632 		}
633 	}
634 
635 	return units;
636 }
637 
findAbleUnit(int * unitIndex,CommandClass ability,CommandClass currentCommand)638 bool Ai::findAbleUnit(int *unitIndex, CommandClass ability, CommandClass currentCommand){
639 	vector<int> units;
640 
641 	*unitIndex= -1;
642 	for(int i=0; i<aiInterface->getMyUnitCount(); ++i){
643 		const Unit *unit= aiInterface->getMyUnit(i);
644 		if(unit->getType()->isCommandable() && unit->getType()->hasCommandClass(ability)){
645 			if(unit->anyCommand() && unit->getCurrCommand()->getCommandType()->getClass()==currentCommand){
646 				units.push_back(i);
647 			}
648 		}
649 	}
650 
651 	if(units.empty()){
652 		return false;
653 	}
654 	else{
655 		*unitIndex= units[random.randRange(0, (int)units.size()-1)];
656 		return true;
657 	}
658 }
659 
findPosForBuilding(const UnitType * building,const Vec2i & searchPos,Vec2i & outPos)660 bool Ai::findPosForBuilding(const UnitType* building, const Vec2i &searchPos, Vec2i &outPos){
661 
662     for(int currRadius = 0; currRadius < maxBuildRadius; ++currRadius) {
663         for(int i=searchPos.x - currRadius; i < searchPos.x + currRadius; ++i) {
664             for(int j=searchPos.y - currRadius; j < searchPos.y + currRadius; ++j) {
665                 outPos= Vec2i(i, j);
666                 if(aiInterface->isFreeCells(outPos - Vec2i(minBuildSpacing), building->getAiBuildSize() + minBuildSpacing * 2, fLand)) {
667                 	int aiBuildSizeDiff= building->getAiBuildSize()- building->getSize();
668                 	if( aiBuildSizeDiff>0){
669                 		int halfSize=aiBuildSizeDiff/2;
670                 		outPos.x+=halfSize;
671                 		outPos.y+=halfSize;
672                 	}
673                		return true;
674                 }
675             }
676         }
677     }
678 
679     return false;
680 
681 }
682 
683 
684 // ==================== tasks ====================
685 
addTask(const Task * task)686 void Ai::addTask(const Task *task){
687 	tasks.push_back(task);
688 	aiInterface->printLog(2, "Task added: " + task->toString());
689 }
690 
addPriorityTask(const Task * task)691 void Ai::addPriorityTask(const Task *task){
692 	deleteValues(tasks.begin(), tasks.end());
693 	tasks.clear();
694 
695 	tasks.push_back(task);
696 	aiInterface->printLog(2, "Priority Task added: " + task->toString());
697 }
698 
anyTask()699 bool Ai::anyTask(){
700 	return !tasks.empty();
701 }
702 
getTask() const703 const Task *Ai::getTask() const{
704 	if(tasks.empty()){
705 		return NULL;
706 	}
707 	else{
708 		return tasks.front();
709 	}
710 }
711 
removeTask(const Task * task)712 void Ai::removeTask(const Task *task){
713 	aiInterface->printLog(2, "Task removed: " + task->toString());
714 	tasks.remove(task);
715 	delete task;
716 }
717 
retryTask(const Task * task)718 void Ai::retryTask(const Task *task){
719 	tasks.remove(task);
720 	tasks.push_back(task);
721 }
722 // ==================== expansions ====================
723 
addExpansion(const Vec2i & pos)724 void Ai::addExpansion(const Vec2i &pos) {
725 
726 	//check if there is a nearby expansion
727 	for(Positions::iterator it = expansionPositions.begin(); it != expansionPositions.end(); ++it) {
728 		if((*it).dist(pos) < villageRadius) {
729 			return;
730 		}
731 	}
732 
733 	//add expansion
734 	expansionPositions.push_front(pos);
735 
736 	//remove expansion if queue is list is full
737 	if((int)expansionPositions.size() > maxExpansions){
738 		expansionPositions.pop_back();
739 	}
740 }
741 
getRandomHomePosition()742 Vec2i Ai::getRandomHomePosition() {
743 
744 	if(expansionPositions.empty() || random.randRange(0, 1) == 0){
745 		return aiInterface->getHomeLocation();
746 	}
747 
748 	return expansionPositions[random.randRange(0, (int)expansionPositions.size()-1)];
749 }
750 
751 // ==================== actions ====================
752 
sendScoutPatrol()753 void Ai::sendScoutPatrol(){
754 
755 	Vec2i pos;
756 	int unit;
757 	bool possibleTargetFound= false;
758 
759 	bool ultraResourceAttack= (aiInterface->getControlType() == ctCpuUltra || aiInterface->getControlType() == ctNetworkCpuUltra)
760 	        && random.randRange(0, 2) == 1;
761 	bool megaResourceAttack=(aiInterface->getControlType() == ctCpuMega || aiInterface->getControlType() == ctNetworkCpuMega)
762 			&& random.randRange(0, 1) == 1;
763 
764 	if(possibleTargetFound == false && (megaResourceAttack || ultraResourceAttack)) {
765 		Map *map= aiInterface->getMap();
766 
767 		const TechTree *tt= aiInterface->getTechTree();
768 		const ResourceType *rt= tt->getResourceType(0);
769 		int tryCount= 0;
770 		int height= map->getH();
771 		int width= map->getW();
772 
773 		for(int i= 0; i < tt->getResourceTypeCount(); ++i){
774 			const ResourceType *rt_= tt->getResourceType(i);
775 			//const Resource *r= aiInterface->getResource(rt);
776 
777 			if(rt_->getClass() == rcTech){
778 				rt=rt_;
779 				break;
780 			}
781 		}
782 		//printf("looking for resource %s\n",rt->getName().c_str());
783 		while(possibleTargetFound == false){
784 			tryCount++;
785 			if(tryCount == 4){
786 				//printf("no target found\n");
787 				break;
788 			}
789 			pos= Vec2i(random.randRange(2, width - 2), random.randRange(2, height - 2));
790 			if(map->isInside(pos) && map->isInsideSurface(map->toSurfCoords(pos))){
791 				//printf("is inside map\n");
792 				// find first resource in this area
793 				Vec2i resPos;
794 				if(aiInterface->isResourceInRegion(pos, rt, resPos, scoutResourceRange)){
795 					// found a possible target.
796 					pos= resPos;
797 					//printf("lets try the new target\n");
798 					possibleTargetFound= true;
799 					break;
800 				}
801 			}
802 			//else printf("is outside map\n");
803 		}
804 	}
805 
806 	std::vector<Vec2i> warningEnemyList = aiInterface->getEnemyWarningPositionList();
807 	if( (possibleTargetFound == false) && (warningEnemyList.empty() == false)) {
808 		//for(int i = (int)warningEnemyList.size() - 1; i <= 0; --i) {
809 			//Vec2i &checkPos = warningEnemyList[i];
810 			Vec2i &checkPos = warningEnemyList[0];
811 			if (random.randRange(0, 1) == 1 ) {
812 				pos = checkPos;
813 				possibleTargetFound = true;
814 				warningEnemyList.clear();
815 			} else {
816 				aiInterface->removeEnemyWarningPositionFromList(checkPos);
817 			}
818 			//break;
819 		//}
820 	}
821 
822 	if(possibleTargetFound == false){
823 		startLoc= (startLoc + 1) % aiInterface->getMapMaxPlayers();
824 		pos= aiInterface->getStartLocation(startLoc);
825 		//printf("normal target used\n");
826 	}
827 
828 	if(aiInterface->getHomeLocation() != pos){
829 		if(findAbleUnit(&unit, ccAttack, false)){
830 			if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled)
831 				SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
832 
833 			aiInterface->giveCommand(unit, ccAttack, pos);
834 			aiInterface->printLog(2, "Scout patrol sent to: " + intToStr(pos.x) + "," + intToStr(pos.y) + "\n");
835 		}
836 	}
837 }
838 
massiveAttack(const Vec2i & pos,Field field,bool ultraAttack)839 void Ai::massiveAttack(const Vec2i &pos, Field field, bool ultraAttack){
840 	int producerWarriorCount=0;
841 	int maxProducerWarriors=random.randRange(1,11);
842 	int unitCount = aiInterface->getMyUnitCount();
843 	int unitGroupCommandId = -1;
844 
845 	int attackerWorkersHarvestingCount = 0;
846     for(int i = 0; i < unitCount; ++i) {
847     	bool isWarrior=false;
848     	bool productionInProgress=false;
849         const Unit *unit= aiInterface->getMyUnit(i);
850 		const AttackCommandType *act= unit->getType()->getFirstAttackCommand(field);
851 
852 		if(	aiInterface->getControlType() == ctCpuMega ||
853 			aiInterface->getControlType() == ctNetworkCpuMega) {
854 			if(producerWarriorCount > maxProducerWarriors) {
855 				if(
856 					unit->getCommandSize()>0 &&
857 					unit->getCurrCommand()->getCommandType()!=NULL && (
858 				    unit->getCurrCommand()->getCommandType()->getClass()==ccBuild ||
859 					unit->getCurrCommand()->getCommandType()->getClass()==ccMorph ||
860 					unit->getCurrCommand()->getCommandType()->getClass()==ccProduce
861 					)
862 				 ) {
863 					productionInProgress=true;
864 					isWarrior=false;
865 					producerWarriorCount++;
866 				}
867 				else {
868 					isWarrior =! unit->getType()->hasCommandClass(ccHarvest);
869 				}
870 
871 			}
872 			else {
873 				isWarrior= !unit->getType()->hasCommandClass(ccHarvest) && !unit->getType()->hasCommandClass(ccProduce);
874 			}
875 		}
876 		else {
877 			isWarrior= !unit->getType()->hasCommandClass(ccHarvest) && !unit->getType()->hasCommandClass(ccProduce);
878 		}
879 
880 		bool alreadyAttacking= (unit->getCurrSkill()->getClass() == scAttack);
881 
882 		bool unitSignalledToAttack = false;
883 		if(alreadyAttacking == false && unit->getType()->hasSkillClass(scAttack) && (aiInterface->getControlType()
884 		        == ctCpuUltra || aiInterface->getControlType() == ctCpuMega || aiInterface->getControlType()
885 		        == ctNetworkCpuUltra || aiInterface->getControlType() == ctNetworkCpuMega)){
886 			//printf("~~~~~~~~ Unit [%s - %d] checking if unit is being attacked\n",unit->getFullName().c_str(),unit->getId());
887 
888 			std::pair<bool, Unit *> beingAttacked= aiInterface->getWorld()->getUnitUpdater()->unitBeingAttacked(unit);
889 			if(beingAttacked.first == true){
890 				Unit *enemy= beingAttacked.second;
891 				const AttackCommandType *act_forenemy= unit->getType()->getFirstAttackCommand(enemy->getCurrField());
892 
893 				//printf("~~~~~~~~ Unit [%s - %d] attacked by enemy [%s - %d] act_forenemy [%p] enemy->getCurrField() = %d\n",unit->getFullName().c_str(),unit->getId(),enemy->getFullName().c_str(),enemy->getId(),act_forenemy,enemy->getCurrField());
894 
895 				if(act_forenemy != NULL){
896 					bool shouldAttack= true;
897 					if(unit->getType()->hasSkillClass(scHarvest)){
898 						shouldAttack= (attackerWorkersHarvestingCount > minWorkerAttackersHarvesting);
899 						if(shouldAttack == false){
900 							attackerWorkersHarvestingCount++;
901 						}
902 					}
903 					if(shouldAttack) {
904 						if(unitGroupCommandId == -1) {
905 							unitGroupCommandId = aiInterface->getWorld()->getNextCommandGroupId();
906 						}
907 
908 						aiInterface->giveCommand(i, act_forenemy, beingAttacked.second->getPos(), unitGroupCommandId);
909 						unitSignalledToAttack= true;
910 					}
911 				}
912 				else{
913 					const AttackStoppedCommandType *asct_forenemy= unit->getType()->getFirstAttackStoppedCommand(
914 					        enemy->getCurrField());
915 					//printf("~~~~~~~~ Unit [%s - %d] found enemy [%s - %d] asct_forenemy [%p] enemy->getCurrField() = %d\n",unit->getFullName().c_str(),unit->getId(),enemy->getFullName().c_str(),enemy->getId(),asct_forenemy,enemy->getCurrField());
916 					if(asct_forenemy != NULL){
917 						bool shouldAttack= true;
918 						if(unit->getType()->hasSkillClass(scHarvest)){
919 							shouldAttack= (attackerWorkersHarvestingCount > minWorkerAttackersHarvesting);
920 							if(shouldAttack == false){
921 								attackerWorkersHarvestingCount++;
922 							}
923 						}
924 						if(shouldAttack){
925 //								printf("~~~~~~~~ Unit [%s - %d] WILL AttackStoppedCommand [%s - %d]\n", unit->getFullName().c_str(),
926 //								        unit->getId(), enemy->getFullName().c_str(), enemy->getId());
927 
928 							if(unitGroupCommandId == -1) {
929 								unitGroupCommandId = aiInterface->getWorld()->getNextCommandGroupId();
930 							}
931 
932 							aiInterface->giveCommand(i, asct_forenemy, beingAttacked.second->getCenteredPos(), unitGroupCommandId);
933 							unitSignalledToAttack= true;
934 						}
935 					}
936 				}
937 			}
938 		}
939 		if(alreadyAttacking == false && act != NULL && (ultraAttack || isWarrior) &&
940 			unitSignalledToAttack == false) {
941 			bool shouldAttack = true;
942 			if(unit->getType()->hasSkillClass(scHarvest)) {
943 				shouldAttack = (attackerWorkersHarvestingCount > minWorkerAttackersHarvesting);
944 				if(shouldAttack == false) {
945 					attackerWorkersHarvestingCount++;
946 				}
947 			}
948 
949 			// Mega CPU does not send ( far away ) units which are currently producing something
950 			if(aiInterface->getControlType() == ctCpuMega || aiInterface->getControlType() == ctNetworkCpuMega){
951 				if(!isWarrior ){
952 					if(!productionInProgress){
953 						shouldAttack= false;
954 						//printf("no attack \n ");
955 					}
956 				}
957 			}
958 			if(shouldAttack) {
959 				if(unitGroupCommandId == -1) {
960 					unitGroupCommandId = aiInterface->getWorld()->getNextCommandGroupId();
961 				}
962 
963 				aiInterface->giveCommand(i, act, pos, unitGroupCommandId);
964 			}
965 		}
966     }
967 
968     if(	aiInterface->getControlType() == ctCpuEasy ||
969     	aiInterface->getControlType() == ctNetworkCpuEasy) {
970 		minWarriors += minMinWarriorsExpandCpuEasy;
971 	}
972 	else if(aiInterface->getControlType() == ctCpuMega ||
973 			aiInterface->getControlType() == ctNetworkCpuMega) {
974 		minWarriors += minMinWarriorsExpandCpuMega;
975 		if(minWarriors > maxMinWarriors-1 || randomMinWarriorsReached) {
976 			randomMinWarriorsReached=true;
977 			minWarriors=random.randRange(maxMinWarriors-10, maxMinWarriors*2);
978 		}
979 	}
980 	else if(minWarriors < maxMinWarriors) {
981 	    if(aiInterface->getControlType() == ctCpuUltra ||
982 	    	aiInterface->getControlType() == ctNetworkCpuUltra) {
983     		minWarriors += minMinWarriorsExpandCpuUltra;
984 		}
985 	    else {
986 	    	minWarriors+= minMinWarriorsExpandCpuNormal;
987 	    }
988 	}
989 	aiInterface->printLog(2, "Massive attack to pos: "+ intToStr(pos.x)+", "+intToStr(pos.y)+"\n");
990 }
991 
returnBase(int unitIndex)992 void Ai::returnBase(int unitIndex) {
993     Vec2i pos;
994     //std::pair<CommandResult,string> r(crFailUndefined,"");
995     //aiInterface->getFactionIndex();
996     pos= Vec2i(
997 		random.randRange(-villageRadius, villageRadius),
998 		random.randRange(-villageRadius, villageRadius)) +
999 		                 getRandomHomePosition();
1000 
1001     if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
1002     //r= aiInterface->giveCommand(unitIndex, ccMove, pos);
1003     aiInterface->giveCommand(unitIndex, ccMove, pos);
1004 
1005     //aiInterface->printLog(1, "Order return to base pos:" + intToStr(pos.x)+", "+intToStr(pos.y)+": "+rrToStr(r)+"\n");
1006 }
1007 
harvest(int unitIndex)1008 void Ai::harvest(int unitIndex) {
1009 	const ResourceType *rt= getNeededResource(unitIndex);
1010 	if(rt != NULL) {
1011 		const HarvestCommandType *hct= aiInterface->getMyUnit(unitIndex)->getType()->getFirstHarvestCommand(rt,aiInterface->getMyUnit(unitIndex)->getFaction());
1012 
1013 		Vec2i resPos;
1014 		if(hct != NULL && aiInterface->getNearestSightedResource(rt, aiInterface->getHomeLocation(), resPos, false)) {
1015 			resPos= resPos+Vec2i(random.randRange(-2, 2), random.randRange(-2, 2));
1016 			aiInterface->giveCommand(unitIndex, hct, resPos, -1);
1017 			//aiInterface->printLog(4, "Order harvest pos:" + intToStr(resPos.x)+", "+intToStr(resPos.y)+": "+rrToStr(r)+"\n");
1018 		}
1019 	}
1020 }
1021 
haveBlockedUnits()1022 bool Ai::haveBlockedUnits() {
1023 	Chrono chrono;
1024 	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start();
1025 
1026 	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1027 
1028 	int unitCount = aiInterface->getMyUnitCount();
1029 	Map *map = aiInterface->getMap();
1030 	//If there is no close store
1031 	for(int j=0; j < unitCount; ++j) {
1032 		const Unit *u= aiInterface->getMyUnit(j);
1033 		const UnitType *ut= u->getType();
1034 
1035 		// If this building is a store
1036 		if(u->isAlive() && ut->isMobile() && u->getPath() != NULL && (u->getPath()->isBlocked() || u->getPath()->getBlockCount())) {
1037 			Vec2i unitPos = u->getPosNotThreadSafe();
1038 
1039 			//printf("#1 AI found blocked unit [%d - %s]\n",u->getId(),u->getFullName().c_str());
1040 
1041 			int failureCount = 0;
1042 			int cellCount = 0;
1043 
1044 			for(int i = -1; i <= 1; ++i) {
1045 				for(int j = -1; j <= 1; ++j) {
1046 					Vec2i pos = unitPos + Vec2i(i, j);
1047 					if(map->isInside(pos) && map->isInsideSurface(map->toSurfCoords(pos))) {
1048 						if(pos != unitPos) {
1049 							bool canUnitMoveToCell = map->aproxCanMove(u, unitPos, pos);
1050 							if(canUnitMoveToCell == false) {
1051 								failureCount++;
1052 							}
1053 							cellCount++;
1054 						}
1055 					}
1056 				}
1057 			}
1058 			bool unitImmediatelyBlocked = (failureCount == cellCount);
1059 			//printf("#1 unitImmediatelyBlocked = %d, failureCount = %d, cellCount = %d\n",unitImmediatelyBlocked,failureCount,cellCount);
1060 
1061 			if(unitImmediatelyBlocked) {
1062 				//printf("#1 AI unit IS BLOCKED [%d - %s]\n",u->getId(),u->getFullName().c_str());
1063 				if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1064 				return true;
1065 			}
1066 		}
1067 	}
1068 
1069 	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1070 	return false;
1071 }
1072 
getAdjacentUnits(std::map<float,std::map<int,const Unit * >> & signalAdjacentUnits,const Unit * unit)1073 bool Ai::getAdjacentUnits(std::map<float, std::map<int, const Unit *> > &signalAdjacentUnits, const Unit *unit) {
1074 	//printf("In getAdjacentUnits...\n");
1075 
1076 	bool result = false;
1077 	Map *map = aiInterface->getMap();
1078 	Vec2i unitPos = unit->getPosNotThreadSafe();
1079 	for(int i = -1; i <= 1; ++i) {
1080 		for(int j = -1; j <= 1; ++j) {
1081 			Vec2i pos = unitPos + Vec2i(i, j);
1082 			if(map->isInside(pos) && map->isInsideSurface(map->toSurfCoords(pos))) {
1083 				if(pos != unitPos) {
1084 					Unit *adjacentUnit = map->getCell(pos)->getUnit(unit->getCurrField());
1085 					if(adjacentUnit != NULL && adjacentUnit->getFactionIndex() == unit->getFactionIndex()) {
1086 						if(adjacentUnit->getType()->isMobile() && adjacentUnit->getPath() != NULL) {
1087 							//signalAdjacentUnits.push_back(adjacentUnit);
1088 							float dist = unitPos.dist(adjacentUnit->getPos());
1089 
1090 							std::map<float, std::map<int, const Unit *> >::iterator iterFind1 = signalAdjacentUnits.find(dist);
1091 							if(iterFind1 == signalAdjacentUnits.end()) {
1092 								signalAdjacentUnits[dist][adjacentUnit->getId()] = adjacentUnit;
1093 
1094 								getAdjacentUnits(signalAdjacentUnits, adjacentUnit);
1095 								result = true;
1096 							}
1097 							else {
1098 								std::map<int, const Unit *>::iterator iterFind2 = iterFind1->second.find(adjacentUnit->getId());
1099 								if(iterFind2 == iterFind1->second.end()) {
1100 									signalAdjacentUnits[dist][adjacentUnit->getId()] = adjacentUnit;
1101 									getAdjacentUnits(signalAdjacentUnits, adjacentUnit);
1102 									result = true;
1103 								}
1104 							}
1105 						}
1106 					}
1107 				}
1108 			}
1109 		}
1110 	}
1111 	return result;
1112 }
1113 
unblockUnits()1114 void Ai::unblockUnits() {
1115 	Chrono chrono;
1116 	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start();
1117 
1118 	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1119 
1120 	int unitCount = aiInterface->getMyUnitCount();
1121 	Map *map = aiInterface->getMap();
1122 	// Find blocked units and move surrounding units out of the way
1123 	std::map<float, std::map<int, const Unit *> > signalAdjacentUnits;
1124 	for(int idx=0; idx < unitCount; ++idx) {
1125 		const Unit *u= aiInterface->getMyUnit(idx);
1126 		const UnitType *ut= u->getType();
1127 
1128 		// If this building is a store
1129 		if(u->isAlive() && ut->isMobile() && u->getPath() != NULL && (u->getPath()->isBlocked() || u->getPath()->getBlockCount())) {
1130 			Vec2i unitPos = u->getPosNotThreadSafe();
1131 
1132 			//printf("#2 AI found blocked unit [%d - %s]\n",u->getId(),u->getFullName().c_str());
1133 
1134 			int failureCount = 0;
1135 			int cellCount = 0;
1136 
1137 			for(int i = -1; i <= 1; ++i) {
1138 				for(int j = -1; j <= 1; ++j) {
1139 					Vec2i pos = unitPos + Vec2i(i, j);
1140 					if(map->isInside(pos) && map->isInsideSurface(map->toSurfCoords(pos))) {
1141 						if(pos != unitPos) {
1142 							bool canUnitMoveToCell = map->aproxCanMove(u, unitPos, pos);
1143 							if(canUnitMoveToCell == false) {
1144 								failureCount++;
1145 								getAdjacentUnits(signalAdjacentUnits, u);
1146 							}
1147 							cellCount++;
1148 						}
1149 					}
1150 				}
1151 			}
1152 			//bool unitImmediatelyBlocked = (failureCount == cellCount);
1153 			//printf("#2 unitImmediatelyBlocked = %d, failureCount = %d, cellCount = %d, signalAdjacentUnits.size() = %d\n",unitImmediatelyBlocked,failureCount,cellCount,signalAdjacentUnits.size());
1154 		}
1155 	}
1156 
1157 	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1158 
1159 	if(signalAdjacentUnits.empty() == false) {
1160 		//printf("#2 AI units ARE BLOCKED about to unblock\n");
1161 
1162 		int unitGroupCommandId = -1;
1163 
1164 		for(std::map<float, std::map<int, const Unit *> >::reverse_iterator iterMap = signalAdjacentUnits.rbegin();
1165 			iterMap != signalAdjacentUnits.rend(); ++iterMap) {
1166 
1167 			for(std::map<int, const Unit *>::iterator iterMap2 = iterMap->second.begin();
1168 				iterMap2 != iterMap->second.end(); ++iterMap2) {
1169 				//int idx = iterMap2->first;
1170 				const Unit *adjacentUnit = iterMap2->second;
1171 				if(adjacentUnit != NULL && adjacentUnit->getType()->getFirstCtOfClass(ccMove) != NULL) {
1172 					const CommandType *ct = adjacentUnit->getType()->getFirstCtOfClass(ccMove);
1173 
1174 					for(int moveAttempt = 1; moveAttempt <= villageRadius; ++moveAttempt) {
1175 						Vec2i pos= Vec2i(
1176 							random.randRange(-villageRadius*2, villageRadius*2),
1177 							random.randRange(-villageRadius*2, villageRadius*2)) +
1178 											 adjacentUnit->getPosNotThreadSafe();
1179 
1180 						bool canUnitMoveToCell = map->aproxCanMove(adjacentUnit, adjacentUnit->getPosNotThreadSafe(), pos);
1181 						if(canUnitMoveToCell == true) {
1182 
1183 							if(ct != NULL) {
1184 								if(unitGroupCommandId == -1) {
1185 									unitGroupCommandId = aiInterface->getWorld()->getNextCommandGroupId();
1186 								}
1187 
1188 								//std::pair<CommandResult,string> r = aiInterface->giveCommand(adjacentUnit,ct, pos, unitGroupCommandId);
1189 								aiInterface->giveCommand(adjacentUnit,ct, pos, unitGroupCommandId);
1190 							}
1191 						}
1192 					}
1193 				}
1194 			}
1195 		}
1196 	}
1197 
1198 	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1199 }
1200 
outputAIBehaviourToConsole() const1201 bool Ai::outputAIBehaviourToConsole() const {
1202 	return false;
1203 }
1204 
saveGame(XmlNode * rootNode) const1205 void Ai::saveGame(XmlNode *rootNode) const {
1206 	std::map<string,string> mapTagReplacements;
1207 	XmlNode *aiNode = rootNode->addChild("Ai");
1208 
1209 //    AiInterface *aiInterface;
1210 //	AiRules aiRules;
1211 //    int startLoc;
1212 	aiNode->addAttribute("startLoc",intToStr(startLoc), mapTagReplacements);
1213 //    bool randomMinWarriorsReached;
1214 	aiNode->addAttribute("randomMinWarriorsReached",intToStr(randomMinWarriorsReached), mapTagReplacements);
1215 //	Tasks tasks;
1216 	for(Tasks::const_iterator it = tasks.begin(); it != tasks.end(); ++it) {
1217 		(*it)->saveGame(aiNode);
1218 	}
1219 //	Positions expansionPositions;
1220 	for(Positions::const_iterator it = expansionPositions.begin(); it != expansionPositions.end(); ++it) {
1221 		XmlNode *expansionPositionsNode = aiNode->addChild("expansionPositions");
1222 		expansionPositionsNode->addAttribute("pos",(*it).getString(), mapTagReplacements);
1223 	}
1224 
1225 //	RandomGen random;
1226 	aiNode->addAttribute("random",intToStr(random.getLastNumber()), mapTagReplacements);
1227 //	std::map<int,int> factionSwitchTeamRequestCount;
1228 
1229 //	int maxBuildRadius;
1230 	aiNode->addAttribute("maxBuildRadius",intToStr(maxBuildRadius), mapTagReplacements);
1231 //	int minMinWarriors;
1232 	aiNode->addAttribute("minMinWarriors",intToStr(minMinWarriors), mapTagReplacements);
1233 //	int minMinWarriorsExpandCpuEasy;
1234 	aiNode->addAttribute("minMinWarriorsExpandCpuEasy",intToStr(minMinWarriorsExpandCpuEasy), mapTagReplacements);
1235 //	int minMinWarriorsExpandCpuMega;
1236 	aiNode->addAttribute("minMinWarriorsExpandCpuMega",intToStr(minMinWarriorsExpandCpuMega), mapTagReplacements);
1237 //	int minMinWarriorsExpandCpuUltra;
1238 	aiNode->addAttribute("minMinWarriorsExpandCpuUltra",intToStr(minMinWarriorsExpandCpuUltra), mapTagReplacements);
1239 //	int minMinWarriorsExpandCpuNormal;
1240 	aiNode->addAttribute("minMinWarriorsExpandCpuNormal",intToStr(minMinWarriorsExpandCpuNormal), mapTagReplacements);
1241 //	int maxMinWarriors;
1242 	aiNode->addAttribute("maxMinWarriors",intToStr(maxMinWarriors), mapTagReplacements);
1243 //	int maxExpansions;
1244 	aiNode->addAttribute("maxExpansions",intToStr(maxExpansions), mapTagReplacements);
1245 //	int villageRadius;
1246 	aiNode->addAttribute("villageRadius",intToStr(villageRadius), mapTagReplacements);
1247 //	int scoutResourceRange;
1248 	aiNode->addAttribute("scoutResourceRange",intToStr(scoutResourceRange), mapTagReplacements);
1249 //	int minWorkerAttackersHarvesting;
1250 	aiNode->addAttribute("minWorkerAttackersHarvesting",intToStr(minWorkerAttackersHarvesting), mapTagReplacements);
1251 }
1252 
loadGame(const XmlNode * rootNode,Faction * faction)1253 void Ai::loadGame(const XmlNode *rootNode, Faction *faction) {
1254 	const XmlNode *aiNode = rootNode->getChild("Ai");
1255 
1256 	startLoc = aiNode->getAttribute("startLoc")->getIntValue();
1257 	randomMinWarriorsReached = aiNode->getAttribute("randomMinWarriorsReached")->getIntValue() != 0;
1258 
1259 	vector<XmlNode *> taskNodeList = aiNode->getChildList("Task");
1260 	for(unsigned int i = 0; i < taskNodeList.size(); ++i) {
1261 		XmlNode *taskNode = taskNodeList[i];
1262 		TaskClass taskClass = static_cast<TaskClass>(taskNode->getAttribute("taskClass")->getIntValue());
1263 		switch(taskClass) {
1264 			case tcProduce:
1265 				{
1266 				ProduceTask *newTask = ProduceTask::loadGame(taskNode, faction);
1267 				tasks.push_back(newTask);
1268 				}
1269 				break;
1270 			case tcBuild:
1271 				{
1272 				BuildTask *newTask = BuildTask::loadGame(taskNode, faction);
1273 				tasks.push_back(newTask);
1274 				}
1275 				break;
1276 			case tcUpgrade:
1277 				{
1278 				UpgradeTask *newTask = UpgradeTask::loadGame(taskNode, faction);
1279 				tasks.push_back(newTask);
1280 				}
1281 				break;
1282 		}
1283 	}
1284 
1285 	vector<XmlNode *> expansionPositionsNodeList = aiNode->getChildList("expansionPositions");
1286 	for(unsigned int i = 0; i < expansionPositionsNodeList.size(); ++i) {
1287 		XmlNode *expansionPositionsNode = expansionPositionsNodeList[i];
1288 		Vec2i pos = Vec2i::strToVec2(expansionPositionsNode->getAttribute("pos")->getValue());
1289 		expansionPositions.push_back(pos);
1290 	}
1291 
1292 	//	RandomGen random;
1293 	random.setLastNumber(aiNode->getAttribute("random")->getIntValue());
1294 	//	std::map<int,int> factionSwitchTeamRequestCount;
1295 
1296 	//	int maxBuildRadius;
1297 	maxBuildRadius = aiNode->getAttribute("maxBuildRadius")->getIntValue();
1298 	//	int minMinWarriors;
1299 	minMinWarriors = aiNode->getAttribute("minMinWarriors")->getIntValue();
1300 	//	int minMinWarriorsExpandCpuEasy;
1301 	minMinWarriorsExpandCpuEasy = aiNode->getAttribute("minMinWarriorsExpandCpuEasy")->getIntValue();
1302 	//	int minMinWarriorsExpandCpuMega;
1303 	minMinWarriorsExpandCpuMega = aiNode->getAttribute("minMinWarriorsExpandCpuMega")->getIntValue();
1304 	//	int minMinWarriorsExpandCpuUltra;
1305 	minMinWarriorsExpandCpuUltra = aiNode->getAttribute("minMinWarriorsExpandCpuUltra")->getIntValue();
1306 	//	int minMinWarriorsExpandCpuNormal;
1307 	minMinWarriorsExpandCpuNormal = aiNode->getAttribute("minMinWarriorsExpandCpuNormal")->getIntValue();
1308 	//	int maxMinWarriors;
1309 	maxMinWarriors = aiNode->getAttribute("maxMinWarriors")->getIntValue();
1310 	//	int maxExpansions;
1311 	maxExpansions = aiNode->getAttribute("maxExpansions")->getIntValue();
1312 	//	int villageRadius;
1313 	villageRadius = aiNode->getAttribute("villageRadius")->getIntValue();
1314 	//	int scoutResourceRange;
1315 	scoutResourceRange = aiNode->getAttribute("scoutResourceRange")->getIntValue();
1316 	//	int minWorkerAttackersHarvesting;
1317 	minWorkerAttackersHarvesting = aiNode->getAttribute("minWorkerAttackersHarvesting")->getIntValue();
1318 }
1319 
1320 }}//end namespace
1321