Browse Source

Nullkiller: Try to join behavior and goal and see what come out of it.

Andrii Danylchenko 4 years ago
parent
commit
223a52b3d1
64 changed files with 846 additions and 1194 deletions
  1. 2 2
      AI/Nullkiller/Behaviors/Behavior.h
  2. 1 1
      AI/Nullkiller/Behaviors/BuildingBehavior.cpp
  3. 15 8
      AI/Nullkiller/Behaviors/BuildingBehavior.h
  4. 1 1
      AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp
  5. 15 9
      AI/Nullkiller/Behaviors/BuyArmyBehavior.h
  6. 7 7
      AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp
  7. 48 37
      AI/Nullkiller/Behaviors/CaptureObjectsBehavior.h
  8. 237 0
      AI/Nullkiller/Behaviors/CompleteQuestBehavior.cpp
  9. 48 0
      AI/Nullkiller/Behaviors/CompleteQuestBehavior.h
  10. 3 4
      AI/Nullkiller/Behaviors/DefenceBehavior.cpp
  11. 18 10
      AI/Nullkiller/Behaviors/DefenceBehavior.h
  12. 15 17
      AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp
  13. 20 18
      AI/Nullkiller/Behaviors/GatherArmyBehavior.h
  14. 2 2
      AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp
  15. 16 8
      AI/Nullkiller/Behaviors/RecruitHeroBehavior.h
  16. 2 2
      AI/Nullkiller/Behaviors/StartupBehavior.cpp
  17. 16 8
      AI/Nullkiller/Behaviors/StartupBehavior.h
  18. 2 12
      AI/Nullkiller/CMakeLists.txt
  19. 63 29
      AI/Nullkiller/Engine/Nullkiller.cpp
  20. 2 3
      AI/Nullkiller/Engine/Nullkiller.h
  21. 1 4
      AI/Nullkiller/Engine/PriorityEvaluator.cpp
  22. 0 216
      AI/Nullkiller/FuzzyEngines.cpp
  23. 0 34
      AI/Nullkiller/FuzzyEngines.h
  24. 13 6
      AI/Nullkiller/Goals/AbstractGoal.cpp
  25. 32 32
      AI/Nullkiller/Goals/AbstractGoal.h
  26. 2 7
      AI/Nullkiller/Goals/AdventureSpellCast.cpp
  27. 3 9
      AI/Nullkiller/Goals/AdventureSpellCast.h
  28. 19 18
      AI/Nullkiller/Goals/BuildBoat.cpp
  29. 5 8
      AI/Nullkiller/Goals/BuildBoat.h
  30. 1 1
      AI/Nullkiller/Goals/BuildThis.cpp
  31. 6 6
      AI/Nullkiller/Goals/BuildThis.h
  32. 2 2
      AI/Nullkiller/Goals/BuyArmy.cpp
  33. 5 4
      AI/Nullkiller/Goals/BuyArmy.h
  34. 61 8
      AI/Nullkiller/Goals/CGoal.h
  35. 16 16
      AI/Nullkiller/Goals/CollectRes.cpp
  36. 2 3
      AI/Nullkiller/Goals/CollectRes.h
  37. 0 270
      AI/Nullkiller/Goals/CompleteQuest.cpp
  38. 0 45
      AI/Nullkiller/Goals/CompleteQuest.h
  39. 13 13
      AI/Nullkiller/Goals/DigAtTile.cpp
  40. 3 6
      AI/Nullkiller/Goals/DigAtTile.h
  41. 1 9
      AI/Nullkiller/Goals/DismissHero.cpp
  42. 3 9
      AI/Nullkiller/Goals/DismissHero.h
  43. 2 7
      AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp
  44. 2 8
      AI/Nullkiller/Goals/ExchangeSwapTownHeroes.h
  45. 22 20
      AI/Nullkiller/Goals/ExecuteHeroChain.cpp
  46. 6 8
      AI/Nullkiller/Goals/ExecuteHeroChain.h
  47. 29 29
      AI/Nullkiller/Goals/FindObj.cpp
  48. 3 7
      AI/Nullkiller/Goals/FindObj.h
  49. 27 27
      AI/Nullkiller/Goals/GatherTroops.cpp
  50. 0 4
      AI/Nullkiller/Goals/GatherTroops.h
  51. 5 5
      AI/Nullkiller/Goals/GetArtOfType.cpp
  52. 0 6
      AI/Nullkiller/Goals/GetArtOfType.h
  53. 0 5
      AI/Nullkiller/Goals/Goals.h
  54. 0 22
      AI/Nullkiller/Goals/Invalid.h
  55. 5 0
      AI/Nullkiller/Goals/RecruitHero.cpp
  56. 12 3
      AI/Nullkiller/Goals/RecruitHero.h
  57. 1 6
      AI/Nullkiller/Goals/Trade.cpp
  58. 0 2
      AI/Nullkiller/Goals/Trade.h
  59. 2 1
      AI/Nullkiller/Goals/VisitHero.h
  60. 0 2
      AI/Nullkiller/Goals/VisitObj.cpp
  61. 2 0
      AI/Nullkiller/Goals/VisitObj.h
  62. 2 1
      AI/Nullkiller/Goals/VisitTile.h
  63. 4 119
      AI/Nullkiller/VCAI.cpp
  64. 1 8
      AI/Nullkiller/VCAI.h

+ 2 - 2
AI/Nullkiller/Behaviors/Behavior.h

@@ -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;
 };

+ 1 - 1
AI/Nullkiller/Behaviors/BuildingBehavior.cpp

@@ -29,7 +29,7 @@ std::string BuildingBehavior::toString() const
 	return "Build";
 }
 
-Goals::TGoalVec BuildingBehavior::getTasks()
+Goals::TGoalVec BuildingBehavior::decompose() const
 {
 	Goals::TGoalVec tasks;
 

+ 15 - 8
AI/Nullkiller/Behaviors/BuildingBehavior.h

@@ -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;
+		}
+	};
+}
 

+ 1 - 1
AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp

@@ -29,7 +29,7 @@ std::string BuyArmyBehavior::toString() const
 	return "Buy army";
 }
 
-Goals::TGoalVec BuyArmyBehavior::getTasks()
+Goals::TGoalVec BuyArmyBehavior::decompose() const
 {
 	Goals::TGoalVec tasks;
 

+ 15 - 9
AI/Nullkiller/Behaviors/BuyArmyBehavior.h

@@ -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;
+		}
+	};
+}

+ 7 - 7
AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp

@@ -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",

+ 48 - 37
AI/Nullkiller/Behaviors/CaptureObjectsBehavior.h

@@ -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 decompose() const override;
+		virtual std::string toString() const override;
 
-	virtual Goals::TGoalVec getTasks() 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;
-	}
+		virtual bool operator==(const CaptureObjectsBehavior & other) const override
+		{
+			return false;
+		}
 
-private:
-	bool shouldVisitObject(ObjectIdRef obj) const;
-};
+	private:
+		bool shouldVisitObject(ObjectIdRef obj) const;
+	};
+}
 

+ 237 - 0
AI/Nullkiller/Behaviors/CompleteQuestBehavior.cpp

@@ -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 - 0
AI/Nullkiller/Behaviors/CompleteQuestBehavior.h

@@ -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;
+	};
+}

+ 3 - 4
AI/Nullkiller/Behaviors/DefenceBehavior.cpp

@@ -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

+ 18 - 10
AI/Nullkiller/Behaviors/DefenceBehavior.h

@@ -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;
+	};
+}
 

+ 15 - 17
AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp

@@ -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",

+ 20 - 18
AI/Nullkiller/Behaviors/GatherArmyBehavior.h

@@ -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;
+	};
+}
 

+ 2 - 2
AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp

@@ -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)));
 			}
 		}
 	}

+ 16 - 8
AI/Nullkiller/Behaviors/RecruitHeroBehavior.h

@@ -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;
+		}
+	};
+}

+ 2 - 2
AI/Nullkiller/Behaviors/StartupBehavior.cpp

@@ -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())

+ 16 - 8
AI/Nullkiller/Behaviors/StartupBehavior.h

@@ -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;
+		}
+	};
+}
 

+ 2 - 12
AI/Nullkiller/CMakeLists.txt

@@ -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
 )
 

+ 63 - 29
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -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;

+ 2 - 3
AI/Nullkiller/Engine/Nullkiller.h

@@ -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;
 };

+ 1 - 4
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -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],

+ 0 - 216
AI/Nullkiller/FuzzyEngines.cpp

@@ -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;
-}

+ 0 - 34
AI/Nullkiller/FuzzyEngines.h

@@ -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;
-};

+ 13 - 6
AI/Nullkiller/Goals/AbstractGoal.cpp

@@ -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),

+ 32 - 32
AI/Nullkiller/Goals/AbstractGoal.h

@@ -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)
-
-			AbstractGoal(EGoals goal = EGoals::INVALID)
+		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)
 			: 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;
+	};
 }

+ 2 - 7
AI/Nullkiller/Goals/AdventureSpellCast.cpp

@@ -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;
 }

+ 3 - 9
AI/Nullkiller/Goals/AdventureSpellCast.h

@@ -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;
 	};
 }

+ 19 - 18
AI/Nullkiller/Goals/BuildBoat.cpp

@@ -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";
 }

+ 5 - 8
AI/Nullkiller/Goals/BuildBoat.h

@@ -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;
 	};
 }

+ 1 - 1
AI/Nullkiller/Goals/BuildThis.cpp

@@ -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;
 }

+ 6 - 6
AI/Nullkiller/Goals/BuildThis.h

@@ -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;
 	};
 }

+ 2 - 2
AI/Nullkiller/Goals/BuyArmy.cpp

@@ -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;
 }

+ 5 - 4
AI/Nullkiller/Goals/BuyArmy.h

@@ -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;
 	};
 }

+ 61 - 8
AI/Nullkiller/Goals/CGoal.h

@@ -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";
+		}
 	};
 }

+ 16 - 16
AI/Nullkiller/Goals/CollectRes.cpp

@@ -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;
-}
-
-TSubgoal CollectRes::whatToDoToAchieve()
-{
-	auto goals = getAllPossibleSubgoals();
-	auto trade = whatToDoToTrade();
-	if (!trade->invalid())
-		goals.push_back(trade);
-
-	return sptr(Invalid()); //we can always do that
-}
+//	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::whatToDoToTrade()
 {

+ 2 - 3
AI/Nullkiller/Goals/CollectRes.h

@@ -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;
 	};

+ 0 - 270
AI/Nullkiller/Goals/CompleteQuest.cpp

@@ -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;
-}

+ 0 - 45
AI/Nullkiller/Goals/CompleteQuest.h

@@ -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;
-	};
-}

+ 13 - 13
AI/Nullkiller/Goals/DigAtTile.cpp

@@ -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));
+//}

+ 3 - 6
AI/Nullkiller/Goals/DigAtTile.h

@@ -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;
 	};
 }

+ 1 - 9
AI/Nullkiller/Goals/DismissHero.cpp

@@ -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;
 }

+ 3 - 9
AI/Nullkiller/Goals/DismissHero.h

@@ -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;
 	};
 }

+ 2 - 7
AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp

@@ -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)

+ 2 - 8
AI/Nullkiller/Goals/ExchangeSwapTownHeroes.h

@@ -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;
 	};
 }

+ 22 - 20
AI/Nullkiller/Goals/ExecuteHeroChain.cpp

@@ -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);
 }

+ 6 - 8
AI/Nullkiller/Goals/ExecuteHeroChain.h

@@ -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);
 	};
 }

+ 29 - 29
AI/Nullkiller/Goals/FindObj.cpp

@@ -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()));
+//}

+ 3 - 7
AI/Nullkiller/Goals/FindObj.h

@@ -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;
 	};
 }

+ 27 - 27
AI/Nullkiller/Goals/GatherTroops.cpp

@@ -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());
-}
-
-
-TGoalVec GatherTroops::getAllPossibleSubgoals()
-{
-	TGoalVec solutions;
+//
+//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;
 
 	//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
+//}

+ 0 - 4
AI/Nullkiller/Goals/GatherTroops.h

@@ -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:

+ 5 - 5
AI/Nullkiller/Goals/GetArtOfType.cpp

@@ -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));
+//}

+ 0 - 6
AI/Nullkiller/Goals/GetArtOfType.h

@@ -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;
 	};
 }

+ 0 - 5
AI/Nullkiller/Goals/Goals.h

@@ -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"

+ 0 - 22
AI/Nullkiller/Goals/Invalid.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;
-		}
-	};
 }

+ 5 - 0
AI/Nullkiller/Goals/RecruitHero.cpp

@@ -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;
+}

+ 12 - 3
AI/Nullkiller/Goals/RecruitHero.h

@@ -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;
 	};
 }

+ 1 - 6
AI/Nullkiller/Goals/Trade.cpp

@@ -15,9 +15,4 @@ using namespace Goals;
 bool Trade::operator==(const Trade & other) const
 {
 	return resID == other.resID;
-}
-
-TSubgoal Trade::whatToDoToAchieve()
-{
-	return iAmElementar();
-}
+}

+ 0 - 2
AI/Nullkiller/Goals/Trade.h

@@ -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;
 	};
 }

+ 2 - 1
AI/Nullkiller/Goals/VisitHero.h

@@ -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;
 	};

+ 0 - 2
AI/Nullkiller/Goals/VisitObj.cpp

@@ -38,6 +38,4 @@ VisitObj::VisitObj(int Objid)
 		tile = obj->visitablePos();
 	else
 		logAi->error("VisitObj constructed with invalid object instance %d", Objid);
-
-	priority = 3;
 }

+ 2 - 0
AI/Nullkiller/Goals/VisitObj.h

@@ -9,6 +9,8 @@
 */
 #pragma once
 
+#error not used
+
 #include "CGoal.h"
 
 struct HeroPtr;

+ 2 - 1
AI/Nullkiller/Goals/VisitTile.h

@@ -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;
 	};

+ 4 - 119
AI/Nullkiller/VCAI.cpp

@@ -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)
 	{

+ 1 - 8
AI/Nullkiller/VCAI.h

@@ -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 ()