mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Nullkiller: Try to join behavior and goal and see what come out of it.
This commit is contained in:
		
				
					committed by
					
						 Andrii Danylchenko
						Andrii Danylchenko
					
				
			
			
				
	
			
			
			
						parent
						
							af0dcf97c4
						
					
				
				
					commit
					223a52b3d1
				
			| @@ -10,10 +10,10 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "../VCAI.h" | ||||
| #error REMOVE THIS FILE | ||||
|  | ||||
| class Behavior | ||||
| class Behavior : public Goals::AbstractGoal | ||||
| { | ||||
| public: | ||||
| 	virtual Goals::TGoalVec getTasks() = 0; | ||||
| 	virtual std::string toString() const = 0; | ||||
| }; | ||||
|   | ||||
| @@ -29,7 +29,7 @@ std::string BuildingBehavior::toString() const | ||||
| 	return "Build"; | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec BuildingBehavior::getTasks() | ||||
| Goals::TGoalVec BuildingBehavior::decompose() const | ||||
| { | ||||
| 	Goals::TGoalVec tasks; | ||||
|  | ||||
|   | ||||
| @@ -10,17 +10,24 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "lib/VCMI_Lib.h" | ||||
| #include "Behavior.h" | ||||
| #include "../AIUtility.h" | ||||
| #include "../Goals/CGoal.h" | ||||
|  | ||||
| class BuildingBehavior : public Behavior | ||||
| namespace Goals | ||||
| { | ||||
| public: | ||||
| 	BuildingBehavior() | ||||
| 	class BuildingBehavior : public CGoal<BuildingBehavior> | ||||
| 	{ | ||||
| 	} | ||||
| 	public: | ||||
| 		BuildingBehavior() | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 	virtual Goals::TGoalVec getTasks() override; | ||||
| 	virtual std::string toString() const override; | ||||
| }; | ||||
| 		virtual Goals::TGoalVec decompose() const override; | ||||
| 		virtual std::string toString() const override; | ||||
| 		virtual bool operator==(const BuildingBehavior & other) const override | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
| 	}; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -29,7 +29,7 @@ std::string BuyArmyBehavior::toString() const | ||||
| 	return "Buy army"; | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec BuyArmyBehavior::getTasks() | ||||
| Goals::TGoalVec BuyArmyBehavior::decompose() const | ||||
| { | ||||
| 	Goals::TGoalVec tasks; | ||||
|  | ||||
|   | ||||
| @@ -10,17 +10,23 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "lib/VCMI_Lib.h" | ||||
| #include "Behavior.h" | ||||
| #include "../AIUtility.h" | ||||
| #include "../Goals/CGoal.h" | ||||
|  | ||||
| class BuyArmyBehavior : public Behavior | ||||
| namespace Goals | ||||
| { | ||||
| public: | ||||
| 	BuyArmyBehavior() | ||||
| 	class BuyArmyBehavior : public CGoal<BuyArmyBehavior> | ||||
| 	{ | ||||
| 	} | ||||
|  | ||||
| 	virtual Goals::TGoalVec getTasks() override; | ||||
| 	virtual std::string toString() const override; | ||||
| }; | ||||
| 	public: | ||||
| 		BuyArmyBehavior() | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 		virtual Goals::TGoalVec decompose() const override; | ||||
| 		virtual std::string toString() const override; | ||||
| 		virtual bool operator==(const BuyArmyBehavior & other) const override | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,7 @@ std::string CaptureObjectsBehavior::toString() const | ||||
| 	return "Capture objects"; | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec CaptureObjectsBehavior::getTasks() | ||||
| Goals::TGoalVec CaptureObjectsBehavior::decompose() const | ||||
| { | ||||
| 	Goals::TGoalVec tasks; | ||||
|  | ||||
| @@ -42,7 +42,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks() | ||||
|  | ||||
| 		for(auto objToVisit : objs) | ||||
| 		{			 | ||||
| #ifdef AI_TRACE_LEVEL >= 1 | ||||
| #if AI_TRACE_LEVEL >= 1 | ||||
| 			logAi->trace("Checking object %s, %s", objToVisit->getObjectName(), objToVisit->visitablePos().toString()); | ||||
| #endif | ||||
|  | ||||
| @@ -55,19 +55,19 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks() | ||||
| 			std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj; | ||||
| 			std::shared_ptr<ExecuteHeroChain> closestWay; | ||||
| 					 | ||||
| #ifdef AI_TRACE_LEVEL >= 1 | ||||
| #if AI_TRACE_LEVEL >= 1 | ||||
| 			logAi->trace("Found %d paths", paths.size()); | ||||
| #endif | ||||
|  | ||||
| 			for(auto & path : paths) | ||||
| 			{ | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| #if AI_TRACE_LEVEL >= 2 | ||||
| 				logAi->trace("Path found %s", path.toString()); | ||||
| #endif | ||||
|  | ||||
| 				if(path.getFirstBlockedAction()) | ||||
| 				{ | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| #if AI_TRACE_LEVEL >= 2 | ||||
| 					// TODO: decomposition? | ||||
| 					logAi->trace("Ignore path. Action is blocked."); | ||||
| #endif | ||||
| @@ -76,7 +76,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks() | ||||
|  | ||||
| 				if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path)) | ||||
| 				{ | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| #if AI_TRACE_LEVEL >= 2 | ||||
| 					logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength()); | ||||
| #endif | ||||
| 					continue; | ||||
| @@ -98,7 +98,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks() | ||||
|  | ||||
| 				auto isSafe = isSafeToVisit(hero, path.heroArmy, danger); | ||||
| 				 | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| #if AI_TRACE_LEVEL >= 2 | ||||
| 				logAi->trace( | ||||
| 					"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld",  | ||||
| 					isSafe ? "safe" : "not safe", | ||||
|   | ||||
| @@ -10,51 +10,62 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "lib/VCMI_Lib.h" | ||||
| #include "Behavior.h" | ||||
| #include "../AIUtility.h" | ||||
| #include "../Goals/CGoal.h" | ||||
|  | ||||
| class CaptureObjectsBehavior : public Behavior { | ||||
| private: | ||||
| 	std::vector<int> objectTypes; | ||||
| 	std::vector<int> objectSubTypes; | ||||
| 	std::vector<const CGObjectInstance *> objectsToCapture; | ||||
| 	bool specificObjects; | ||||
| public: | ||||
| 	CaptureObjectsBehavior() | ||||
| namespace Goals | ||||
| { | ||||
| 	class CaptureObjectsBehavior : public CGoal<CaptureObjectsBehavior> | ||||
| 	{ | ||||
| 		objectTypes = std::vector<int>(); | ||||
| 		specificObjects = false; | ||||
| 	} | ||||
| 	private: | ||||
| 		std::vector<int> objectTypes; | ||||
| 		std::vector<int> objectSubTypes; | ||||
| 		std::vector<const CGObjectInstance *> objectsToCapture; | ||||
| 		bool specificObjects; | ||||
| 	public: | ||||
| 		CaptureObjectsBehavior() | ||||
| 		{ | ||||
| 			objectTypes = std::vector<int>(); | ||||
| 			specificObjects = false; | ||||
| 		} | ||||
|  | ||||
| 	CaptureObjectsBehavior(std::vector<const CGObjectInstance *> objectsToCapture) | ||||
| 	{ | ||||
| 		this->objectsToCapture = objectsToCapture; | ||||
| 		specificObjects = true; | ||||
| 	} | ||||
| 		CaptureObjectsBehavior(std::vector<const CGObjectInstance *> objectsToCapture) | ||||
| 		{ | ||||
| 			this->objectsToCapture = objectsToCapture; | ||||
| 			specificObjects = true; | ||||
| 		} | ||||
|  | ||||
| 	CaptureObjectsBehavior(const CGObjectInstance * objectToCapture) | ||||
| 	{ | ||||
| 		objectsToCapture = std::vector<const CGObjectInstance *>(); | ||||
| 		objectsToCapture.push_back(objectToCapture); | ||||
| 		specificObjects = true; | ||||
| 	} | ||||
| 		CaptureObjectsBehavior(const CGObjectInstance * objectToCapture) | ||||
| 		{ | ||||
| 			objectsToCapture = std::vector<const CGObjectInstance *>(); | ||||
| 			objectsToCapture.push_back(objectToCapture); | ||||
| 			specificObjects = true; | ||||
| 		} | ||||
|  | ||||
| 	virtual Goals::TGoalVec getTasks() override; | ||||
| 	virtual std::string toString() const override; | ||||
| 		virtual Goals::TGoalVec decompose() const override; | ||||
| 		virtual std::string toString() const override; | ||||
|  | ||||
| 	CaptureObjectsBehavior & ofType(int type) { | ||||
| 		objectTypes.push_back(type); | ||||
| 		CaptureObjectsBehavior & ofType(int type) | ||||
| 		{ | ||||
| 			objectTypes.push_back(type); | ||||
|  | ||||
| 		return *this; | ||||
| 	} | ||||
| 	CaptureObjectsBehavior & ofType(int type, int subType) { | ||||
| 		objectTypes.push_back(type); | ||||
| 		objectSubTypes.push_back(subType); | ||||
| 			return *this; | ||||
| 		} | ||||
| 		CaptureObjectsBehavior & ofType(int type, int subType) | ||||
| 		{ | ||||
| 			objectTypes.push_back(type); | ||||
| 			objectSubTypes.push_back(subType); | ||||
|  | ||||
| 		return *this; | ||||
| 	} | ||||
| 			return *this; | ||||
| 		} | ||||
|  | ||||
| private: | ||||
| 	bool shouldVisitObject(ObjectIdRef obj) const; | ||||
| }; | ||||
| 		virtual bool operator==(const CaptureObjectsBehavior & other) const override | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 	private: | ||||
| 		bool shouldVisitObject(ObjectIdRef obj) const; | ||||
| 	}; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										237
									
								
								AI/Nullkiller/Behaviors/CompleteQuestBehavior.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								AI/Nullkiller/Behaviors/CompleteQuestBehavior.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,237 @@ | ||||
| /* | ||||
| * CompleteQuestBehavior.cpp, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
| #include "StdInc.h" | ||||
| #include "CompleteQuestBehavior.h" | ||||
| #include "CaptureObjectsBehavior.h" | ||||
| #include "../VCAI.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../../../lib/mapping/CMap.h" //for victory conditions | ||||
| #include "../../../lib/CPathfinder.h" | ||||
|  | ||||
| extern boost::thread_specific_ptr<CCallback> cb; | ||||
| extern boost::thread_specific_ptr<VCAI> ai; | ||||
| extern FuzzyHelper * fh; | ||||
|  | ||||
| using namespace Goals; | ||||
|  | ||||
| std::string CompleteQuestBehavior::toString() const | ||||
| { | ||||
| 	return "Complete Quests"; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuestBehavior::decompose() const | ||||
| { | ||||
| 	TGoalVec solutions; | ||||
|  | ||||
| 	auto quests = cb->getMyQuests(); | ||||
|  | ||||
| 	for(auto & q : quests) | ||||
| 	{ | ||||
| 		if(q.quest->missionType == CQuest::MISSION_NONE || q.quest->progress == CQuest::COMPLETE) | ||||
| 		{ | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		vstd::concatenate(solutions, getQuestTasks(q)); | ||||
| 	} | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuestBehavior::getQuestTasks(const QuestInfo & q) const | ||||
| { | ||||
| 	logAi->debug("Trying to realize quest: %s", questToString(q)); | ||||
|  | ||||
| 	switch(q.quest->missionType) | ||||
| 	{ | ||||
| 	case CQuest::MISSION_ART: | ||||
| 		return missionArt(q); | ||||
|  | ||||
| 	case CQuest::MISSION_HERO: | ||||
| 		return missionHero(q); | ||||
|  | ||||
| 	case CQuest::MISSION_ARMY: | ||||
| 		return missionArmy(q); | ||||
|  | ||||
| 	case CQuest::MISSION_RESOURCES: | ||||
| 		return missionResources(q); | ||||
|  | ||||
| 	case CQuest::MISSION_KILL_HERO: | ||||
| 	case CQuest::MISSION_KILL_CREATURE: | ||||
| 		return missionDestroyObj(q); | ||||
|  | ||||
| 	case CQuest::MISSION_PRIMARY_STAT: | ||||
| 		return missionIncreasePrimaryStat(q); | ||||
|  | ||||
| 	case CQuest::MISSION_LEVEL: | ||||
| 		return missionLevel(q); | ||||
|  | ||||
| 	case CQuest::MISSION_PLAYER: | ||||
| 		if(ai->playerID.getNum() != q.quest->m13489val) | ||||
| 			logAi->debug("Can't be player of color %d", q.quest->m13489val); | ||||
|  | ||||
| 		break; | ||||
|  | ||||
| 	case CQuest::MISSION_KEYMASTER: | ||||
| 		return missionKeymaster(q); | ||||
|  | ||||
| 	} //end of switch | ||||
|  | ||||
| 	return TGoalVec(); | ||||
| } | ||||
|  | ||||
| std::string CompleteQuestBehavior::questToString(const QuestInfo & q) const | ||||
| { | ||||
| 	if(q.quest->missionType == CQuest::MISSION_NONE) | ||||
| 		return "inactive quest"; | ||||
|  | ||||
| 	MetaString ms; | ||||
| 	q.quest->getRolloverText(ms, false); | ||||
|  | ||||
| 	return ms.toString(); | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuestBehavior::tryCompleteQuest(const QuestInfo & q) const | ||||
| { | ||||
| 	TGoalVec solutions; | ||||
|  | ||||
| 	auto tasks = CaptureObjectsBehavior(q.obj).decompose(); //TODO: choose best / free hero from among many possibilities? | ||||
|  | ||||
| 	for(auto task : tasks) | ||||
| 	{ | ||||
| 		if(task->hero && q.quest->checkQuest(task->hero.get())) | ||||
| 		{ | ||||
| 			solutions.push_back(task); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuestBehavior::missionArt(const QuestInfo & q) const | ||||
| { | ||||
| 	TGoalVec solutions = tryCompleteQuest(q); | ||||
|  | ||||
| 	if(!solutions.empty()) | ||||
| 		return solutions; | ||||
|  | ||||
| 	/*for(auto art : q.quest->m5arts) | ||||
| 	{ | ||||
| 		solutions.push_back(sptr(GetArtOfType(art))); //TODO: transport? | ||||
| 	}*/ | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuestBehavior::missionHero(const QuestInfo & q) const | ||||
| { | ||||
| 	TGoalVec solutions = tryCompleteQuest(q); | ||||
|  | ||||
| 	if(solutions.empty()) | ||||
| 	{ | ||||
| 		//rule of a thumb - quest heroes usually are locked in prisons | ||||
| 		return CaptureObjectsBehavior().ofType(Obj::PRISON).decompose(); | ||||
| 	} | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuestBehavior::missionArmy(const QuestInfo & q) const | ||||
| { | ||||
| 	TGoalVec solutions = tryCompleteQuest(q); | ||||
|  | ||||
| 	if(!solutions.empty()) | ||||
| 		return solutions; | ||||
| 	/* | ||||
| 	for(auto creature : q.quest->m6creatures) | ||||
| 	{ | ||||
| 		solutions.push_back(sptr(GatherTroops(creature.type->idNumber, creature.count))); | ||||
| 	}*/ | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuestBehavior::missionIncreasePrimaryStat(const QuestInfo & q) const | ||||
| { | ||||
| 	return tryCompleteQuest(q); | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuestBehavior::missionLevel(const QuestInfo & q) const | ||||
| { | ||||
| 	return tryCompleteQuest(q); | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuestBehavior::missionKeymaster(const QuestInfo & q) const | ||||
| { | ||||
| 	TGoalVec solutions = tryCompleteQuest(q); | ||||
|  | ||||
| 	if(solutions.empty()) | ||||
| 	{ | ||||
| 		return CaptureObjectsBehavior().ofType(Obj::KEYMASTER, q.obj->subID).decompose(); | ||||
| 	} | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuestBehavior::missionResources(const QuestInfo & q) const | ||||
| { | ||||
| 	TGoalVec solutions; | ||||
|  | ||||
| 	/*auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities? | ||||
|  | ||||
| 	if(heroes.size()) | ||||
| 	{ | ||||
| 		if(q.quest->checkQuest(heroes.front())) //it doesn't matter which hero it is | ||||
| 		{ | ||||
| 			return solutions;// ai->ah->howToVisitObj(q.obj); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			for(int i = 0; i < q.quest->m7resources.size(); ++i) | ||||
| 			{ | ||||
| 				if(q.quest->m7resources[i]) | ||||
| 					solutions.push_back(sptr(CollectRes(i, q.quest->m7resources[i]))); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		solutions.push_back(sptr(Goals::RecruitHero())); //FIXME: checkQuest requires any hero belonging to player :( | ||||
| 	}*/ | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuestBehavior::missionDestroyObj(const QuestInfo & q) const | ||||
| { | ||||
| 	auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val); | ||||
|  | ||||
| 	if(!obj) | ||||
| 		return CaptureObjectsBehavior(q.obj).decompose(); | ||||
|  | ||||
| 	if(obj->ID == Obj::HERO) | ||||
| 	{ | ||||
| 		auto relations = cb->getPlayerRelations(ai->playerID, obj->tempOwner); | ||||
|  | ||||
| 		//if(relations == PlayerRelations::SAME_PLAYER) | ||||
| 		//{ | ||||
| 		//	auto heroToProtect = cb->getHero(obj->id); | ||||
|  | ||||
| 		//	//solutions.push_back(sptr(GatherArmy().sethero(heroToProtect))); | ||||
| 		//} | ||||
| 		//else  | ||||
| 		if(relations == PlayerRelations::ENEMIES) | ||||
| 		{ | ||||
| 			return CaptureObjectsBehavior(obj).decompose(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return TGoalVec(); | ||||
| } | ||||
							
								
								
									
										48
									
								
								AI/Nullkiller/Behaviors/CompleteQuestBehavior.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								AI/Nullkiller/Behaviors/CompleteQuestBehavior.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| /* | ||||
| * CompleteQuestBehavior.h, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| #include "lib/VCMI_Lib.h" | ||||
| #include "../AIUtility.h" | ||||
| #include "../../../lib/VCMI_Lib.h" | ||||
| #include "../../../CCallback.h" | ||||
| #include "../Goals/CGoal.h" | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	class CompleteQuestBehavior : public CGoal<CompleteQuestBehavior> | ||||
| 	{ | ||||
| 	public: | ||||
| 		CompleteQuestBehavior() | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 		virtual Goals::TGoalVec decompose() const override; | ||||
| 		virtual std::string toString() const override; | ||||
|  | ||||
| 		virtual bool operator==(const CompleteQuestBehavior & other) const override | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 	private: | ||||
| 		TGoalVec getQuestTasks(const QuestInfo & q) const; | ||||
| 		TGoalVec tryCompleteQuest(const QuestInfo & q) const; | ||||
| 		TGoalVec missionArt(const QuestInfo & q) const; | ||||
| 		TGoalVec missionHero(const QuestInfo & q) const; | ||||
| 		TGoalVec missionArmy(const QuestInfo & q) const; | ||||
| 		TGoalVec missionResources(const QuestInfo & q) const; | ||||
| 		TGoalVec missionDestroyObj(const QuestInfo & q) const; | ||||
| 		TGoalVec missionIncreasePrimaryStat(const QuestInfo & q) const; | ||||
| 		TGoalVec missionLevel(const QuestInfo & q) const; | ||||
| 		TGoalVec missionKeymaster(const QuestInfo & q) const; | ||||
| 		std::string questToString(const QuestInfo & q) const; | ||||
| 	}; | ||||
| } | ||||
| @@ -14,7 +14,6 @@ | ||||
| #include "../AIhelper.h" | ||||
| #include "../AIUtility.h" | ||||
| #include "../Goals/BuyArmy.h" | ||||
| #include "../Goals/VisitTile.h" | ||||
| #include "../Goals/ExecuteHeroChain.h" | ||||
| #include "../Goals/DismissHero.h" | ||||
| #include "../Goals/ExchangeSwapTownHeroes.h" | ||||
| @@ -32,7 +31,7 @@ std::string DefenceBehavior::toString() const | ||||
| 	return "Defend towns"; | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec DefenceBehavior::getTasks() | ||||
| Goals::TGoalVec DefenceBehavior::decompose() const | ||||
| { | ||||
| 	Goals::TGoalVec tasks; | ||||
| 		 | ||||
| @@ -65,7 +64,7 @@ uint64_t townArmyIncome(const CGTownInstance * town) | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) | ||||
| void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const | ||||
| { | ||||
| 	auto basicPriority = 0.3f + std::sqrt(townArmyIncome(town) / 20000.0f) | ||||
| 		+ town->dailyIncome()[Res::GOLD] / 10000.0f; | ||||
| @@ -163,7 +162,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta | ||||
| 					if(cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES) | ||||
| 					{ | ||||
| 						logAi->debug("Hero %s can be recruited to defend %s", hero->name, town->name); | ||||
| 						tasks.push_back(Goals::sptr(Goals::RecruitHero().settown(town).setobjid(hero->id.getNum()).setpriority(1))); | ||||
| 						tasks.push_back(Goals::sptr(Goals::RecruitHero(town, hero).setpriority(1))); | ||||
| 						continue; | ||||
| 					} | ||||
| 					else | ||||
|   | ||||
| @@ -10,20 +10,28 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "lib/VCMI_Lib.h" | ||||
| #include "Behavior.h" | ||||
| #include "../Goals/CGoal.h" | ||||
| #include "../AIUtility.h" | ||||
|  | ||||
| class DefenceBehavior : public Behavior | ||||
| namespace Goals | ||||
| { | ||||
| public: | ||||
| 	DefenceBehavior() | ||||
| 	class DefenceBehavior : public CGoal<DefenceBehavior> | ||||
| 	{ | ||||
| 	} | ||||
| 	public: | ||||
| 		DefenceBehavior() | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 	virtual Goals::TGoalVec getTasks() override; | ||||
| 	virtual std::string toString() const override; | ||||
| 		virtual Goals::TGoalVec decompose() const override; | ||||
| 		virtual std::string toString() const override; | ||||
|  | ||||
| private: | ||||
| 	void evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town); | ||||
| }; | ||||
| 		virtual bool operator==(const DefenceBehavior & other) const override | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 	private: | ||||
| 		void evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const; | ||||
| 	}; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -23,14 +23,12 @@ extern FuzzyHelper * fh; | ||||
|  | ||||
| using namespace Goals; | ||||
|  | ||||
| #define AI_TRACE_LEVEL 2 | ||||
|  | ||||
| std::string GatherArmyBehavior::toString() const | ||||
| { | ||||
| 	return "Gather army"; | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec GatherArmyBehavior::getTasks() | ||||
| Goals::TGoalVec GatherArmyBehavior::decompose() const | ||||
| { | ||||
| 	Goals::TGoalVec tasks; | ||||
|  | ||||
| @@ -65,12 +63,12 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her | ||||
| 	Goals::TGoalVec tasks; | ||||
| 	const int3 pos = hero->visitablePos(); | ||||
|  | ||||
| #ifdef AI_TRACE_LEVEL >= 1 | ||||
| #if AI_TRACE_LEVEL >= 1 | ||||
| 	logAi->trace("Checking ways to gaher army for hero %s, %s", hero->getObjectName(), pos.toString()); | ||||
| #endif | ||||
| 	if(ai->nullkiller->isHeroLocked(hero)) | ||||
| 	{ | ||||
| #ifdef AI_TRACE_LEVEL >= 1 | ||||
| #if AI_TRACE_LEVEL >= 1 | ||||
| 		logAi->trace("Skipping locked hero %s, %s", hero->getObjectName(), pos.toString()); | ||||
| #endif | ||||
| 		return tasks; | ||||
| @@ -79,13 +77,13 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her | ||||
| 	auto paths = ai->ah->getPathsToTile(pos); | ||||
| 	std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj; | ||||
|  | ||||
| #ifdef AI_TRACE_LEVEL >= 1 | ||||
| #if AI_TRACE_LEVEL >= 1 | ||||
| 	logAi->trace("Found %d paths", paths.size()); | ||||
| #endif | ||||
|  | ||||
| 	for(const AIPath & path : paths) | ||||
| 	{ | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| #if AI_TRACE_LEVEL >= 2 | ||||
| 		logAi->trace("Path found %s", path.toString()); | ||||
| #endif | ||||
| 		 | ||||
| @@ -93,7 +91,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her | ||||
|  | ||||
| 		if(path.getFirstBlockedAction()) | ||||
| 		{ | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| #if AI_TRACE_LEVEL >= 2 | ||||
| 			// TODO: decomposition? | ||||
| 			logAi->trace("Ignore path. Action is blocked."); | ||||
| #endif | ||||
| @@ -102,7 +100,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her | ||||
|  | ||||
| 		if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path)) | ||||
| 		{ | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| #if AI_TRACE_LEVEL >= 2 | ||||
| 			logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength()); | ||||
| #endif | ||||
| 			continue; | ||||
| @@ -122,7 +120,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her | ||||
|  | ||||
| 		auto isSafe = isSafeToVisit(hero, path.heroArmy, danger); | ||||
|  | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| #if AI_TRACE_LEVEL >= 2 | ||||
| 		logAi->trace( | ||||
| 			"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld", | ||||
| 			isSafe ? "safe" : "not safe", | ||||
| @@ -164,25 +162,25 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader) | ||||
| 	const int3 pos = upgrader->visitablePos(); | ||||
| 	TResources availableResources = cb->getResourceAmount(); | ||||
|  | ||||
| #ifdef AI_TRACE_LEVEL >= 1 | ||||
| #if AI_TRACE_LEVEL >= 1 | ||||
| 	logAi->trace("Checking ways to upgrade army in town %s, %s", upgrader->getObjectName(), pos.toString()); | ||||
| #endif | ||||
| 	 | ||||
| 	auto paths = ai->ah->getPathsToTile(pos); | ||||
| 	std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj; | ||||
|  | ||||
| #ifdef AI_TRACE_LEVEL >= 1 | ||||
| #if AI_TRACE_LEVEL >= 1 | ||||
| 	logAi->trace("Found %d paths", paths.size()); | ||||
| #endif | ||||
|  | ||||
| 	for(const AIPath & path : paths) | ||||
| 	{ | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| #if AI_TRACE_LEVEL >= 2 | ||||
| 		logAi->trace("Path found %s", path.toString()); | ||||
| #endif | ||||
| 		if(upgrader->visitingHero != path.targetHero) | ||||
| 		{ | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| #if AI_TRACE_LEVEL >= 2 | ||||
| 			logAi->trace("Ignore path. Town has visiting hero."); | ||||
| #endif | ||||
| 			continue; | ||||
| @@ -190,7 +188,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader) | ||||
|  | ||||
| 		if(path.getFirstBlockedAction()) | ||||
| 		{ | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| #if AI_TRACE_LEVEL >= 2 | ||||
| 			// TODO: decomposition? | ||||
| 			logAi->trace("Ignore path. Action is blocked."); | ||||
| #endif | ||||
| @@ -199,7 +197,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader) | ||||
|  | ||||
| 		if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path)) | ||||
| 		{ | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| #if AI_TRACE_LEVEL >= 2 | ||||
| 			logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength()); | ||||
| #endif | ||||
| 			continue; | ||||
| @@ -215,7 +213,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader) | ||||
|  | ||||
| 		auto isSafe = isSafeToVisit(path.targetHero, path.heroArmy, danger); | ||||
|  | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| #if AI_TRACE_LEVEL >= 2 | ||||
| 		logAi->trace( | ||||
| 			"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld", | ||||
| 			isSafe ? "safe" : "not safe", | ||||
|   | ||||
| @@ -10,27 +10,29 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "lib/VCMI_Lib.h" | ||||
| #include "Behavior.h" | ||||
| #include "../Goals/CGoal.h" | ||||
| #include "../AIUtility.h" | ||||
|  | ||||
| class GatherArmyBehavior : public Behavior { | ||||
| private: | ||||
| 	std::vector<int> objectTypes; | ||||
| 	std::vector<int> objectSubTypes; | ||||
| 	std::vector<const CGObjectInstance *> objectsToCapture; | ||||
| 	bool specificObjects; | ||||
| public: | ||||
| 	GatherArmyBehavior() | ||||
| namespace Goals | ||||
| { | ||||
| 	class GatherArmyBehavior : public CGoal<GatherArmyBehavior> | ||||
| 	{ | ||||
| 		objectTypes = std::vector<int>(); | ||||
| 		specificObjects = false; | ||||
| 	} | ||||
| 	public: | ||||
| 		GatherArmyBehavior() | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 	virtual Goals::TGoalVec getTasks() override; | ||||
| 	virtual std::string toString() const override; | ||||
| 		virtual TGoalVec decompose() const override; | ||||
| 		virtual std::string toString() const override; | ||||
|  | ||||
| private: | ||||
| 	Goals::TGoalVec deliverArmyToHero(const CGHeroInstance * hero) const; | ||||
| 	Goals::TGoalVec upgradeArmy(const CGTownInstance * upgrader) const; | ||||
| }; | ||||
| 		virtual bool operator==(const GatherArmyBehavior & other) const override | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 	private: | ||||
| 		TGoalVec deliverArmyToHero(const CGHeroInstance * hero) const; | ||||
| 		TGoalVec upgradeArmy(const CGTownInstance * upgrader) const; | ||||
| 	}; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -28,7 +28,7 @@ std::string RecruitHeroBehavior::toString() const | ||||
| 	return "Recruit hero"; | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec RecruitHeroBehavior::getTasks() | ||||
| Goals::TGoalVec RecruitHeroBehavior::decompose() const | ||||
| { | ||||
| 	Goals::TGoalVec tasks; | ||||
| 	auto towns = cb->getTownsInfo(); | ||||
| @@ -40,7 +40,7 @@ Goals::TGoalVec RecruitHeroBehavior::getTasks() | ||||
| 			if(cb->getHeroesInfo().size() < cb->getTownsInfo().size() + 1 | ||||
| 				|| cb->getResourceAmount(Res::GOLD) > 10000) | ||||
| 			{ | ||||
| 				tasks.push_back(Goals::sptr(Goals::RecruitHero().settown(town).setpriority(3))); | ||||
| 				tasks.push_back(Goals::sptr(Goals::RecruitHero(town).setpriority(3))); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -10,16 +10,24 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "lib/VCMI_Lib.h" | ||||
| #include "Behavior.h" | ||||
| #include "../Goals/CGoal.h" | ||||
| #include "../AIUtility.h" | ||||
|  | ||||
| class RecruitHeroBehavior : public Behavior | ||||
| namespace Goals | ||||
| { | ||||
| public: | ||||
| 	RecruitHeroBehavior() | ||||
| 	class RecruitHeroBehavior : public CGoal<RecruitHeroBehavior> | ||||
| 	{ | ||||
| 	} | ||||
| 	public: | ||||
| 		RecruitHeroBehavior() | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 	virtual Goals::TGoalVec getTasks() override; | ||||
| 	virtual std::string toString() const override; | ||||
| }; | ||||
| 		virtual TGoalVec decompose() const override; | ||||
| 		virtual std::string toString() const override; | ||||
|  | ||||
| 		virtual bool operator==(const RecruitHeroBehavior & other) const override | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
| 	}; | ||||
| } | ||||
| @@ -92,7 +92,7 @@ bool needToRecruitHero(const CGTownInstance * startupTown) | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec StartupBehavior::getTasks() | ||||
| Goals::TGoalVec StartupBehavior::decompose() const | ||||
| { | ||||
| 	Goals::TGoalVec tasks; | ||||
| 	auto towns = cb->getTownsInfo(); | ||||
| @@ -166,7 +166,7 @@ Goals::TGoalVec StartupBehavior::getTasks() | ||||
|  | ||||
| 	if(tasks.empty() && canRecruitHero && !startupTown->visitingHero) | ||||
| 	{ | ||||
| 		tasks.push_back(Goals::sptr(Goals::RecruitHero())); | ||||
| 		tasks.push_back(Goals::sptr(Goals::RecruitHero(startupTown))); | ||||
| 	} | ||||
|  | ||||
| 	if(tasks.empty() && towns.size()) | ||||
|   | ||||
| @@ -10,17 +10,25 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "lib/VCMI_Lib.h" | ||||
| #include "Behavior.h" | ||||
| #include "../Goals/CGoal.h" | ||||
| #include "../AIUtility.h" | ||||
|  | ||||
| class StartupBehavior : public Behavior | ||||
| namespace Goals | ||||
| { | ||||
| public: | ||||
| 	StartupBehavior() | ||||
| 	class StartupBehavior : public CGoal<StartupBehavior> | ||||
| 	{ | ||||
| 	} | ||||
| 	public: | ||||
| 		StartupBehavior() | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 	virtual Goals::TGoalVec getTasks() override; | ||||
| 	virtual std::string toString() const override; | ||||
| }; | ||||
| 		virtual TGoalVec decompose() const override; | ||||
| 		virtual std::string toString() const override; | ||||
|  | ||||
| 		virtual bool operator==(const StartupBehavior & other) const override | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
| 	}; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -27,24 +27,18 @@ set(VCAI_SRCS | ||||
| 		Goals/GatherTroops.cpp | ||||
| 		Goals/BuyArmy.cpp | ||||
| 		Goals/AdventureSpellCast.cpp | ||||
| 		Goals/Win.cpp | ||||
| 		Goals/VisitTile.cpp | ||||
| 		Goals/VisitObj.cpp | ||||
| 		Goals/VisitHero.cpp | ||||
| 		Goals/CollectRes.cpp | ||||
| 		Goals/Trade.cpp | ||||
| 		Goals/RecruitHero.cpp | ||||
| 		Goals/DigAtTile.cpp | ||||
| 		Goals/GetArtOfType.cpp | ||||
| 		Goals/FindObj.cpp | ||||
| 		Goals/CompleteQuest.cpp | ||||
| 		Goals/ExecuteHeroChain.cpp | ||||
| 		Goals/ExchangeSwapTownHeroes.cpp | ||||
| 		Engine/Nullkiller.cpp | ||||
| 		Engine/PriorityEvaluator.cpp | ||||
| 		Analyzers/DangerHitMapAnalyzer.cpp | ||||
| 		Analyzers/BuildAnalyzer.cpp | ||||
| 		Behaviors/Behavior.cpp | ||||
| 		Behaviors/CaptureObjectsBehavior.cpp | ||||
| 		Behaviors/RecruitHeroBehavior.cpp | ||||
| 		Behaviors/BuyArmyBehavior.cpp | ||||
| @@ -52,6 +46,7 @@ set(VCAI_SRCS | ||||
| 		Behaviors/StartupBehavior.cpp | ||||
| 		Behaviors/BuildingBehavior.cpp | ||||
| 		Behaviors/GatherArmyBehavior.cpp | ||||
| 		Behaviors/CompleteQuestBehavior.cpp | ||||
| 		main.cpp | ||||
| 		VCAI.cpp | ||||
| ) | ||||
| @@ -88,17 +83,12 @@ set(VCAI_HEADERS | ||||
| 		Goals/GatherTroops.h | ||||
| 		Goals/BuyArmy.h | ||||
| 		Goals/AdventureSpellCast.h | ||||
| 		Goals/Win.h | ||||
| 		Goals/VisitTile.h | ||||
| 		Goals/VisitObj.h | ||||
| 		Goals/VisitHero.h | ||||
| 		Goals/CollectRes.h | ||||
| 		Goals/Trade.h | ||||
| 		Goals/RecruitHero.h | ||||
| 		Goals/DigAtTile.h | ||||
| 		Goals/GetArtOfType.h | ||||
| 		Goals/FindObj.h | ||||
| 		Goals/CompleteQuest.h | ||||
| 		Goals/ExecuteHeroChain.h | ||||
| 		Goals/ExchangeSwapTownHeroes.h | ||||
| 		Goals/Goals.h | ||||
| @@ -106,7 +96,6 @@ set(VCAI_HEADERS | ||||
| 		Engine/PriorityEvaluator.h | ||||
| 		Analyzers/DangerHitMapAnalyzer.h | ||||
| 		Analyzers/BuildAnalyzer.h | ||||
| 		Behaviors/Behavior.h | ||||
| 		Behaviors/CaptureObjectsBehavior.h | ||||
| 		Behaviors/RecruitHeroBehavior.h | ||||
| 		Behaviors/BuyArmyBehavior.h | ||||
| @@ -114,6 +103,7 @@ set(VCAI_HEADERS | ||||
| 		Behaviors/StartupBehavior.h | ||||
| 		Behaviors/BuildingBehavior.h | ||||
| 		Behaviors/GatherArmyBehavior.h | ||||
| 		Behaviors/CompleteQuestBehavior.h | ||||
| 		VCAI.h | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -23,6 +23,8 @@ | ||||
| extern boost::thread_specific_ptr<CCallback> cb; | ||||
| extern boost::thread_specific_ptr<VCAI> ai; | ||||
|  | ||||
| using namespace Goals; | ||||
|  | ||||
| Nullkiller::Nullkiller() | ||||
| { | ||||
| 	priorityEvaluator.reset(new PriorityEvaluator()); | ||||
| @@ -30,38 +32,75 @@ Nullkiller::Nullkiller() | ||||
| 	buildAnalyzer.reset(new BuildAnalyzer()); | ||||
| } | ||||
|  | ||||
| Goals::TSubgoal Nullkiller::choseBestTask(Goals::TGoalVec & tasks) const | ||||
| Goals::TTask Nullkiller::choseBestTask(Goals::TTaskVec & tasks) const | ||||
| { | ||||
| 	Goals::TSubgoal bestTask = *vstd::maxElementByFun(tasks, [](Goals::TSubgoal goal) -> float{ | ||||
| 		return goal->priority; | ||||
| 	Goals::TTask bestTask = *vstd::maxElementByFun(tasks, [](Goals::TTask task) -> float{ | ||||
| 		return task->priority; | ||||
| 	}); | ||||
|  | ||||
| 	return bestTask; | ||||
| } | ||||
|  | ||||
| Goals::TSubgoal Nullkiller::choseBestTask(std::shared_ptr<Behavior> behavior) const | ||||
| Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const | ||||
| { | ||||
| 	logAi->debug("Checking behavior %s", behavior->toString()); | ||||
|  | ||||
| 	auto tasks = behavior->getTasks(); | ||||
| 	const int MAX_DEPTH = 10; | ||||
| 	Goals::TGoalVec goals[MAX_DEPTH + 1]; | ||||
| 	Goals::TTaskVec tasks; | ||||
|  | ||||
| 	goals[0] = {behavior}; | ||||
|  | ||||
| 	if(tasks.empty()) | ||||
| 	{ | ||||
| 		logAi->debug("Behavior %s found no tasks", behavior->toString()); | ||||
|  | ||||
| 		return Goals::sptr(Goals::Invalid()); | ||||
| 		return Goals::taskptr(Goals::Invalid()); | ||||
| 	} | ||||
|  | ||||
| 	logAi->trace("Evaluating priorities, tasks count %d", tasks.size()); | ||||
|  | ||||
| 	for(auto task : tasks) | ||||
| 	int depth = 0; | ||||
| 	while(goals[0].size()) | ||||
| 	{ | ||||
| 		task->setpriority(priorityEvaluator->evaluate(task)); | ||||
| 		TSubgoal current = goals[depth].back(); | ||||
| 		TGoalVec subgoals = current->decompose(); | ||||
|  | ||||
| 		goals[depth + 1].clear(); | ||||
|  | ||||
| 		for(auto subgoal : subgoals) | ||||
| 		{ | ||||
| 			if(subgoal->isElementar) | ||||
| 			{ | ||||
| 				auto task = taskptr(*subgoal); | ||||
|  | ||||
| 				if(task->priority <= 0) | ||||
| 					task->priority = priorityEvaluator->evaluate(subgoal); | ||||
|  | ||||
| 				tasks.push_back(task); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				goals[depth + 1].push_back(subgoal); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if(goals[depth + 1].size() && depth < MAX_DEPTH) | ||||
| 		{ | ||||
| 			depth++; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			while(depth > 0 && goals[depth].empty()) | ||||
| 			{ | ||||
| 				depth--; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	auto task = choseBestTask(tasks); | ||||
|  | ||||
| 	logAi->debug("Behavior %s returns %s(%s), priority %f", behavior->toString(), task->name(), task->tile.toString(), task->priority); | ||||
| 	logAi->debug("Behavior %s returns %s, priority %f", behavior->toString(), task->toString(), task->priority); | ||||
|  | ||||
| 	return task; | ||||
| } | ||||
| @@ -141,54 +180,49 @@ void Nullkiller::makeTurn() | ||||
| 	{ | ||||
| 		updateAiState(); | ||||
|  | ||||
| 		Goals::TGoalVec bestTasks = { | ||||
| 			choseBestTask(std::make_shared<BuyArmyBehavior>()), | ||||
| 			choseBestTask(std::make_shared<CaptureObjectsBehavior>()), | ||||
| 			choseBestTask(std::make_shared<RecruitHeroBehavior>()), | ||||
| 			choseBestTask(std::make_shared<DefenceBehavior>()), | ||||
| 			choseBestTask(std::make_shared<BuildingBehavior>()), | ||||
| 			choseBestTask(std::make_shared<GatherArmyBehavior>()) | ||||
| 		Goals::TTaskVec bestTasks = { | ||||
| 			choseBestTask(sptr(BuyArmyBehavior())), | ||||
| 			choseBestTask(sptr(CaptureObjectsBehavior())), | ||||
| 			choseBestTask(sptr(RecruitHeroBehavior())), | ||||
| 			choseBestTask(sptr(DefenceBehavior())), | ||||
| 			choseBestTask(sptr(BuildingBehavior())), | ||||
| 			choseBestTask(sptr(GatherArmyBehavior())) | ||||
| 		}; | ||||
|  | ||||
| 		if(cb->getDate(Date::DAY) == 1) | ||||
| 		{ | ||||
| 			bestTasks.push_back(choseBestTask(std::make_shared<StartupBehavior>())); | ||||
| 			bestTasks.push_back(choseBestTask(sptr(StartupBehavior()))); | ||||
| 		} | ||||
|  | ||||
| 		Goals::TSubgoal bestTask = choseBestTask(bestTasks); | ||||
| 		Goals::TTask bestTask = choseBestTask(bestTasks); | ||||
|  | ||||
| 		if(bestTask->invalid()) | ||||
| 		/*if(bestTask->invalid()) | ||||
| 		{ | ||||
| 			logAi->trace("No goals found. Ending turn."); | ||||
|  | ||||
| 			return; | ||||
| 		} | ||||
| 		}*/ | ||||
|  | ||||
| 		if(bestTask->priority < MIN_PRIORITY) | ||||
| 		{ | ||||
| 			logAi->trace("Goal %s has too low priority. It is not worth doing it. Ending turn.", bestTask->name()); | ||||
| 			logAi->trace("Goal %s has too low priority. It is not worth doing it. Ending turn.", bestTask->toString()); | ||||
|  | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		logAi->debug("Trying to realize %s (value %2.3f)", bestTask->name(), bestTask->priority); | ||||
| 		logAi->debug("Trying to realize %s (value %2.3f)", bestTask->toString(), bestTask->priority); | ||||
|  | ||||
| 		try | ||||
| 		{ | ||||
| 			if(bestTask->hero) | ||||
| 			{ | ||||
| 				setActive(bestTask->hero.get(), bestTask->tile); | ||||
| 			} | ||||
|  | ||||
| 			bestTask->accept(ai.get()); | ||||
| 		} | ||||
| 		catch(goalFulfilledException &) | ||||
| 		{ | ||||
| 			logAi->trace("Task %s completed", bestTask->name()); | ||||
| 			logAi->trace("Task %s completed", bestTask->toString()); | ||||
| 		} | ||||
| 		catch(std::exception & e) | ||||
| 		{ | ||||
| 			logAi->debug("Failed to realize subgoal of type %s, I will stop.", bestTask->name()); | ||||
| 			logAi->debug("Failed to realize subgoal of type %s, I will stop.", bestTask->toString()); | ||||
| 			logAi->debug("The error message was: %s", e.what()); | ||||
|  | ||||
| 			return; | ||||
|   | ||||
| @@ -13,7 +13,6 @@ | ||||
| #include "../Analyzers/DangerHitMapAnalyzer.h" | ||||
| #include "../Analyzers/BuildAnalyzer.h" | ||||
| #include "../Goals/AbstractGoal.h" | ||||
| #include "../Behaviors/Behavior.h" | ||||
|  | ||||
| const float MAX_GOLD_PEASURE = 0.3f; | ||||
| const float MIN_PRIORITY = 0.01f; | ||||
| @@ -56,6 +55,6 @@ public: | ||||
| private: | ||||
| 	void resetAiState(); | ||||
| 	void updateAiState(); | ||||
| 	Goals::TSubgoal choseBestTask(std::shared_ptr<Behavior> behavior) const; | ||||
| 	Goals::TSubgoal choseBestTask(Goals::TGoalVec & tasks) const; | ||||
| 	Goals::TTask choseBestTask(Goals::TSubgoal behavior) const; | ||||
| 	Goals::TTask choseBestTask(Goals::TTaskVec & tasks) const; | ||||
| }; | ||||
|   | ||||
| @@ -520,9 +520,6 @@ Goals::EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgo | ||||
| /// importance | ||||
| float PriorityEvaluator::evaluate(Goals::TSubgoal task) | ||||
| { | ||||
| 	if(task->priority > 0) | ||||
| 		return task->priority; | ||||
|  | ||||
| 	auto evaluationContext = buildEvaluationContext(task); | ||||
|  | ||||
| 	int rewardType = (evaluationContext.goldReward > 0 ? 1 : 0)  | ||||
| @@ -561,7 +558,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task) | ||||
|  | ||||
| #ifdef VCMI_TRACE_PATHFINDER | ||||
| 	logAi->trace("Evaluated %s, loss: %f, turns main: %f, scout: %f, gold: %d, cost: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, cwr: %f, result %f", | ||||
| 		task->name(), | ||||
| 		task->toString(), | ||||
| 		evaluationContext.armyLossPersentage, | ||||
| 		evaluationContext.movementCostByRole[HeroRole::MAIN], | ||||
| 		evaluationContext.movementCostByRole[HeroRole::SCOUT], | ||||
|   | ||||
| @@ -89,19 +89,6 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army) | ||||
| 	return as; | ||||
| } | ||||
|  | ||||
| float HeroMovementGoalEngineBase::calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const | ||||
| { | ||||
| 	if(goal.evaluationContext.movementCost > 0) | ||||
| 	{ | ||||
| 		return goal.evaluationContext.movementCost; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		auto pathInfo = ai->myCb->getPathsInfo(goal.hero.h)->getPathInfo(goal.tile); | ||||
| 		return pathInfo->cost; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| TacticalAdvantageEngine::TacticalAdvantageEngine() | ||||
| { | ||||
| 	try | ||||
| @@ -254,206 +241,3 @@ float TacticalAdvantageEngine::getTacticalAdvantage(const CArmedInstance * we, c | ||||
|  | ||||
| 	return output; | ||||
| } | ||||
|  | ||||
| //std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec) | ||||
|  | ||||
| HeroMovementGoalEngineBase::HeroMovementGoalEngineBase() | ||||
| { | ||||
| 	try | ||||
| 	{ | ||||
| 		strengthRatio = new fl::InputVariable("strengthRatio"); //hero must be strong enough to defeat guards | ||||
| 		heroStrengthVariable = new fl::InputVariable("heroStrengthVariable"); //we want to use weakest possible hero | ||||
| 		turnDistanceVariable = new fl::InputVariable("turnDistanceVariable"); //we want to use hero who is near | ||||
| 		missionImportance = new fl::InputVariable("lockedMissionImportance"); //we may want to preempt hero with low-priority mission | ||||
| 		value = new fl::OutputVariable("Value"); | ||||
| 		value->setMinimum(0); | ||||
| 		value->setMaximum(5); | ||||
|  | ||||
| 		std::vector<fl::InputVariable *> helper = { strengthRatio, heroStrengthVariable, turnDistanceVariable, missionImportance }; | ||||
| 		for(auto val : helper) | ||||
| 		{ | ||||
| 			engine.addInputVariable(val); | ||||
| 		} | ||||
| 		engine.addOutputVariable(value); | ||||
|  | ||||
| 		strengthRatio->addTerm(new fl::Ramp("LOW", SAFE_ATTACK_CONSTANT, 0)); | ||||
| 		strengthRatio->addTerm(new fl::Ramp("HIGH", SAFE_ATTACK_CONSTANT, SAFE_ATTACK_CONSTANT * 3)); | ||||
| 		strengthRatio->setRange(0, SAFE_ATTACK_CONSTANT * 3); | ||||
|  | ||||
| 		//strength compared to our main hero | ||||
| 		heroStrengthVariable->addTerm(new fl::Ramp("LOW", 0.5, 0)); | ||||
| 		heroStrengthVariable->addTerm(new fl::Triangle("MEDIUM", 0.2, 0.8)); | ||||
| 		heroStrengthVariable->addTerm(new fl::Ramp("HIGH", 0.5, 1)); | ||||
| 		heroStrengthVariable->setRange(0.0, 1.0); | ||||
|  | ||||
| 		turnDistanceVariable->addTerm(new fl::Ramp("SHORT", 0.5, 0)); | ||||
| 		turnDistanceVariable->addTerm(new fl::Triangle("MEDIUM", 0.1, 0.8)); | ||||
| 		turnDistanceVariable->addTerm(new fl::Ramp("LONG", 0.5, 10)); | ||||
| 		turnDistanceVariable->setRange(0.0, 10.0); | ||||
|  | ||||
| 		missionImportance->addTerm(new fl::Ramp("LOW", 2.5, 0)); | ||||
| 		missionImportance->addTerm(new fl::Triangle("MEDIUM", 2, 3)); | ||||
| 		missionImportance->addTerm(new fl::Ramp("HIGH", 2.5, 5)); | ||||
| 		missionImportance->setRange(0.0, 5.0); | ||||
|  | ||||
| 		//an issue: in 99% cases this outputs center of mass (2.5) regardless of actual input :/ | ||||
| 		//should be same as "mission Importance" to keep consistency | ||||
| 		value->addTerm(new fl::Ramp("LOW", 2.5, 0)); | ||||
| 		value->addTerm(new fl::Triangle("MEDIUM", 2, 3)); //can't be center of mass :/ | ||||
| 		value->addTerm(new fl::Ramp("HIGH", 2.5, 5)); | ||||
| 		value->setRange(0.0, 5.0); | ||||
|  | ||||
| 		//use unarmed scouts if possible | ||||
| 		addRule("if strengthRatio is HIGH and heroStrengthVariable is LOW then Value is HIGH"); | ||||
| 		//we may want to use secondary hero(es) rather than main hero | ||||
| 		addRule("if strengthRatio is HIGH and heroStrengthVariable is MEDIUM then Value is MEDIUM"); | ||||
| 		addRule("if strengthRatio is HIGH and heroStrengthVariable is HIGH then Value is LOW"); | ||||
| 		//don't assign targets to heroes who are too weak, but prefer targets of our main hero (in case we need to gather army) | ||||
| 		addRule("if strengthRatio is LOW and heroStrengthVariable is LOW then Value is LOW"); | ||||
| 		//attempt to arm secondary heroes is not stupid | ||||
| 		addRule("if strengthRatio is LOW and heroStrengthVariable is MEDIUM then Value is HIGH"); | ||||
| 		addRule("if strengthRatio is LOW and heroStrengthVariable is HIGH then Value is LOW"); | ||||
|  | ||||
| 		//do not cancel important goals | ||||
| 		addRule("if lockedMissionImportance is HIGH then Value is LOW"); | ||||
| 		addRule("if lockedMissionImportance is MEDIUM then Value is MEDIUM"); | ||||
| 		addRule("if lockedMissionImportance is LOW then Value is HIGH"); | ||||
| 		//pick nearby objects if it's easy, avoid long walks | ||||
| 		addRule("if turnDistanceVariable is SHORT then Value is HIGH"); | ||||
| 		addRule("if turnDistanceVariable is MEDIUM then Value is MEDIUM"); | ||||
| 		addRule("if turnDistanceVariable is LONG then Value is LOW"); | ||||
| 	} | ||||
| 	catch(fl::Exception & fe) | ||||
| 	{ | ||||
| 		logAi->error("HeroMovementGoalEngineBase: %s", fe.getWhat()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void HeroMovementGoalEngineBase::setSharedFuzzyVariables(Goals::AbstractGoal & goal) | ||||
| { | ||||
| 	float turns = calculateTurnDistanceInputValue(goal); | ||||
| 	float missionImportanceData = 0; | ||||
|  | ||||
| 	if(vstd::contains(ai->lockedHeroes, goal.hero)) | ||||
| 	{ | ||||
| 		missionImportanceData = ai->lockedHeroes[goal.hero]->priority; | ||||
| 	} | ||||
| 	else if(goal.parent) | ||||
| 	{ | ||||
| 		missionImportanceData = goal.parent->priority; | ||||
| 	} | ||||
|  | ||||
| 	float strengthRatioData = 10.0f; //we are much stronger than enemy | ||||
| 	ui64 danger = fh->evaluateDanger(goal.tile, goal.hero.h); | ||||
| 	if(danger) | ||||
| 		strengthRatioData = (fl::scalar)goal.hero.h->getTotalStrength() / danger; | ||||
|  | ||||
| 	try | ||||
| 	{ | ||||
| 		strengthRatio->setValue(strengthRatioData); | ||||
| 		heroStrengthVariable->setValue((fl::scalar)goal.hero->getTotalStrength() / ai->primaryHero()->getTotalStrength()); | ||||
| 		turnDistanceVariable->setValue(turns); | ||||
| 		missionImportance->setValue(missionImportanceData); | ||||
| 	} | ||||
| 	catch(fl::Exception & fe) | ||||
| 	{ | ||||
| 		logAi->error("HeroMovementGoalEngineBase::setSharedFuzzyVariables: %s", fe.getWhat()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| VisitObjEngine::VisitObjEngine() | ||||
| { | ||||
| 	try | ||||
| 	{ | ||||
| 		objectValue = new fl::InputVariable("objectValue"); //value of that object type known by AI | ||||
|  | ||||
| 		engine.addInputVariable(objectValue); | ||||
|  | ||||
| 		//objectValue ranges are based on checking RMG priorities of some objects and checking LOW/MID/HIGH proportions for various values in QtFuzzyLite | ||||
| 		objectValue->addTerm(new fl::Ramp("LOW", 3500.0, 0.0)); | ||||
| 		objectValue->addTerm(new fl::Triangle("MEDIUM", 0.0, 8500.0)); | ||||
| 		std::vector<fl::Discrete::Pair> multiRamp = { fl::Discrete::Pair(5000.0, 0.0), fl::Discrete::Pair(10000.0, 0.75), fl::Discrete::Pair(20000.0, 1.0) }; | ||||
| 		objectValue->addTerm(new fl::Discrete("HIGH", multiRamp)); | ||||
| 		objectValue->setRange(0.0, 20000.0); //relic artifact value is border value by design, even better things are scaled down. | ||||
|  | ||||
| 		addRule("if objectValue is HIGH then Value is HIGH"); | ||||
| 		addRule("if objectValue is MEDIUM then Value is MEDIUM"); | ||||
| 		addRule("if objectValue is LOW then Value is LOW"); | ||||
| 	} | ||||
| 	catch(fl::Exception & fe) | ||||
| 	{ | ||||
| 		logAi->error("FindWanderTarget: %s", fe.getWhat()); | ||||
| 	} | ||||
| 	configure(); | ||||
| } | ||||
|  | ||||
| float VisitObjEngine::evaluate(Goals::VisitObj & goal) | ||||
| { | ||||
| 	if(!goal.hero) | ||||
| 		return 0; | ||||
|  | ||||
| 	auto obj = ai->myCb->getObj(ObjectInstanceID(goal.objid)); | ||||
| 	if(!obj) | ||||
| 	{ | ||||
| 		logAi->error("Goals::VisitObj objid " + std::to_string(goal.objid) + " no longer visible, probably goal used for something it's not intended"); | ||||
| 		return -100; // FIXME: Added check when goal was used for hero instead of VisitHero, but crashes are bad anyway | ||||
| 	} | ||||
|  | ||||
| 	boost::optional<int> objValueKnownByAI = MapObjectsEvaluator::getInstance().getObjectValue(obj); | ||||
| 	int objValue = 0; | ||||
|  | ||||
| 	if(objValueKnownByAI != boost::none) //consider adding value manipulation based on object instances on map | ||||
| 	{ | ||||
| 		objValue = std::min(std::max(objValueKnownByAI.get(), 0), 20000); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		MapObjectsEvaluator::getInstance().addObjectData(obj->ID, obj->subID, 0); | ||||
| 		logGlobal->error("AI met object type it doesn't know - ID: " + std::to_string(obj->ID) + ", subID: " + std::to_string(obj->subID) + " - adding to database with value " + std::to_string(objValue)); | ||||
| 	} | ||||
|  | ||||
| 	setSharedFuzzyVariables(goal); | ||||
|  | ||||
| 	float output = -1.0f; | ||||
| 	try | ||||
| 	{ | ||||
| 		objectValue->setValue(objValue); | ||||
| 		engine.process(); | ||||
| 		output = value->getValue(); | ||||
| 	} | ||||
| 	catch(fl::Exception & fe) | ||||
| 	{ | ||||
| 		logAi->error("evaluate getWanderTargetObjectValue: %s", fe.getWhat()); | ||||
| 	} | ||||
| 	assert(output >= 0.0f); | ||||
| 	return output; | ||||
| } | ||||
|  | ||||
| VisitTileEngine::VisitTileEngine() //so far no VisitTile-specific variables that are not shared with HeroMovementGoalEngineBase | ||||
| { | ||||
| 	configure(); | ||||
| } | ||||
|  | ||||
| float VisitTileEngine::evaluate(Goals::VisitTile & goal) | ||||
| { | ||||
| 	//we assume that hero is already set and we want to choose most suitable one for the mission | ||||
| 	if(!goal.hero) | ||||
| 		return 0; | ||||
|  | ||||
| 	//assert(cb->isInTheMap(g.tile)); | ||||
|  | ||||
| 	setSharedFuzzyVariables(goal); | ||||
|  | ||||
| 	try | ||||
| 	{ | ||||
| 		engine.process(); | ||||
|  | ||||
| 		goal.priority = value->getValue(); | ||||
| 	} | ||||
| 	catch(fl::Exception & fe) | ||||
| 	{ | ||||
| 		logAi->error("evaluate VisitTile: %s", fe.getWhat()); | ||||
| 	} | ||||
| 	assert(goal.priority >= 0); | ||||
| 	return goal.priority; | ||||
| } | ||||
|   | ||||
| @@ -36,37 +36,3 @@ private: | ||||
| 	fl::InputVariable * castleWalls; | ||||
| 	fl::OutputVariable * threat; | ||||
| }; | ||||
|  | ||||
| class HeroMovementGoalEngineBase : public engineBase //in future - maybe derive from some (GoalEngineBase : public engineBase) class for handling non-movement goals with common utility for goal engines | ||||
| { | ||||
| public: | ||||
| 	HeroMovementGoalEngineBase(); | ||||
|  | ||||
| protected: | ||||
| 	void setSharedFuzzyVariables(Goals::AbstractGoal & goal); | ||||
|  | ||||
| 	fl::InputVariable * strengthRatio; | ||||
| 	fl::InputVariable * heroStrengthVariable; | ||||
| 	fl::InputVariable * turnDistanceVariable; | ||||
| 	fl::InputVariable * missionImportance; | ||||
| 	fl::OutputVariable * value; | ||||
|  | ||||
| private: | ||||
| 	float calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const; | ||||
| }; | ||||
|  | ||||
| class VisitTileEngine : public HeroMovementGoalEngineBase | ||||
| { | ||||
| public: | ||||
| 	VisitTileEngine(); | ||||
| 	float evaluate(Goals::VisitTile & goal); | ||||
| }; | ||||
|  | ||||
| class VisitObjEngine : public HeroMovementGoalEngineBase | ||||
| { | ||||
| public: | ||||
| 	VisitObjEngine(); | ||||
| 	float evaluate(Goals::VisitObj & goal); | ||||
| protected: | ||||
| 	fl::InputVariable * objectValue; | ||||
| }; | ||||
|   | ||||
| @@ -29,7 +29,19 @@ TSubgoal Goals::sptr(const AbstractGoal & tmp) | ||||
| 	return ptr; | ||||
| } | ||||
|  | ||||
| std::string AbstractGoal::name() const //TODO: virtualize | ||||
| TTask Goals::taskptr(const AbstractGoal & tmp) | ||||
| { | ||||
| 	TTask ptr; | ||||
|  | ||||
| 	if(!tmp.isElementar) | ||||
| 		throw cannotFulfillGoalException(tmp.toString() + " is not elementar"); | ||||
|  | ||||
| 	ptr.reset(dynamic_cast<ITask *>(tmp.clone())); | ||||
|  | ||||
| 	return ptr; | ||||
| } | ||||
|  | ||||
| std::string AbstractGoal::toString() const //TODO: virtualize | ||||
| { | ||||
| 	std::string desc; | ||||
| 	switch(goalType) | ||||
| @@ -124,11 +136,6 @@ bool AbstractGoal::invalid() const | ||||
| 	return goalType == EGoals::INVALID; | ||||
| } | ||||
|  | ||||
| void AbstractGoal::accept(VCAI * ai) | ||||
| { | ||||
| 	ai->tryRealize(*this); | ||||
| } | ||||
|  | ||||
| EvaluationContext::EvaluationContext() | ||||
| 	: movementCost(0.0), | ||||
| 	manaCost(0), | ||||
|   | ||||
| @@ -22,21 +22,16 @@ class FuzzyHelper; | ||||
| namespace Goals | ||||
| { | ||||
| 	class AbstractGoal; | ||||
| 	class Explore; | ||||
| 	class ITask; | ||||
| 	class RecruitHero; | ||||
| 	class VisitTile; | ||||
| 	class VisitObj; | ||||
| 	class VisitHero; | ||||
| 	class BuildThis; | ||||
| 	class DigAtTile; | ||||
| 	class CollectRes; | ||||
| 	class BuyArmy; | ||||
| 	class BuildBoat; | ||||
| 	class GatherArmy; | ||||
| 	class ClearWayTo; | ||||
| 	class Invalid; | ||||
| 	class Trade; | ||||
| 	class CompleteQuest; | ||||
| 	class AdventureSpellCast; | ||||
|  | ||||
| 	enum EGoals | ||||
| @@ -78,6 +73,8 @@ namespace Goals | ||||
| 		//TODO: serialize? | ||||
| 	}; | ||||
|  | ||||
| 	typedef std::shared_ptr<ITask> TTask; | ||||
| 	typedef std::vector<TTask> TTaskVec; | ||||
| 	typedef std::vector<TSubgoal> TGoalVec; | ||||
|  | ||||
| 	//method chaining + clone pattern | ||||
| @@ -91,6 +88,7 @@ namespace Goals | ||||
| 	enum { LOW_PR = -1 }; | ||||
|  | ||||
| 	DLL_EXPORT TSubgoal sptr(const AbstractGoal & tmp); | ||||
| 	DLL_EXPORT TTask taskptr(const AbstractGoal & tmp); | ||||
|  | ||||
| 	struct DLL_EXPORT EvaluationContext | ||||
| 	{ | ||||
| @@ -117,23 +115,21 @@ namespace Goals | ||||
| 	{ | ||||
| 	public: | ||||
| 		bool isElementar; VSETTER(bool, isElementar) | ||||
| 			bool isAbstract; VSETTER(bool, isAbstract) | ||||
| 			float priority; VSETTER(float, priority) | ||||
| 			int value; VSETTER(int, value) | ||||
| 			int resID; VSETTER(int, resID) | ||||
| 			int objid; VSETTER(int, objid) | ||||
| 			int aid; VSETTER(int, aid) | ||||
| 			int3 tile; VSETTER(int3, tile) | ||||
| 			HeroPtr hero; VSETTER(HeroPtr, hero) | ||||
| 			const CGTownInstance *town; VSETTER(CGTownInstance *, town) | ||||
| 			int bid; VSETTER(int, bid) | ||||
| 			TSubgoal parent; VSETTER(TSubgoal, parent) | ||||
| 			EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext) | ||||
| 		bool isAbstract; VSETTER(bool, isAbstract) | ||||
| 		int value; VSETTER(int, value) | ||||
| 		int resID; VSETTER(int, resID) | ||||
| 		int objid; VSETTER(int, objid) | ||||
| 		int aid; VSETTER(int, aid) | ||||
| 		int3 tile; VSETTER(int3, tile) | ||||
| 		HeroPtr hero; VSETTER(HeroPtr, hero) | ||||
| 		const CGTownInstance *town; VSETTER(CGTownInstance *, town) | ||||
| 		int bid; VSETTER(int, bid) | ||||
| 		TSubgoal parent; VSETTER(TSubgoal, parent) | ||||
| 		EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext) | ||||
|  | ||||
| 			AbstractGoal(EGoals goal = EGoals::INVALID) | ||||
| 		AbstractGoal(EGoals goal = EGoals::INVALID) | ||||
| 			: goalType(goal), evaluationContext() | ||||
| 		{ | ||||
| 			priority = 0; | ||||
| 			isElementar = false; | ||||
| 			isAbstract = false; | ||||
| 			value = 0; | ||||
| @@ -150,25 +146,18 @@ namespace Goals | ||||
| 		{ | ||||
| 			return const_cast<AbstractGoal *>(this); | ||||
| 		} | ||||
| 		virtual TGoalVec getAllPossibleSubgoals() | ||||
|  | ||||
| 		virtual TGoalVec decompose() const | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
| 		virtual TSubgoal whatToDoToAchieve() | ||||
| 		{ | ||||
| 			return sptr(AbstractGoal()); | ||||
| 		} | ||||
|  | ||||
| 		EGoals goalType; | ||||
|  | ||||
| 		virtual std::string name() const; | ||||
| 		virtual std::string toString() const; | ||||
|  | ||||
| 		bool invalid() const; | ||||
|  | ||||
| 		///Visitor pattern | ||||
| 		//TODO: make accept work for std::shared_ptr... somehow | ||||
| 		virtual void accept(VCAI * ai); //unhandled goal will report standard error | ||||
|  | ||||
| 		 | ||||
| 		virtual bool operator==(const AbstractGoal & g) const; | ||||
| 		 | ||||
| 		bool operator!=(const AbstractGoal & g) const | ||||
| @@ -192,4 +181,15 @@ namespace Goals | ||||
| 			h & bid; | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	class DLL_EXPORT ITask | ||||
| 	{ | ||||
| 	public: | ||||
| 		float priority; | ||||
|  | ||||
| 		///Visitor pattern | ||||
| 		//TODO: make accept work for std::shared_ptr... somehow | ||||
| 		virtual void accept(VCAI * ai) = 0; //unhandled goal will report standard error | ||||
| 		virtual std::string toString() const = 0; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -26,7 +26,7 @@ bool AdventureSpellCast::operator==(const AdventureSpellCast & other) const | ||||
| 	return hero.h == other.hero.h; | ||||
| } | ||||
|  | ||||
| TSubgoal AdventureSpellCast::whatToDoToAchieve() | ||||
| void AdventureSpellCast::accept(VCAI * ai) | ||||
| { | ||||
| 	if(!hero.validAndSet()) | ||||
| 		throw cannotFulfillGoalException("Invalid hero!"); | ||||
| @@ -47,11 +47,6 @@ TSubgoal AdventureSpellCast::whatToDoToAchieve() | ||||
| 	if(spellID == SpellID::TOWN_PORTAL && town && town->visitingHero) | ||||
| 		throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->name); | ||||
|  | ||||
| 	return iAmElementar(); | ||||
| } | ||||
|  | ||||
| void AdventureSpellCast::accept(VCAI * ai) | ||||
| { | ||||
| 	if(town && spellID == SpellID::TOWN_PORTAL) | ||||
| 	{ | ||||
| 		ai->selectedObject = town->id; | ||||
| @@ -73,7 +68,7 @@ void AdventureSpellCast::accept(VCAI * ai) | ||||
| 	throw goalFulfilledException(sptr(*this)); | ||||
| } | ||||
|  | ||||
| std::string AdventureSpellCast::name() const | ||||
| std::string AdventureSpellCast::toString() const | ||||
| { | ||||
| 	return "AdventureSpellCast " + spellID.toSpell()->name; | ||||
| } | ||||
|   | ||||
| @@ -13,31 +13,25 @@ | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	class DLL_EXPORT AdventureSpellCast : public CGoal<AdventureSpellCast> | ||||
| 	class DLL_EXPORT AdventureSpellCast : public ElementarGoal<AdventureSpellCast> | ||||
| 	{ | ||||
| 	private: | ||||
| 		SpellID spellID; | ||||
|  | ||||
| 	public: | ||||
| 		AdventureSpellCast(HeroPtr hero, SpellID spellID) | ||||
| 			: CGoal(Goals::ADVENTURE_SPELL_CAST), spellID(spellID) | ||||
| 			: ElementarGoal(Goals::ADVENTURE_SPELL_CAST), spellID(spellID) | ||||
| 		{ | ||||
| 			sethero(hero); | ||||
| 		} | ||||
|  | ||||
| 		TGoalVec getAllPossibleSubgoals() override | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
|  | ||||
| 		const CSpell * getSpell() const | ||||
| 		{  | ||||
| 			return spellID.toSpell(); | ||||
| 		} | ||||
|  | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		void accept(VCAI * ai) override; | ||||
| 		std::string name() const override; | ||||
| 		std::string toString() const override; | ||||
| 		virtual bool operator==(const AdventureSpellCast & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -14,6 +14,7 @@ | ||||
| #include "../AIhelper.h" | ||||
| #include "../../../lib/mapping/CMap.h" //for victory conditions | ||||
| #include "../../../lib/CPathfinder.h" | ||||
| #include "../Behaviors/CaptureObjectsBehavior.h" | ||||
|  | ||||
| extern boost::thread_specific_ptr<CCallback> cb; | ||||
| extern boost::thread_specific_ptr<VCAI> ai; | ||||
| @@ -26,23 +27,23 @@ bool BuildBoat::operator==(const BuildBoat & other) const | ||||
| 	return shipyard->o->id == other.shipyard->o->id; | ||||
| } | ||||
|  | ||||
| //TSubgoal BuildBoat::whatToDoToAchieve() | ||||
| //{ | ||||
| //	if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) | ||||
| //	{ | ||||
| //		return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o)); | ||||
| //	} | ||||
| // | ||||
| //	if(shipyard->shipyardStatus() != IShipyard::GOOD) | ||||
| //	{ | ||||
| //		throw cannotFulfillGoalException("Shipyard is busy."); | ||||
| //	} | ||||
| // | ||||
| //	TResources boatCost; | ||||
| //	shipyard->getBoatCost(boatCost); | ||||
| // | ||||
| //	return ai->ah->whatToDo(boatCost, this->iAmElementar()); | ||||
| //} | ||||
| TSubgoal BuildBoat::decomposeSingle() const | ||||
| { | ||||
| 	if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) | ||||
| 	{ | ||||
| 		return sptr(CaptureObjectsBehavior(shipyard->o)); | ||||
| 	} | ||||
|  | ||||
| 	if(shipyard->shipyardStatus() != IShipyard::GOOD) | ||||
| 	{ | ||||
| 		throw cannotFulfillGoalException("Shipyard is busy."); | ||||
| 	} | ||||
|  | ||||
| 	TResources boatCost; | ||||
| 	shipyard->getBoatCost(boatCost); | ||||
|  | ||||
| 	return iAmElementar(); | ||||
| } | ||||
|  | ||||
| void BuildBoat::accept(VCAI * ai) | ||||
| { | ||||
| @@ -75,7 +76,7 @@ void BuildBoat::accept(VCAI * ai) | ||||
| 	throw goalFulfilledException(sptr(*this)); | ||||
| } | ||||
|  | ||||
| std::string BuildBoat::name() const | ||||
| std::string BuildBoat::toString() const | ||||
| { | ||||
| 	return "BuildBoat"; | ||||
| } | ||||
|   | ||||
| @@ -13,23 +13,20 @@ | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	class DLL_EXPORT BuildBoat : public CGoal<BuildBoat> | ||||
| 	class DLL_EXPORT BuildBoat : public ElementarGoal<BuildBoat> | ||||
| 	{ | ||||
| 	private: | ||||
| 		const IShipyard * shipyard; | ||||
| 		TSubgoal decomposeSingle() const override; | ||||
|  | ||||
| 	public: | ||||
| 		BuildBoat(const IShipyard * shipyard) | ||||
| 			: CGoal(Goals::BUILD_BOAT), shipyard(shipyard) | ||||
| 			: ElementarGoal(Goals::BUILD_BOAT), shipyard(shipyard) | ||||
| 		{ | ||||
| 			priority = 0; | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
|  | ||||
| 		void accept(VCAI * ai) override; | ||||
| 		std::string name() const override; | ||||
| 		std::string toString() const override; | ||||
| 		virtual bool operator==(const BuildBoat & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -29,7 +29,7 @@ bool BuildThis::operator==(const BuildThis & other) const | ||||
| 	return town == other.town && bid == other.bid; | ||||
| } | ||||
|  | ||||
| std::string BuildThis::name() const | ||||
| std::string BuildThis::toString() const | ||||
| { | ||||
| 	return "Build " + buildingInfo.name + "(" + std::to_string(bid) + ") in " + town->name; | ||||
| } | ||||
| @@ -18,36 +18,36 @@ class FuzzyHelper; | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	class DLL_EXPORT BuildThis : public CGoal<BuildThis> | ||||
| 	class DLL_EXPORT BuildThis : public ElementarGoal<BuildThis> | ||||
| 	{ | ||||
| 	public: | ||||
| 		BuildingInfo buildingInfo; | ||||
| 		TownDevelopmentInfo townInfo; | ||||
|  | ||||
| 		BuildThis() //should be private, but unit test uses it | ||||
| 			: CGoal(Goals::BUILD_STRUCTURE) | ||||
| 			: ElementarGoal(Goals::BUILD_STRUCTURE) | ||||
| 		{ | ||||
| 		} | ||||
| 		BuildThis(const BuildingInfo & buildingInfo, const TownDevelopmentInfo & townInfo) //should be private, but unit test uses it | ||||
| 			: CGoal(Goals::BUILD_STRUCTURE), buildingInfo(buildingInfo), townInfo(townInfo) | ||||
| 			: ElementarGoal(Goals::BUILD_STRUCTURE), buildingInfo(buildingInfo), townInfo(townInfo) | ||||
| 		{ | ||||
| 			bid = buildingInfo.id; | ||||
| 			town = townInfo.town; | ||||
| 		} | ||||
| 		BuildThis(BuildingID Bid, const CGTownInstance * tid) | ||||
| 			: CGoal(Goals::BUILD_STRUCTURE) | ||||
| 			: ElementarGoal(Goals::BUILD_STRUCTURE) | ||||
| 		{ | ||||
| 			bid = Bid; | ||||
| 			town = tid; | ||||
| 			priority = 1; | ||||
| 		} | ||||
| 		BuildThis(BuildingID Bid) | ||||
| 			: CGoal(Goals::BUILD_STRUCTURE) | ||||
| 			: ElementarGoal(Goals::BUILD_STRUCTURE) | ||||
| 		{ | ||||
| 			bid = Bid; | ||||
| 			priority = 1; | ||||
| 		} | ||||
| 		virtual bool operator==(const BuildThis & other) const override; | ||||
| 		virtual std::string name() const override; | ||||
| 		virtual std::string toString() const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -25,7 +25,7 @@ bool BuyArmy::operator==(const BuyArmy & other) const | ||||
| 	return town == other.town && objid == other.objid; | ||||
| } | ||||
|  | ||||
| TSubgoal BuyArmy::whatToDoToAchieve() | ||||
| std::string BuyArmy::toString() const | ||||
| { | ||||
| 	return iAmElementar(); | ||||
| 	return "Buy army at " + town->name; | ||||
| } | ||||
| @@ -17,23 +17,24 @@ class FuzzyHelper; | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	class DLL_EXPORT BuyArmy : public CGoal<BuyArmy> | ||||
| 	class DLL_EXPORT BuyArmy : public ElementarGoal<BuyArmy> | ||||
| 	{ | ||||
| 	private: | ||||
| 		BuyArmy() | ||||
| 			: CGoal(Goals::BUY_ARMY) | ||||
| 			: ElementarGoal(Goals::BUY_ARMY) | ||||
| 		{ | ||||
| 		} | ||||
| 	public: | ||||
| 		BuyArmy(const CGTownInstance * Town, int val) | ||||
| 			: CGoal(Goals::BUY_ARMY) | ||||
| 			: ElementarGoal(Goals::BUY_ARMY) | ||||
| 		{ | ||||
| 			town = Town; //where to buy this army | ||||
| 			value = val; //expressed in AI unit strength | ||||
| 			priority = 3;//TODO: evaluate? | ||||
| 		} | ||||
|  | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		virtual bool operator==(const BuyArmy & other) const override; | ||||
|  | ||||
| 		virtual std::string toString() const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -23,9 +23,8 @@ namespace Goals | ||||
| 	public: | ||||
| 		CGoal<T>(EGoals goal = INVALID) : AbstractGoal(goal) | ||||
| 		{ | ||||
| 			priority = 0; | ||||
| 			isElementar = false; | ||||
| 			isAbstract = false; | ||||
| 			isAbstract = true; | ||||
| 			value = 0; | ||||
| 			aid = -1; | ||||
| 			objid = -1; | ||||
| @@ -36,7 +35,6 @@ namespace Goals | ||||
|  | ||||
| 		OSETTER(bool, isElementar) | ||||
| 		OSETTER(bool, isAbstract) | ||||
| 		OSETTER(float, priority) | ||||
| 		OSETTER(int, value) | ||||
| 		OSETTER(int, resID) | ||||
| 		OSETTER(int, objid) | ||||
| @@ -46,11 +44,6 @@ namespace Goals | ||||
| 		OSETTER(CGTownInstance *, town) | ||||
| 		OSETTER(int, bid) | ||||
|  | ||||
| 		void accept(VCAI * ai) override | ||||
| 		{ | ||||
| 			ai->tryRealize(static_cast<T &>(*this)); //casting enforces template instantiation | ||||
| 		} | ||||
|  | ||||
| 		CGoal<T> * clone() const override | ||||
| 		{ | ||||
| 			return new T(static_cast<T const &>(*this)); //casting enforces template instantiation | ||||
| @@ -80,5 +73,65 @@ namespace Goals | ||||
| 		} | ||||
|  | ||||
| 		virtual bool operator==(const T & other) const = 0; | ||||
|  | ||||
| 		virtual TGoalVec decompose() const override | ||||
| 		{ | ||||
| 			return {decomposeSingle()}; | ||||
| 		} | ||||
|  | ||||
| 	protected: | ||||
| 		virtual TSubgoal decomposeSingle() const | ||||
| 		{ | ||||
| 			return sptr(Invalid()); | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	template<typename T> class DLL_EXPORT ElementarGoal : public CGoal<T>, public ITask | ||||
| 	{ | ||||
| 	public: | ||||
| 		ElementarGoal<T>(EGoals goal = INVALID) : CGoal(goal) | ||||
| 		{ | ||||
| 			priority = 0; | ||||
| 			isElementar = true; | ||||
| 			isAbstract = false; | ||||
| 		} | ||||
|  | ||||
| 		///Visitor pattern | ||||
| 		//TODO: make accept work for std::shared_ptr... somehow | ||||
| 		virtual void accept(VCAI * ai) override //unhandled goal will report standard error | ||||
| 		{ | ||||
| 			ai->tryRealize(*this); | ||||
| 		} | ||||
|  | ||||
| 		T & setpriority(float p) | ||||
| 		{ | ||||
| 			priority = p; | ||||
|  | ||||
| 			return *((T *)this); | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	class DLL_EXPORT Invalid : public ElementarGoal<Invalid> | ||||
| 	{ | ||||
| 	public: | ||||
| 		Invalid() | ||||
| 			: ElementarGoal(Goals::INVALID) | ||||
| 		{ | ||||
| 			priority = -1; | ||||
| 		} | ||||
| 		TGoalVec decompose() const override | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
|  | ||||
| 		virtual bool operator==(const Invalid & other) const override | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 		virtual std::string toString() const override | ||||
| 		{ | ||||
| 			return "Invalid"; | ||||
| 		} | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -29,10 +29,10 @@ bool CollectRes::operator==(const CollectRes & other) const | ||||
| 	return resID == other.resID; | ||||
| } | ||||
|  | ||||
| TGoalVec CollectRes::getAllPossibleSubgoals() | ||||
| { | ||||
| 	TGoalVec ret; | ||||
|  | ||||
| //TGoalVec CollectRes::getAllPossibleSubgoals() | ||||
| //{ | ||||
| //	TGoalVec ret; | ||||
| // | ||||
| 	//auto givesResource = [this](const CGObjectInstance * obj) -> bool | ||||
| 	//{ | ||||
| 	//	//TODO: move this logic to object side | ||||
| @@ -107,18 +107,18 @@ TGoalVec CollectRes::getAllPossibleSubgoals() | ||||
| 	//		vstd::concatenate(ret, waysToGo); | ||||
| 	//	} | ||||
| 	//} | ||||
| 	return ret; | ||||
| } | ||||
| //	return ret; | ||||
| //} | ||||
|  | ||||
| TSubgoal CollectRes::whatToDoToAchieve() | ||||
| { | ||||
| 	auto goals = getAllPossibleSubgoals(); | ||||
| 	auto trade = whatToDoToTrade(); | ||||
| 	if (!trade->invalid()) | ||||
| 		goals.push_back(trade); | ||||
|  | ||||
| 	return sptr(Invalid()); //we can always do that | ||||
| } | ||||
| //TSubgoal CollectRes::whatToDoToAchieve() | ||||
| //{ | ||||
| //	auto goals = getAllPossibleSubgoals(); | ||||
| //	auto trade = whatToDoToTrade(); | ||||
| //	if (!trade->invalid()) | ||||
| //		goals.push_back(trade); | ||||
| // | ||||
| //	return sptr(Invalid()); //we can always do that | ||||
| //} | ||||
|  | ||||
| TSubgoal CollectRes::whatToDoToTrade() | ||||
| { | ||||
|   | ||||
| @@ -29,10 +29,9 @@ namespace Goals | ||||
| 		{ | ||||
| 			resID = rid; | ||||
| 			value = val; | ||||
| 			priority = 2; | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override; | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		/*TGoalVec getAllPossibleSubgoals() override; | ||||
| 		TSubgoal whatToDoToAchieve() override;*/ | ||||
| 		TSubgoal whatToDoToTrade(); | ||||
| 		virtual bool operator==(const CollectRes & other) const override; | ||||
| 	}; | ||||
|   | ||||
| @@ -1,270 +0,0 @@ | ||||
| /* | ||||
| * CompleteQuest.cpp, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
| #include "StdInc.h" | ||||
| #include "Goals.h" | ||||
| #include "../VCAI.h" | ||||
| #include "../FuzzyHelper.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../../../lib/mapping/CMap.h" //for victory conditions | ||||
| #include "../../../lib/CPathfinder.h" | ||||
|  | ||||
| extern boost::thread_specific_ptr<CCallback> cb; | ||||
| extern boost::thread_specific_ptr<VCAI> ai; | ||||
| extern FuzzyHelper * fh; | ||||
|  | ||||
| using namespace Goals; | ||||
|  | ||||
| bool CompleteQuest::operator==(const CompleteQuest & other) const | ||||
| { | ||||
| 	return q.quest->qid == other.q.quest->qid; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuest::getAllPossibleSubgoals() | ||||
| { | ||||
| 	TGoalVec solutions; | ||||
|  | ||||
| 	if(q.quest->missionType && q.quest->progress != CQuest::COMPLETE) | ||||
| 	{ | ||||
| 		logAi->debug("Trying to realize quest: %s", questToString()); | ||||
|  | ||||
| 		switch(q.quest->missionType) | ||||
| 		{ | ||||
| 		case CQuest::MISSION_ART: | ||||
| 			return missionArt(); | ||||
|  | ||||
| 		case CQuest::MISSION_HERO: | ||||
| 			return missionHero(); | ||||
|  | ||||
| 		case CQuest::MISSION_ARMY: | ||||
| 			return missionArmy(); | ||||
|  | ||||
| 		case CQuest::MISSION_RESOURCES: | ||||
| 			return missionResources(); | ||||
|  | ||||
| 		case CQuest::MISSION_KILL_HERO: | ||||
| 		case CQuest::MISSION_KILL_CREATURE: | ||||
| 			return missionDestroyObj(); | ||||
|  | ||||
| 		case CQuest::MISSION_PRIMARY_STAT: | ||||
| 			return missionIncreasePrimaryStat(); | ||||
|  | ||||
| 		case CQuest::MISSION_LEVEL: | ||||
| 			return missionLevel(); | ||||
|  | ||||
| 		case CQuest::MISSION_PLAYER: | ||||
| 			if(ai->playerID.getNum() != q.quest->m13489val) | ||||
| 				logAi->debug("Can't be player of color %d", q.quest->m13489val); | ||||
|  | ||||
| 			break; | ||||
| 		 | ||||
| 		case CQuest::MISSION_KEYMASTER: | ||||
| 			return missionKeymaster(); | ||||
|  | ||||
| 		} //end of switch | ||||
| 	} | ||||
|  | ||||
| 	return TGoalVec(); | ||||
| } | ||||
|  | ||||
| TSubgoal CompleteQuest::whatToDoToAchieve() | ||||
| { | ||||
| 	if(q.quest->missionType == CQuest::MISSION_NONE) | ||||
| 	{ | ||||
| 		throw cannotFulfillGoalException("Can not complete inactive quest"); | ||||
| 	} | ||||
|  | ||||
| 	TGoalVec solutions = getAllPossibleSubgoals(); | ||||
|  | ||||
| 	throw cannotFulfillGoalException("Can not complete quest " + questToString()); | ||||
| /* | ||||
| 	TSubgoal result = fh->chooseSolution(solutions); | ||||
|  | ||||
| 	logAi->trace( | ||||
| 		"Returning %s, tile: %s, objid: %d, hero: %s", | ||||
| 		result->name(), | ||||
| 		result->tile.toString(), | ||||
| 		result->objid, | ||||
| 		result->hero.validAndSet() ? result->hero->name : "not specified"); | ||||
|  | ||||
| 	return result;*/ | ||||
| } | ||||
|  | ||||
| std::string CompleteQuest::name() const | ||||
| { | ||||
| 	return "CompleteQuest"; | ||||
| } | ||||
|  | ||||
| std::string CompleteQuest::questToString() const | ||||
| { | ||||
| 	if(q.quest->missionType == CQuest::MISSION_NONE) | ||||
| 		return "inactive quest"; | ||||
|  | ||||
| 	MetaString ms; | ||||
| 	q.quest->getRolloverText(ms, false); | ||||
|  | ||||
| 	return ms.toString(); | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuest::tryCompleteQuest() const | ||||
| { | ||||
| 	TGoalVec solutions; | ||||
|  | ||||
| 	auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities? | ||||
|  | ||||
| 	for(auto hero : heroes) | ||||
| 	{ | ||||
| 		if(q.quest->checkQuest(hero)) | ||||
| 		{ | ||||
| 			//vstd::concatenate(solutions, ai->ah->howToVisitObj(hero, ObjectIdRef(q.obj->id))); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuest::missionArt() const | ||||
| { | ||||
| 	TGoalVec solutions = tryCompleteQuest(); | ||||
|  | ||||
| 	if(!solutions.empty()) | ||||
| 		return solutions; | ||||
|  | ||||
| 	for(auto art : q.quest->m5arts) | ||||
| 	{ | ||||
| 		solutions.push_back(sptr(GetArtOfType(art))); //TODO: transport? | ||||
| 	} | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuest::missionHero() const | ||||
| { | ||||
| 	TGoalVec solutions = tryCompleteQuest(); | ||||
|  | ||||
| 	if(solutions.empty()) | ||||
| 	{ | ||||
| 		//rule of a thumb - quest heroes usually are locked in prisons | ||||
| 		solutions.push_back(sptr(FindObj(Obj::PRISON))); | ||||
| 	} | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuest::missionArmy() const | ||||
| { | ||||
| 	TGoalVec solutions = tryCompleteQuest(); | ||||
|  | ||||
| 	if(!solutions.empty()) | ||||
| 		return solutions; | ||||
|  | ||||
| 	for(auto creature : q.quest->m6creatures) | ||||
| 	{ | ||||
| 		solutions.push_back(sptr(GatherTroops(creature.type->idNumber, creature.count))); | ||||
| 	} | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuest::missionIncreasePrimaryStat() const | ||||
| { | ||||
| 	TGoalVec solutions = tryCompleteQuest(); | ||||
|  | ||||
| 	if(solutions.empty()) | ||||
| 	{ | ||||
| 		for(int i = 0; i < q.quest->m2stats.size(); ++i) | ||||
| 		{ | ||||
| 			// TODO: library, school and other boost objects | ||||
| 			logAi->debug("Don't know how to increase primary stat %d", i); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuest::missionLevel() const | ||||
| { | ||||
| 	TGoalVec solutions = tryCompleteQuest(); | ||||
|  | ||||
| 	if(solutions.empty()) | ||||
| 	{ | ||||
| 		logAi->debug("Don't know how to reach hero level %d", q.quest->m13489val); | ||||
| 	} | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuest::missionKeymaster() const | ||||
| { | ||||
| 	TGoalVec solutions = tryCompleteQuest(); | ||||
|  | ||||
| 	if(solutions.empty()) | ||||
| 	{ | ||||
| 		solutions.push_back(sptr(Goals::FindObj(Obj::KEYMASTER, q.obj->subID))); | ||||
| 	} | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuest::missionResources() const | ||||
| { | ||||
| 	TGoalVec solutions; | ||||
|  | ||||
| 	auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities? | ||||
|  | ||||
| 	if(heroes.size()) | ||||
| 	{ | ||||
| 		if(q.quest->checkQuest(heroes.front())) //it doesn't matter which hero it is | ||||
| 		{ | ||||
| 			return solutions;// ai->ah->howToVisitObj(q.obj); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			for(int i = 0; i < q.quest->m7resources.size(); ++i) | ||||
| 			{ | ||||
| 				if(q.quest->m7resources[i]) | ||||
| 					solutions.push_back(sptr(CollectRes(i, q.quest->m7resources[i]))); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		solutions.push_back(sptr(Goals::RecruitHero())); //FIXME: checkQuest requires any hero belonging to player :( | ||||
| 	} | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
|  | ||||
| TGoalVec CompleteQuest::missionDestroyObj() const | ||||
| { | ||||
| 	TGoalVec solutions; | ||||
|  | ||||
| 	auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val); | ||||
|  | ||||
| 	if(!obj) | ||||
| 		return solutions;// ai->ah->howToVisitObj(q.obj); | ||||
|  | ||||
| 	if(obj->ID == Obj::HERO) | ||||
| 	{ | ||||
| 		auto relations = cb->getPlayerRelations(ai->playerID, obj->tempOwner); | ||||
|  | ||||
| 		if(relations == PlayerRelations::SAME_PLAYER) | ||||
| 		{ | ||||
| 			auto heroToProtect = cb->getHero(obj->id); | ||||
|  | ||||
| 			//solutions.push_back(sptr(GatherArmy().sethero(heroToProtect))); | ||||
| 		} | ||||
| 		else if(relations == PlayerRelations::ENEMIES) | ||||
| 		{ | ||||
| 			//solutions = ai->ah->howToVisitObj(obj); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return solutions; | ||||
| } | ||||
| @@ -1,45 +0,0 @@ | ||||
| /* | ||||
| * CompleteQuest.h, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| #include "CGoal.h" | ||||
| #include "../../../lib/VCMI_Lib.h" | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	class DLL_EXPORT CompleteQuest : public CGoal<CompleteQuest> | ||||
| 	{ | ||||
| 	private: | ||||
| 		const QuestInfo q; | ||||
|  | ||||
| 	public: | ||||
| 		CompleteQuest(const QuestInfo quest) | ||||
| 			: CGoal(Goals::COMPLETE_QUEST), q(quest) | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 		TGoalVec getAllPossibleSubgoals() override; | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		std::string name() const override; | ||||
| 		virtual bool operator==(const CompleteQuest & other) const override; | ||||
|  | ||||
| 	private: | ||||
| 		TGoalVec tryCompleteQuest() const; | ||||
| 		TGoalVec missionArt() const; | ||||
| 		TGoalVec missionHero() const; | ||||
| 		TGoalVec missionArmy() const; | ||||
| 		TGoalVec missionResources() const; | ||||
| 		TGoalVec missionDestroyObj() const; | ||||
| 		TGoalVec missionIncreasePrimaryStat() const; | ||||
| 		TGoalVec missionLevel() const; | ||||
| 		TGoalVec missionKeymaster() const; | ||||
| 		std::string questToString() const; | ||||
| 	}; | ||||
| } | ||||
| @@ -24,16 +24,16 @@ bool DigAtTile::operator==(const DigAtTile & other) const | ||||
| { | ||||
| 	return other.hero.h == hero.h && other.tile == tile; | ||||
| } | ||||
|  | ||||
| TSubgoal DigAtTile::whatToDoToAchieve() | ||||
| { | ||||
| 	const CGObjectInstance * firstObj = vstd::frontOrNull(cb->getVisitableObjs(tile)); | ||||
| 	if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest | ||||
| 	{ | ||||
| 		const CGHeroInstance * h = dynamic_cast<const CGHeroInstance *>(firstObj); | ||||
| 		sethero(h).setisElementar(true); | ||||
| 		return sptr(*this); | ||||
| 	} | ||||
|  | ||||
| 	return sptr(VisitTile(tile)); | ||||
| } | ||||
| // | ||||
| //TSubgoal DigAtTile::decomposeSingle() const | ||||
| //{ | ||||
| //	const CGObjectInstance * firstObj = vstd::frontOrNull(cb->getVisitableObjs(tile)); | ||||
| //	if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest | ||||
| //	{ | ||||
| //		const CGHeroInstance * h = dynamic_cast<const CGHeroInstance *>(firstObj); | ||||
| //		sethero(h).setisElementar(true); | ||||
| //		return sptr(*this); | ||||
| //	} | ||||
| // | ||||
| //	return sptr(VisitTile(tile)); | ||||
| //} | ||||
|   | ||||
| @@ -29,13 +29,10 @@ namespace Goals | ||||
| 			: CGoal(Goals::DIG_AT_TILE) | ||||
| 		{ | ||||
| 			tile = Tile; | ||||
| 			priority = 20; | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		virtual bool operator==(const DigAtTile & other) const override; | ||||
|  | ||||
| 	private: | ||||
| 		//TSubgoal decomposeSingle() const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -26,14 +26,6 @@ bool DismissHero::operator==(const DismissHero & other) const | ||||
| 	return hero.h == other.hero.h; | ||||
| } | ||||
|  | ||||
| TSubgoal DismissHero::whatToDoToAchieve() | ||||
| { | ||||
| 	if(!hero.validAndSet()) | ||||
| 		throw cannotFulfillGoalException("Invalid hero!"); | ||||
|  | ||||
| 	return iAmElementar(); | ||||
| } | ||||
|  | ||||
| void DismissHero::accept(VCAI * ai) | ||||
| { | ||||
| 	if(!hero.validAndSet()) | ||||
| @@ -44,7 +36,7 @@ void DismissHero::accept(VCAI * ai) | ||||
| 	throw goalFulfilledException(sptr(*this)); | ||||
| } | ||||
|  | ||||
| std::string DismissHero::name() const | ||||
| std::string DismissHero::toString() const | ||||
| { | ||||
| 	return "DismissHero " + hero.name; | ||||
| } | ||||
|   | ||||
| @@ -13,23 +13,17 @@ | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	class DLL_EXPORT DismissHero : public CGoal<DismissHero> | ||||
| 	class DLL_EXPORT DismissHero : public ElementarGoal<DismissHero> | ||||
| 	{ | ||||
| 	public: | ||||
| 		DismissHero(HeroPtr hero) | ||||
| 			: CGoal(Goals::DISMISS_HERO) | ||||
| 			: ElementarGoal(Goals::DISMISS_HERO) | ||||
| 		{ | ||||
| 			sethero(hero); | ||||
| 		} | ||||
|  | ||||
| 		TGoalVec getAllPossibleSubgoals() override | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
|  | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		void accept(VCAI * ai) override; | ||||
| 		std::string name() const override; | ||||
| 		std::string toString() const override; | ||||
| 		virtual bool operator==(const DismissHero & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -27,11 +27,11 @@ ExchangeSwapTownHeroes::ExchangeSwapTownHeroes( | ||||
| 	const CGTownInstance * town,  | ||||
| 	const CGHeroInstance * garrisonHero, | ||||
| 	HeroLockedReason lockingReason) | ||||
| 	:CGoal(Goals::EXCHANGE_SWAP_TOWN_HEROES), town(town), garrisonHero(garrisonHero), lockingReason(lockingReason) | ||||
| 	:ElementarGoal(Goals::EXCHANGE_SWAP_TOWN_HEROES), town(town), garrisonHero(garrisonHero), lockingReason(lockingReason) | ||||
| { | ||||
| } | ||||
|  | ||||
| std::string ExchangeSwapTownHeroes::name() const | ||||
| std::string ExchangeSwapTownHeroes::toString() const | ||||
| { | ||||
| 	return "Exchange and swap heroes of " + town->name; | ||||
| } | ||||
| @@ -41,11 +41,6 @@ bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) co | ||||
| 	return town == other.town; | ||||
| } | ||||
|  | ||||
| TSubgoal ExchangeSwapTownHeroes::whatToDoToAchieve() | ||||
| { | ||||
| 	return iAmElementar(); | ||||
| } | ||||
|  | ||||
| void ExchangeSwapTownHeroes::accept(VCAI * ai) | ||||
| { | ||||
| 	if(!garrisonHero) | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	class DLL_EXPORT ExchangeSwapTownHeroes : public CGoal<ExchangeSwapTownHeroes> | ||||
| 	class DLL_EXPORT ExchangeSwapTownHeroes : public ElementarGoal<ExchangeSwapTownHeroes> | ||||
| 	{ | ||||
| 	private: | ||||
| 		const CGTownInstance * town; | ||||
| @@ -27,14 +27,8 @@ namespace Goals | ||||
| 			const CGHeroInstance * garrisonHero = nullptr, | ||||
| 			HeroLockedReason lockingReason = HeroLockedReason::NOT_LOCKED); | ||||
|  | ||||
| 		TGoalVec getAllPossibleSubgoals() override | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
|  | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		void accept(VCAI * ai) override; | ||||
| 		std::string name() const override; | ||||
| 		std::string toString() const override; | ||||
| 		virtual bool operator==(const ExchangeSwapTownHeroes & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -24,23 +24,12 @@ extern FuzzyHelper * fh; | ||||
| using namespace Goals; | ||||
|  | ||||
| ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj) | ||||
| 	:CGoal(Goals::EXECUTE_HERO_CHAIN), chainPath(path) | ||||
| 	:ElementarGoal(Goals::EXECUTE_HERO_CHAIN), chainPath(path) | ||||
| { | ||||
| 	evaluationContext.danger = path.getTotalDanger(); | ||||
| 	evaluationContext.movementCost = path.movementCost(); | ||||
| 	evaluationContext.armyLoss = path.getTotalArmyLoss(); | ||||
| 	evaluationContext.heroStrength = path.getHeroStrength(); | ||||
|  | ||||
| 	hero = path.targetHero; | ||||
| 	tile = path.targetTile(); | ||||
|  | ||||
| 	for(auto & node : path.nodes) | ||||
| 	{ | ||||
| 		auto role = ai->ah->getHeroRole(node.targetHero); | ||||
|  | ||||
| 		evaluationContext.movementCostByRole[role] += node.cost; | ||||
| 	} | ||||
|  | ||||
| 	if(obj) | ||||
| 	{ | ||||
| 		objid = obj->id.getNum(); | ||||
| @@ -57,15 +46,12 @@ bool ExecuteHeroChain::operator==(const ExecuteHeroChain & other) const | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| TSubgoal ExecuteHeroChain::whatToDoToAchieve() | ||||
| { | ||||
| 	return iAmElementar(); | ||||
| } | ||||
|  | ||||
| void ExecuteHeroChain::accept(VCAI * ai) | ||||
| { | ||||
| 	logAi->debug("Executing hero chain towards %s. Path %s", targetName, chainPath.toString()); | ||||
|  | ||||
| 	ai->nullkiller->setActive(chainPath.targetHero, tile); | ||||
|  | ||||
| 	std::set<int> blockedIndexes; | ||||
|  | ||||
| 	for(int i = chainPath.nodes.size() - 1; i >= 0; i--) | ||||
| @@ -89,7 +75,6 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 		{ | ||||
| 			if(hero->movement) | ||||
| 			{ | ||||
|  | ||||
| 				ai->nullkiller->setActive(hero, node.coord); | ||||
|  | ||||
| 				if(node.specialAction) | ||||
| @@ -98,6 +83,8 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 					{ | ||||
| 						auto specialGoal = node.specialAction->whatToDo(hero); | ||||
|  | ||||
| 						if(!specialGoal->isElementar) | ||||
|  | ||||
| 						specialGoal->accept(ai); | ||||
| 					} | ||||
| 					else | ||||
| @@ -135,7 +122,10 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 				{ | ||||
| 					try | ||||
| 					{ | ||||
| 						Goals::VisitTile(node.coord).sethero(hero).accept(ai); | ||||
| 						if(moveHeroToTile(hero, node.coord)) | ||||
| 						{ | ||||
| 							continue; | ||||
| 						} | ||||
| 					} | ||||
| 					catch(cannotFulfillGoalException) | ||||
| 					{ | ||||
| @@ -195,7 +185,19 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| std::string ExecuteHeroChain::name() const | ||||
| std::string ExecuteHeroChain::toString() const | ||||
| { | ||||
| 	return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->name; | ||||
| } | ||||
|  | ||||
| bool ExecuteHeroChain::moveHeroToTile(const CGHeroInstance * hero, const int3 & tile) | ||||
| { | ||||
| 	if(g.tile == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2) | ||||
| 	{ | ||||
| 		logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile.toString()); | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	return ai->moveHeroToTile(tile, hero); | ||||
| } | ||||
| @@ -13,7 +13,7 @@ | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	class DLL_EXPORT ExecuteHeroChain : public CGoal<ExecuteHeroChain> | ||||
| 	class DLL_EXPORT ExecuteHeroChain : public ElementarGoal<ExecuteHeroChain> | ||||
| 	{ | ||||
| 	private: | ||||
| 		AIPath chainPath; | ||||
| @@ -22,15 +22,13 @@ namespace Goals | ||||
| 	public: | ||||
| 		ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj = nullptr); | ||||
|  | ||||
| 		TGoalVec getAllPossibleSubgoals() override | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
|  | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		 | ||||
| 		void accept(VCAI * ai) override; | ||||
| 		std::string name() const override; | ||||
| 		std::string toString() const override; | ||||
| 		virtual bool operator==(const ExecuteHeroChain & other) const override; | ||||
| 		const AIPath & getPath() const { return chainPath; } | ||||
|  | ||||
| 	private: | ||||
| 		bool moveHeroToTile(const CGHeroInstance * hero, const int3 & tile); | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -23,32 +23,32 @@ bool FindObj::operator==(const FindObj & other) const | ||||
| { | ||||
| 	return other.hero.h == hero.h && other.objid == objid; | ||||
| } | ||||
|  | ||||
| TSubgoal FindObj::whatToDoToAchieve() | ||||
| { | ||||
| 	const CGObjectInstance * o = nullptr; | ||||
| 	if(resID > -1) //specified | ||||
| 	{ | ||||
| 		for(const CGObjectInstance * obj : ai->visitableObjs) | ||||
| 		{ | ||||
| 			if(obj->ID == objid && obj->subID == resID) | ||||
| 			{ | ||||
| 				o = obj; | ||||
| 				break; //TODO: consider multiple objects and choose best | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		for(const CGObjectInstance * obj : ai->visitableObjs) | ||||
| 		{ | ||||
| 			if(obj->ID == objid) | ||||
| 			{ | ||||
| 				o = obj; | ||||
| 				break; //TODO: consider multiple objects and choose best | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is | ||||
| 		return sptr(VisitObj(o->id.getNum())); | ||||
| } | ||||
| // | ||||
| //TSubgoal FindObj::whatToDoToAchieve() | ||||
| //{ | ||||
| //	const CGObjectInstance * o = nullptr; | ||||
| //	if(resID > -1) //specified | ||||
| //	{ | ||||
| //		for(const CGObjectInstance * obj : ai->visitableObjs) | ||||
| //		{ | ||||
| //			if(obj->ID == objid && obj->subID == resID) | ||||
| //			{ | ||||
| //				o = obj; | ||||
| //				break; //TODO: consider multiple objects and choose best | ||||
| //			} | ||||
| //		} | ||||
| //	} | ||||
| //	else | ||||
| //	{ | ||||
| //		for(const CGObjectInstance * obj : ai->visitableObjs) | ||||
| //		{ | ||||
| //			if(obj->ID == objid) | ||||
| //			{ | ||||
| //				o = obj; | ||||
| //				break; //TODO: consider multiple objects and choose best | ||||
| //			} | ||||
| //		} | ||||
| //	} | ||||
| //	if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is | ||||
| //		return sptr(VisitObj(o->id.getNum())); | ||||
| //} | ||||
| @@ -27,20 +27,16 @@ namespace Goals | ||||
| 		{ | ||||
| 			objid = ID; | ||||
| 			resID = -1; //subid unspecified | ||||
| 			priority = 1; | ||||
| 		} | ||||
| 		FindObj(int ID, int subID) | ||||
| 			: CGoal(Goals::FIND_OBJ) | ||||
| 		{ | ||||
| 			objid = ID; | ||||
| 			resID = subID; | ||||
| 			priority = 1; | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		virtual bool operator==(const FindObj & other) const override; | ||||
|  | ||||
| 	private: | ||||
| 		//TSubgoal decomposeSingle() const override; | ||||
| 	}; | ||||
| } | ||||
| @@ -43,30 +43,30 @@ int GatherTroops::getCreaturesCount(const CArmedInstance * army) | ||||
|  | ||||
| 	return count; | ||||
| } | ||||
| // | ||||
| //TSubgoal GatherTroops::whatToDoToAchieve() | ||||
| //{ | ||||
| //	logAi->trace("Entering GatherTroops::whatToDoToAchieve"); | ||||
| // | ||||
| //	auto heroes = cb->getHeroesInfo(true); | ||||
| // | ||||
| //	for(auto hero : heroes) | ||||
| //	{ | ||||
| //		if(getCreaturesCount(hero) >= this->value) | ||||
| //		{ | ||||
| //			logAi->trace("Completing GATHER_TROOPS by hero %s", hero->name); | ||||
| // | ||||
| //			throw goalFulfilledException(sptr(*this)); | ||||
| //		} | ||||
| //	} | ||||
| // | ||||
| //	return sptr(Invalid()); | ||||
| //} | ||||
|  | ||||
| TSubgoal GatherTroops::whatToDoToAchieve() | ||||
| { | ||||
| 	logAi->trace("Entering GatherTroops::whatToDoToAchieve"); | ||||
|  | ||||
| 	auto heroes = cb->getHeroesInfo(true); | ||||
|  | ||||
| 	for(auto hero : heroes) | ||||
| 	{ | ||||
| 		if(getCreaturesCount(hero) >= this->value) | ||||
| 		{ | ||||
| 			logAi->trace("Completing GATHER_TROOPS by hero %s", hero->name); | ||||
|  | ||||
| 			throw goalFulfilledException(sptr(*this)); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return sptr(Invalid()); | ||||
| } | ||||
|  | ||||
|  | ||||
| TGoalVec GatherTroops::getAllPossibleSubgoals() | ||||
| { | ||||
| 	TGoalVec solutions; | ||||
| // | ||||
| //TGoalVec GatherTroops::getAllPossibleSubgoals() | ||||
| //{ | ||||
| //	TGoalVec solutions; | ||||
|  | ||||
| 	//for(const CGTownInstance * t : cb->getTownsInfo()) | ||||
| 	//{ | ||||
| @@ -136,6 +136,6 @@ TGoalVec GatherTroops::getAllPossibleSubgoals() | ||||
| 	//	return goal->hero && !goal->hero->getSlotFor(creID).validSlot() && !goal->hero->getFreeSlot().validSlot(); | ||||
| 	//}); | ||||
|  | ||||
| 	return solutions; | ||||
| 	//TODO: exchange troops between heroes | ||||
| } | ||||
| //	return solutions; | ||||
| //	//TODO: exchange troops between heroes | ||||
| //} | ||||
|   | ||||
| @@ -23,17 +23,13 @@ namespace Goals | ||||
| 		GatherTroops() | ||||
| 			: CGoal(Goals::GATHER_TROOPS) | ||||
| 		{ | ||||
| 			priority = 2; | ||||
| 		} | ||||
| 		GatherTroops(int type, int val) | ||||
| 			: CGoal(Goals::GATHER_TROOPS) | ||||
| 		{ | ||||
| 			objid = type; | ||||
| 			value = val; | ||||
| 			priority = 2; | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override; | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		virtual bool operator==(const GatherTroops & other) const override; | ||||
|  | ||||
| 	private: | ||||
|   | ||||
| @@ -24,8 +24,8 @@ bool GetArtOfType::operator==(const GetArtOfType & other) const | ||||
| { | ||||
| 	return other.hero.h == hero.h && other.objid == objid; | ||||
| } | ||||
|  | ||||
| TSubgoal GetArtOfType::whatToDoToAchieve() | ||||
| { | ||||
| 	return sptr(FindObj(Obj::ARTIFACT, aid)); | ||||
| } | ||||
| // | ||||
| //TSubgoal GetArtOfType::whatToDoToAchieve() | ||||
| //{ | ||||
| //	return sptr(FindObj(Obj::ARTIFACT, aid)); | ||||
| //} | ||||
| @@ -28,13 +28,7 @@ namespace Goals | ||||
| 			: CGoal(Goals::GET_ART_TYPE) | ||||
| 		{ | ||||
| 			aid = type; | ||||
| 			priority = 2; | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		virtual bool operator==(const GetArtOfType & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -13,10 +13,6 @@ | ||||
| #include "Invalid.h" | ||||
| #include "BuildBoat.h" | ||||
| #include "BuildThis.h" | ||||
| #include "Win.h" | ||||
| #include "VisitObj.h" | ||||
| #include "VisitTile.h" | ||||
| #include "VisitHero.h" | ||||
| #include "BuyArmy.h" | ||||
| #include "GatherTroops.h" | ||||
| #include "Trade.h" | ||||
| @@ -25,5 +21,4 @@ | ||||
| #include "GetArtOfType.h" | ||||
| #include "DigAtTile.h" | ||||
| #include "FindObj.h" | ||||
| #include "CompleteQuest.h" | ||||
| #include "AdventureSpellCast.h" | ||||
| @@ -16,26 +16,4 @@ class VCAI; | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	class DLL_EXPORT Invalid : public CGoal<Invalid> | ||||
| 	{ | ||||
| 	public: | ||||
| 		Invalid() | ||||
| 			: CGoal(Goals::INVALID) | ||||
| 		{ | ||||
| 			priority = -1e10; | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
| 		TSubgoal whatToDoToAchieve() override | ||||
| 		{ | ||||
| 			return iAmElementar(); | ||||
| 		} | ||||
|  | ||||
| 		virtual bool operator==(const Invalid & other) const override | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -23,3 +23,8 @@ extern boost::thread_specific_ptr<VCAI> ai; | ||||
| extern FuzzyHelper * fh; | ||||
|  | ||||
| using namespace Goals; | ||||
|  | ||||
| std::string RecruitHero::toString() const | ||||
| { | ||||
| 	return "Recruit hero at " + town->name; | ||||
| } | ||||
| @@ -17,18 +17,27 @@ class FuzzyHelper; | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	class DLL_EXPORT RecruitHero : public CGoal<RecruitHero> | ||||
| 	class DLL_EXPORT RecruitHero : public ElementarGoal<RecruitHero> | ||||
| 	{ | ||||
| 	public: | ||||
| 		RecruitHero() | ||||
| 			: CGoal(Goals::RECRUIT_HERO) | ||||
| 		RecruitHero(const CGTownInstance * townWithTavern, const CGHeroInstance * heroToBuy) | ||||
| 			: RecruitHero(townWithTavern) | ||||
| 		{ | ||||
| 			objid = heroToBuy->id.getNum(); | ||||
| 		} | ||||
|  | ||||
| 		RecruitHero(const CGTownInstance * townWithTavern) | ||||
| 			: ElementarGoal(Goals::RECRUIT_HERO) | ||||
| 		{ | ||||
| 			priority = 1; | ||||
| 			town = townWithTavern; | ||||
| 		} | ||||
|  | ||||
| 		virtual bool operator==(const RecruitHero & other) const override | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 		virtual std::string toString() const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -15,9 +15,4 @@ using namespace Goals; | ||||
| bool Trade::operator==(const Trade & other) const | ||||
| { | ||||
| 	return resID == other.resID; | ||||
| } | ||||
|  | ||||
| TSubgoal Trade::whatToDoToAchieve() | ||||
| { | ||||
| 	return iAmElementar(); | ||||
| } | ||||
| } | ||||
| @@ -30,9 +30,7 @@ namespace Goals | ||||
| 			resID = rid; | ||||
| 			value = val; | ||||
| 			objid = Objid; | ||||
| 			priority = 3; //trading is instant, but picking resources is free | ||||
| 		} | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		virtual bool operator==(const Trade & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -9,6 +9,8 @@ | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| #error not used | ||||
|  | ||||
| #include "CGoal.h" | ||||
|  | ||||
| struct HeroPtr; | ||||
| @@ -28,7 +30,6 @@ namespace Goals | ||||
| 			: CGoal(Goals::VISIT_HERO) | ||||
| 		{ | ||||
| 			objid = hid; | ||||
| 			priority = 4; | ||||
| 		} | ||||
| 		virtual bool operator==(const VisitHero & other) const override; | ||||
| 	}; | ||||
|   | ||||
| @@ -38,6 +38,4 @@ VisitObj::VisitObj(int Objid) | ||||
| 		tile = obj->visitablePos(); | ||||
| 	else | ||||
| 		logAi->error("VisitObj constructed with invalid object instance %d", Objid); | ||||
|  | ||||
| 	priority = 3; | ||||
| } | ||||
| @@ -9,6 +9,8 @@ | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| #error not used | ||||
|  | ||||
| #include "CGoal.h" | ||||
|  | ||||
| struct HeroPtr; | ||||
|   | ||||
| @@ -9,6 +9,8 @@ | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| #error not used | ||||
|  | ||||
| #include "CGoal.h" | ||||
|  | ||||
| struct HeroPtr; | ||||
| @@ -27,7 +29,6 @@ namespace Goals | ||||
| 			: CGoal(Goals::VISIT_TILE) | ||||
| 		{ | ||||
| 			tile = Tile; | ||||
| 			priority = 5; | ||||
| 		} | ||||
| 		virtual bool operator==(const VisitTile & other) const override; | ||||
| 	}; | ||||
|   | ||||
| @@ -299,15 +299,6 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q | ||||
|  | ||||
| 	requestActionASAP([=]() | ||||
| 	{ | ||||
| 		float goalpriority1 = 0, goalpriority2 = 0; | ||||
|  | ||||
| 		auto firstGoal = getGoal(firstHero); | ||||
| 		if(firstGoal->goalType == Goals::GATHER_ARMY) | ||||
| 			goalpriority1 = firstGoal->priority; | ||||
| 		auto secondGoal = getGoal(secondHero); | ||||
| 		if(secondGoal->goalType == Goals::GATHER_ARMY) | ||||
| 			goalpriority2 = secondGoal->priority; | ||||
|  | ||||
| 		auto transferFrom2to1 = [this](const CGHeroInstance * h1, const CGHeroInstance * h2) -> void | ||||
| 		{ | ||||
| 			this->pickBestCreatures(h1, h2); | ||||
| @@ -320,28 +311,13 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q | ||||
| 		{ | ||||
| 			logAi->debug("Heroes owned by different players. Do not exchange army or artifacts."); | ||||
| 		} | ||||
| 		else if(nullkiller) | ||||
| 		else | ||||
| 		{ | ||||
| 			if(nullkiller->isActive(firstHero)) | ||||
| 				transferFrom2to1(secondHero, firstHero); | ||||
| 			else | ||||
| 				transferFrom2to1(firstHero, secondHero); | ||||
| 		} | ||||
| 		else if(goalpriority1 > goalpriority2) | ||||
| 		{ | ||||
| 			transferFrom2to1(firstHero, secondHero); | ||||
| 		} | ||||
| 		else if(goalpriority1 < goalpriority2) | ||||
| 		{ | ||||
| 			transferFrom2to1(secondHero, firstHero); | ||||
| 		} | ||||
| 		else //regular criteria | ||||
| 		{ | ||||
| 			if(firstHero->getFightingStrength() > secondHero->getFightingStrength() && ah->canGetArmy(firstHero, secondHero)) | ||||
| 				transferFrom2to1(firstHero, secondHero); | ||||
| 			else if(ah->canGetArmy(secondHero, firstHero)) | ||||
| 				transferFrom2to1(secondHero, firstHero); | ||||
| 		} | ||||
|  | ||||
| 		answerQuery(query, 0); | ||||
| 	}); | ||||
| @@ -1403,7 +1379,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) | ||||
| 		if(path.nodes.empty()) | ||||
| 		{ | ||||
| 			logAi->error("Hero %s cannot reach %s.", h->name, dst.toString()); | ||||
| 			throw goalFulfilledException(sptr(Goals::VisitTile(dst).sethero(h))); | ||||
| 			return true; | ||||
| 		} | ||||
| 		int i = path.nodes.size() - 1; | ||||
|  | ||||
| @@ -1568,11 +1544,6 @@ void VCAI::buildStructure(const CGTownInstance * t, BuildingID building) | ||||
| 	cb->buildBuilding(t, building); //just do this; | ||||
| } | ||||
|  | ||||
| void VCAI::tryRealize(Goals::Explore & g) | ||||
| { | ||||
| 	throw cannotFulfillGoalException("EXPLORE is not an elementar goal!"); | ||||
| } | ||||
|  | ||||
| void VCAI::tryRealize(Goals::RecruitHero & g) | ||||
| { | ||||
| 	const CGTownInstance * t = g.town; | ||||
| @@ -1591,56 +1562,6 @@ void VCAI::tryRealize(Goals::RecruitHero & g) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void VCAI::tryRealize(Goals::VisitTile & g) | ||||
| { | ||||
| 	if(!g.hero->movement) | ||||
| 		throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!"); | ||||
| 	if(g.tile == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2) | ||||
| 	{ | ||||
| 		logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile.toString()); | ||||
| 		throw goalFulfilledException(sptr(g)); | ||||
| 	} | ||||
| 	if(ai->moveHeroToTile(g.tile, g.hero.get())) | ||||
| 	{ | ||||
| 		throw goalFulfilledException(sptr(g)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void VCAI::tryRealize(Goals::VisitObj & g) | ||||
| { | ||||
| 	auto position = g.tile; | ||||
| 	if(!g.hero->movement) | ||||
| 		throw cannotFulfillGoalException("Cannot visit object: hero is out of MPs!"); | ||||
| 	if(position == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2) | ||||
| 	{ | ||||
| 		logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile.toString()); | ||||
| 		throw goalFulfilledException(sptr(g)); | ||||
| 	} | ||||
| 	if(ai->moveHeroToTile(position, g.hero.get())) | ||||
| 	{ | ||||
| 		throw goalFulfilledException(sptr(g)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void VCAI::tryRealize(Goals::VisitHero & g) | ||||
| { | ||||
| 	if(!g.hero->movement) | ||||
| 		throw cannotFulfillGoalException("Cannot visit target hero: hero is out of MPs!"); | ||||
|  | ||||
| 	const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(g.objid)); | ||||
| 	if(obj) | ||||
| 	{ | ||||
| 		if(ai->moveHeroToTile(obj->visitablePos(), g.hero.get())) | ||||
| 		{ | ||||
| 			throw goalFulfilledException(sptr(g)); | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		throw cannotFulfillGoalException("Cannot visit hero: object not found!"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void VCAI::tryRealize(Goals::BuildThis & g) | ||||
| { | ||||
| 	auto b = BuildingID(g.bid); | ||||
| @@ -1773,7 +1694,7 @@ void VCAI::tryRealize(Goals::Invalid & g) | ||||
|  | ||||
| void VCAI::tryRealize(Goals::AbstractGoal & g) | ||||
| { | ||||
| 	logAi->debug("Attempting realizing goal with code %s", g.name()); | ||||
| 	logAi->debug("Attempting realizing goal with code %s", g.toString()); | ||||
| 	throw cannotFulfillGoalException("Unknown type of goal !"); | ||||
| } | ||||
|  | ||||
| @@ -1786,42 +1707,6 @@ const CGTownInstance * VCAI::findTownWithTavern() const | ||||
| 	return nullptr; | ||||
| } | ||||
|  | ||||
| Goals::TSubgoal VCAI::getGoal(HeroPtr h) const | ||||
| { | ||||
| 	auto it = lockedHeroes.find(h); | ||||
| 	if(it != lockedHeroes.end()) | ||||
| 		return it->second; | ||||
| 	else | ||||
| 		return sptr(Goals::Invalid()); | ||||
| } | ||||
|  | ||||
|  | ||||
| std::vector<HeroPtr> VCAI::getUnblockedHeroes() const | ||||
| { | ||||
| 	std::vector<HeroPtr> ret; | ||||
| 	for(auto h : cb->getHeroesInfo()) | ||||
| 	{ | ||||
| 		//&& !vstd::contains(lockedHeroes, h) | ||||
| 		//at this point we assume heroes exhausted their locked goals | ||||
| 		if(canAct(h)) | ||||
| 			ret.push_back(h); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| bool VCAI::canAct(HeroPtr h) const | ||||
| { | ||||
| 	auto mission = lockedHeroes.find(h); | ||||
| 	if(mission != lockedHeroes.end()) | ||||
| 	{ | ||||
| 		//FIXME: I'm afraid there can be other conditions when heroes can act but not move :? | ||||
| 		if(mission->second->goalType == Goals::DIG_AT_TILE && !mission->second->isElementar) | ||||
| 			return false; | ||||
| 	} | ||||
|  | ||||
| 	return h->movement; | ||||
| } | ||||
|  | ||||
| HeroPtr VCAI::primaryHero() const | ||||
| { | ||||
| 	auto hs = cb->getHeroesInfo(); | ||||
| @@ -1876,7 +1761,7 @@ void VCAI::recruitHero(const CGTownInstance * t, bool throwing) | ||||
| 		if(t->visitingHero) | ||||
| 			moveHeroToTile(t->visitablePos(), t->visitingHero.get()); | ||||
|  | ||||
| 		throw goalFulfilledException(sptr(Goals::RecruitHero().settown(t))); | ||||
| 		throw goalFulfilledException(sptr(Goals::RecruitHero(t))); | ||||
| 	} | ||||
| 	else if(throwing) | ||||
| 	{ | ||||
|   | ||||
| @@ -117,11 +117,7 @@ public: | ||||
| 	virtual ~VCAI(); | ||||
|  | ||||
| 	//TODO: use only smart pointers? | ||||
| 	void tryRealize(Goals::Explore & g); | ||||
| 	void tryRealize(Goals::RecruitHero & g); | ||||
| 	void tryRealize(Goals::VisitTile & g); | ||||
| 	void tryRealize(Goals::VisitObj & g); | ||||
| 	void tryRealize(Goals::VisitHero & g); | ||||
| 	void tryRealize(Goals::BuildThis & g); | ||||
| 	void tryRealize(Goals::DigAtTile & g); | ||||
| 	void tryRealize(Goals::Trade & g); | ||||
| @@ -241,9 +237,6 @@ public: | ||||
| 	const CGTownInstance * findTownWithTavern() const; | ||||
| 	bool canRecruitAnyHero(const CGTownInstance * t = NULL) const; | ||||
|  | ||||
| 	Goals::TSubgoal getGoal(HeroPtr h) const; | ||||
| 	bool canAct(HeroPtr h) const; | ||||
| 	std::vector<HeroPtr> getUnblockedHeroes() const; | ||||
| 	std::vector<HeroPtr> getMyHeroes() const; | ||||
| 	HeroPtr primaryHero() const; | ||||
|  | ||||
| @@ -374,7 +367,7 @@ public: | ||||
| 	explicit goalFulfilledException(Goals::TSubgoal Goal) | ||||
| 		: goal(Goal) | ||||
| 	{ | ||||
| 		msg = goal->name(); | ||||
| 		msg = goal->toString(); | ||||
| 	} | ||||
|  | ||||
| 	virtual ~goalFulfilledException() throw () | ||||
|   | ||||
		Reference in New Issue
	
	Block a user