Переглянути джерело

VCAI is now functional again!
Implemented Visitor pattern for double-dispatch (actually only goal type dispatch).

DjWarmonger 12 роки тому
батько
коміт
0ab7b498f5
4 змінених файлів з 124 додано та 106 видалено
  1. 22 6
      AI/VCAI/Goals.cpp
  2. 33 21
      AI/VCAI/Goals.h
  3. 50 59
      AI/VCAI/VCAI.cpp
  4. 19 20
      AI/VCAI/VCAI.h

+ 22 - 6
AI/VCAI/Goals.cpp

@@ -18,6 +18,13 @@ extern boost::thread_specific_ptr<VCAI> ai;
 using namespace vstd;
 using namespace Goals;
 
+TSubgoal Goals::sptr(const AbstractGoal & tmp)
+{
+	shared_ptr<AbstractGoal> ptr;
+	ptr.reset(tmp.clone());
+	return ptr;
+}
+
 std::string Goals::AbstractGoal::name() const //TODO: virtualize
 {
 	switch (goalType)
@@ -67,7 +74,11 @@ std::string Goals::AbstractGoal::name() const //TODO: virtualize
 	}
 }
 
-
+//TSubgoal AbstractGoal::whatToDoToAchieve()
+//{
+//    logAi->debugStream() << boost::format("Decomposing goal of type %s") % name();
+//        return sptr (Goals::Explore());
+//}
 
 TSubgoal Win::whatToDoToAchieve()
 {
@@ -780,11 +791,11 @@ TSubgoal GatherArmy::whatToDoToAchieve()
 	return sptr (Goals::Explore(hero)); //find dwelling. use current hero to prevent him from doing nothing.
 }
 
-TSubgoal AbstractGoal::whatToDoToAchieve()
-{
-    logAi->debugStream() << boost::format("Decomposing goal of type %s") % name();
-	return sptr (Goals::Explore());
-}
+//TSubgoal AbstractGoal::whatToDoToAchieve()
+//{
+//    logAi->debugStream() << boost::format("Decomposing goal of type %s") % name();
+//	return sptr (Goals::Explore());
+//}
 
 TSubgoal AbstractGoal::goVisitOrLookFor(const CGObjectInstance *obj)
 {
@@ -803,3 +814,8 @@ bool AbstractGoal::invalid() const
 {
 	return goalType == INVALID;
 }
+
+void AbstractGoal::accept (VCAI * ai)
+{
+	ai->tryRealize(*this);
+};

+ 33 - 21
AI/VCAI/Goals.h

@@ -17,6 +17,7 @@
  *
  */
 struct HeroPtr;
+class VCAI;
 
 namespace Goals
 {
@@ -49,8 +50,8 @@ namespace Goals
 };
 
 	//method chaining + clone pattern
-#define VSETTER(type, field) AbstractGoal & set ## field(const type &rhs) { field = rhs; return *this; };
-#define OSETTER(type, field) CGoal<T> & set ## field(const type &rhs) { field = rhs; return *this; };
+#define VSETTER(type, field) virtual AbstractGoal & set ## field(const type &rhs) = 0;
+#define OSETTER(type, field) CGoal<T> & set ## field(const type &rhs) override { field = rhs; return *this; };
 
 #if 0
 	#define SETTER
@@ -84,6 +85,8 @@ public:
 		tile = int3(-1, -1, -1);
 		town = nullptr;
 	}
+	virtual ~AbstractGoal(){};
+	virtual AbstractGoal * clone() const = 0;
 
 	EGoals goalType;
 	std::string name() const;
@@ -94,7 +97,9 @@ public:
 	static TSubgoal lookForArtSmart(int aid); //checks non-standard ways of obtaining art (merchants, quests, etc.)
 	static TSubgoal tryRecruitHero();
 
-	virtual TSubgoal whatToDoToAchieve();
+	virtual TSubgoal whatToDoToAchieve() = 0;
+	//TODO: make accept work for shared_ptr... somehow
+	virtual void accept (VCAI * ai); //unhandled goal will report standard error
 
 	bool operator== (AbstractGoal &g) //TODO: virtualize - comparison returns true only for same subclasses
 	{
@@ -107,11 +112,11 @@ public:
 	}
 
 
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & goalType & isElementar & isAbstract & priority;
-		h & value & resID & objid & aid & tile & hero & town & bid;
-	}
+	//template <typename Handler> void serialize(Handler &h, const int version)
+	//{
+	//	h & goalType & isElementar & isAbstract & priority;
+	//	h & value & resID & objid & aid & tile & hero & town & bid;
+	//}
 };
 
 template <typename T> class CGoal : public AbstractGoal
@@ -128,7 +133,7 @@ public:
 		tile = int3(-1, -1, -1);
 		town = nullptr;
 	}
-	//virtual TSubgoal whatToDoToAchieve() override;
+	//virtual TSubgoal whatToDoToAchieve() override; //can't have virtual and template class at once
 
 	OSETTER(bool, isElementar)
 	OSETTER(bool, isAbstract)
@@ -141,25 +146,31 @@ public:
 	OSETTER(HeroPtr, hero)
 	OSETTER(CGTownInstance *, town)
 	OSETTER(int, bid)
-	shared_ptr<CGoal<T>> iAmElementar()
+
+	void accept (VCAI * ai) override
+	{
+		ai->tryRealize(static_cast<T&>(*this));
+	}
+
+	CGoal<T> * clone() const override
 	{
-		return make_shared<CGoal<T>> (setisElementar(true));
+		return new T(static_cast<T const&>(*this));
+	}
+	TSubgoal iAmElementar()
+	{
+		setisElementar(true);
+		shared_ptr<AbstractGoal> ptr;
+		ptr.reset(clone());
+		return ptr;
 	}
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & static_cast<AbstractGoal&>(*this);
+		h & goalType & isElementar & isAbstract & priority;
+		h & value & resID & objid & aid & tile & hero & town & bid;
 	}
 };
 
-//There seems to be some ambiguity on these two, template function keeps form consitent
-template <typename T> shared_ptr<CGoal<T>> sptr(const CGoal<T> & tmp)
-{
-	return make_shared<CGoal<T>> (tmp);
-}
-template <typename T> shared_ptr<CGoal<T>> sptr(const T & obj)
-{
-	return make_shared<CGoal<T>> (obj);
-}
+TSubgoal sptr(const AbstractGoal & tmp);
 
 class Invalid : public CGoal<Invalid>
 {
@@ -190,6 +201,7 @@ class Build : public CGoal<Build>
 	public:
 	Build() : CGoal (Goals::BUILD){};
 	TSubgoal whatToDoToAchieve() override;
+	void accept (const VCAI *);
 };
 class Explore : public CGoal<Explore>
 {

+ 50 - 59
AI/VCAI/VCAI.cpp

@@ -19,12 +19,13 @@
 extern FuzzyHelper *fh;
 
 class CGVisitableOPW;
-class TSubgoal;
 
 const double SAFE_ATTACK_CONSTANT = 1.5;
 const int GOLD_RESERVE = 10000; //when buying creatures we want to keep at least this much gold (10000 so at least we'll be able to reach capitol)
 
 using namespace vstd;
+//extern Goals::TSubgoal sptr(const Goals::AbstractGoal & tmp);
+//#define sptr(x) Goals::sptr(x)
 
 //one thread may be turn of AI and another will be handling a side effect for AI2
 boost::thread_specific_ptr<CCallback> cb;
@@ -258,7 +259,7 @@ void VCAI::heroVisit(const CGHeroInstance *visitor, const CGObjectInstance *visi
 		markObjectVisited (visitedObj);
 		erase_if_present(reservedObjs, visitedObj); //unreserve objects
 		erase_if_present(reservedHeroesMap[visitor], visitedObj);
-		completeGoal (Goals::GetObj(visitedObj->id.getNum()).sethero(visitor)); //we don't need to visit in anymore
+		completeGoal (sptr(Goals::GetObj(visitedObj->id.getNum()).sethero(visitor))); //we don't need to visit in anymore
 	}
 
 	status.heroVisit(visitedObj, start);
@@ -312,8 +313,8 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
 		else if (canGetArmy (secondHero, firstHero))
 			pickBestCreatures (secondHero, firstHero);
 
-		completeGoal(Goals::AbstractGoal(Goals::VISIT_HERO).sethero(firstHero)); //TODO: what if we were visited by other hero in the meantime?
-		completeGoal(Goals::AbstractGoal(Goals::VISIT_HERO).sethero(secondHero));
+		completeGoal(sptr(Goals::VisitHero(firstHero->id.getNum()))); //TODO: what if we were visited by other hero in the meantime?
+		completeGoal(sptr(Goals::VisitHero(secondHero->id.getNum())));
 		//TODO: exchange artifacts
 
 		answerQuery(query, 0);
@@ -668,7 +669,7 @@ void VCAI::makeTurn()
 				ui64 averageDanger = totalDanger / std::max(dangerousObjects, 1);
 				if (dangerousObjects && averageDanger > h->getHeroStrength())
 				{
-					setGoal (h, Goals::GatherArmy(averageDanger * SAFE_ATTACK_CONSTANT).sethero(h).setisAbstract(true));
+					setGoal (h, sptr(Goals::GatherArmy(averageDanger * SAFE_ATTACK_CONSTANT).sethero(h).setisAbstract(true)));
 				}
 			}
 		}
@@ -724,10 +725,10 @@ void VCAI::makeTurnInternal()
 		//finally, continue our abstract long-term goals
 
 		//heroes tend to die in the process and loose their goals, unsafe to iterate it
-		std::vector<std::pair<HeroPtr, Goals::AbstractGoal> > safeCopy;
+		std::vector<std::pair<HeroPtr, Goals::TSubgoal> > safeCopy;
 		boost::copy(lockedHeroes, std::back_inserter(safeCopy));
 
-		typedef std::pair<HeroPtr, Goals::AbstractGoal> TItrType;
+		typedef std::pair<HeroPtr, Goals::TSubgoal> TItrType;
 
 		auto lockedHeroesSorter = [](TItrType h1, TItrType h2) -> bool
 		{
@@ -741,7 +742,7 @@ void VCAI::makeTurnInternal()
 			if (it->first && it->first->tempOwner == playerID && vstd::contains(lockedHeroes, it->first)) //make sure hero still has his goal
 			{
 				cb->setSelection(*it->first);
-				striveToGoal (make_shared<Goals::AbstractGoal>(it->second));
+				striveToGoal (it->second);
 			}
 			safeCopy.erase(it);
 		}
@@ -1214,29 +1215,22 @@ void VCAI::wander(HeroPtr h)
 	}
 }
 
-void VCAI::setGoal(HeroPtr h, const Goals::AbstractGoal &goal)
+void VCAI::setGoal(HeroPtr h, Goals::TSubgoal goal)
 { //TODO: check for presence?
-	if (goal.invalid())
+	if (goal->invalid())
 		erase_if_present(lockedHeroes, h);
 	else
-		lockedHeroes[h] = Goals::AbstractGoal(goal).setisElementar(false); //always evaluate goals before realizing
+		lockedHeroes[h] = goal;
+		goal->setisElementar(false); //always evaluate goals before realizing
 }
 
-void VCAI::setGoal(HeroPtr h, Goals::EGoals goalType)
+void VCAI::completeGoal (Goals::TSubgoal goal)
 {
-	if (goalType == Goals::INVALID)
-		erase_if_present(lockedHeroes, h);
-	else
-		lockedHeroes[h] = Goals::AbstractGoal(goalType).setisElementar(false); //always evaluate goals before realizing;
-}
-
-void VCAI::completeGoal (const Goals::AbstractGoal &goal)
-{
-	if (const CGHeroInstance * h = goal.hero.get(true))
+	if (const CGHeroInstance * h = goal->hero.get(true))
 	{
 		auto it = lockedHeroes.find(h);
 		if (it != lockedHeroes.end())
-			if (it->second.goalType == goal.goalType)
+			if (it->second->goalType == goal->goalType)
 				lockedHeroes.erase(it); //goal fulfilled, free hero
 	}
 }
@@ -1426,7 +1420,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 		{
             logAi->errorStream() << "Hero " << h->name << " cannot reach " << dst;
 			//setGoal(h, INVALID);
-			completeGoal (Goals::AbstractGoal(Goals::VISIT_TILE).sethero(h));
+			completeGoal (sptr(Goals::VisitTile(int3(-1,-1,-1)).sethero(h))); //TODO: better mechanism to determine goal
 			cb->recalculatePaths();
 			throw std::runtime_error("Wrong move order!");
 		}
@@ -1483,12 +1477,12 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
     logAi->debugStream() << boost::format("Hero %s moved from %s to %s. Returning %d.") % h->name % startHpos % h->visitablePos() % ret;
 	return ret;
 }
-void VCAI::tryRealize(Goals::Explore g)
+void VCAI::tryRealize(Goals::Explore & g)
 {
 	throw cannotFulfillGoalException("EXPLORE is not a elementar goal!");
 }
 
-void VCAI::tryRealize(Goals::RecruitHero g)
+void VCAI::tryRealize(Goals::RecruitHero & g)
 {
 	if(const CGTownInstance *t = findTownWithTavern())
 	{
@@ -1498,38 +1492,39 @@ void VCAI::tryRealize(Goals::RecruitHero g)
 	}
 }
 
-void VCAI::tryRealize(Goals::VisitTile g)
+void VCAI::tryRealize(Goals::VisitTile & g)
 {
 	//cb->recalculatePaths();
 	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->warnStream() << boost::format("Why do I want to move hero %s to tile %s? Already standing on that tile! ") % g.hero->name % g.tile;
-		throw goalFulfilledException (g);
+		logAi->warnStream() << boost::format("Why do I want to move hero %s to tile %s? Already standing on that tile! ")
+												% g.hero->name % g.tile;
+		throw goalFulfilledException (sptr(g));
 	}
 	//if(!g.isBlockedBorderGate(g.tile))
 	//{
 		if (ai->moveHeroToTile(g.tile, g.hero.get()))
 		{
-			throw goalFulfilledException (g);
+			throw goalFulfilledException (sptr(g));
 		}
 	//}
 	//else
 	//	throw cannotFulfillGoalException("There's a blocked gate!, we should never be here"); //CLEAR_WAY_TO should get keymaster tent
 }
 
-void VCAI::tryRealize(Goals::VisitHero g)
+void VCAI::tryRealize(Goals::VisitHero & g)
 {
 	if(!g.hero->movement)
 		throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!");
 	if (ai->moveHeroToTile(g.tile, g.hero.get()))
 	{
-		throw goalFulfilledException (g);
+		throw goalFulfilledException (sptr(g));
 	}
 }
 
-void VCAI::tryRealize(Goals::BuildThis g)
+void VCAI::tryRealize(Goals::BuildThis & g)
 {
 	const CGTownInstance *t = g.town;
 
@@ -1558,22 +1553,22 @@ void VCAI::tryRealize(Goals::BuildThis g)
 	throw cannotFulfillGoalException("Cannot build a given structure!");
 }
 
-void VCAI::tryRealize(Goals::DigAtTile g)
+void VCAI::tryRealize(Goals::DigAtTile & g)
 {
 	assert(g.hero->visitablePos() == g.tile); //surely we want to crash here?
 	if (g.hero->diggingStatus() == CGHeroInstance::CAN_DIG)
 	{
 		cb->dig(g.hero.get());
-		setGoal(g.hero, Goals::INVALID); // finished digging
+		setGoal(g.hero, sptr(Goals::Invalid())); // finished digging
 	}
 	else
 	{
-		ai->lockedHeroes[g.hero] = g; //hero who tries to dig shouldn't do anything else
+		ai->lockedHeroes[g.hero] = sptr(g); //hero who tries to dig shouldn't do anything else
 		throw cannotFulfillGoalException("A hero can't dig!\n");
 	}
 }
 
-void VCAI::tryRealize(Goals::CollectRes g)
+void VCAI::tryRealize(Goals::CollectRes & g)
 {
 	if(cb->getResourceAmount(static_cast<Res::ERes>(g.resID)) >= g.value)
 	throw cannotFulfillGoalException("Goal is already fulfilled!");
@@ -1608,20 +1603,20 @@ void VCAI::tryRealize(Goals::CollectRes g)
 	}
 }
 
-void VCAI::tryRealize(Goals::Build g)
+void VCAI::tryRealize(Goals::Build & g)
 {
 	performTypicalActions(); //TODO: separate build and wander
 	throw cannotFulfillGoalException("BUILD has been realized as much as possible.");
 }
-void VCAI::tryRealize(Goals::Invalid g)
+void VCAI::tryRealize(Goals::Invalid & g)
 {
 	throw cannotFulfillGoalException("I don't know how to fulfill this!");
 }
 
-void VCAI::tryRealize(Goals::AbstractGoal g)
+void VCAI::tryRealize(Goals::AbstractGoal & g)
 {
     logAi->debugStream() << boost::format("Attempting realizing goal with code %s") % g.name();
-	throw cannotFulfillGoalException("Unknown type of goal !");
+        throw cannotFulfillGoalException("Unknown type of goal !");
 }
 
 const CGTownInstance * VCAI::findTownWithTavern() const
@@ -1641,7 +1636,7 @@ std::vector<HeroPtr> VCAI::getUnblockedHeroes() const
 	for(auto h : lockedHeroes)
 	{
 		//if (!h.second.invalid()) //we can use heroes without valid goal
-		if (h.second.goalType == Goals::DIG_AT_TILE || !h.first->movement) //experiment: use all heroes that have movement left, TODO: unlock heroes that couldn't realize their goals 
+		if (h.second->goalType == Goals::DIG_AT_TILE || !h.first->movement) //experiment: use all heroes that have movement left, TODO: unlock heroes that couldn't realize their goals 
 			erase_if_present(ret, h.first);
 	}
 	return ret;
@@ -1674,19 +1669,15 @@ void VCAI::endTurn()
     logAi->debugStream() << "Player " << static_cast<int>(playerID.getNum()) << " ended turn";
 }
 
-bool VCAI::fulfillsGoal (Goals::AbstractGoal &goal, Goals::AbstractGoal &mainGoal)
+bool VCAI::fulfillsGoal (Goals::TSubgoal goal, Goals::TSubgoal mainGoal)
 {
-	if (mainGoal.goalType == Goals::GET_OBJ && goal.goalType == Goals::VISIT_TILE) //deduce that GET_OBJ was completed by visiting object's tile
+	if (mainGoal->goalType == Goals::GET_OBJ && goal->goalType == Goals::VISIT_TILE) //deduce that GET_OBJ was completed by visiting object's tile
 	{ //TODO: more universal mechanism
-		if (cb->getObj(ObjectInstanceID(mainGoal.objid))->visitablePos() == goal.tile)
+		if (cb->getObj(ObjectInstanceID(mainGoal->objid))->visitablePos() == goal->tile)
 			return true;
 	}
 	return false;
 }
-bool VCAI::fulfillsGoal (Goals::AbstractGoal &goal, const Goals::AbstractGoal &mainGoal)
-{
-	return fulfillsGoal (goal, const_cast<Goals::AbstractGoal&>(mainGoal));
-}
 
 void VCAI::striveToGoal(Goals::TSubgoal ultimateGoal)
 {
@@ -1732,11 +1723,11 @@ void VCAI::striveToGoal(Goals::TSubgoal ultimateGoal)
 			{
 				if (maxGoals)
 				{
-					setGoal(goal->hero, *goal.get());
+					setGoal(goal->hero, goal);
 				}
 				else
 				{
-					setGoal(goal->hero, Goals::INVALID); // we seemingly don't know what to do with hero
+					setGoal(goal->hero, sptr(Goals::Invalid())); // we seemingly don't know what to do with hero
 				}
 			}
 
@@ -1747,7 +1738,7 @@ void VCAI::striveToGoal(Goals::TSubgoal ultimateGoal)
 				break;
 			}
 			else
-				tryRealize(*goal);
+				goal->accept(this);
 
 			boost::this_thread::interruption_point();
 		}
@@ -1758,8 +1749,8 @@ void VCAI::striveToGoal(Goals::TSubgoal ultimateGoal)
 		}
 		catch(goalFulfilledException &e)
 		{
-			completeGoal (*goal);
-			if (fulfillsGoal (*goal, *ultimateGoal.get()) || maxGoals > 98) //completed goal was main goal //TODO: find better condition
+			completeGoal (goal);
+			if (fulfillsGoal (goal, ultimateGoal) || maxGoals > 98) //completed goal was main goal //TODO: find better condition
 				return; 
 		}
 		catch(std::exception &e)
@@ -1802,7 +1793,7 @@ void VCAI::striveToGoal(Goals::TSubgoal ultimateGoal)
 					std::runtime_error e("Too many subgoals, don't know what to do");
 					throw (e);
 				}
-				tryRealize(*goal);
+				goal->accept(this);
 				boost::this_thread::interruption_point();
 			}
 			catch(boost::thread_interrupted &e)
@@ -1812,8 +1803,8 @@ void VCAI::striveToGoal(Goals::TSubgoal ultimateGoal)
 			}
 			catch(goalFulfilledException &e)
 			{
-				completeGoal (*goal); //FIXME: deduce that we have realized GET_OBJ goal
-				if (fulfillsGoal (*goal, *abstractGoal) || maxGoals > 98) //completed goal was main goal
+				completeGoal (goal); //FIXME: deduce that we have realized GET_OBJ goal
+				if (fulfillsGoal (goal, abstractGoal) || maxGoals > 98) //completed goal was main goal
 					return;
 			}
 			catch(std::exception &e)
@@ -2119,8 +2110,8 @@ void VCAI::checkHeroArmy (HeroPtr h)
 	auto it = lockedHeroes.find(h);
 	if (it != lockedHeroes.end())
 	{
-		if (it->second.goalType == Goals::GATHER_ARMY && it->second.value <= h->getArmyStrength())
-			completeGoal(Goals::GatherArmy(it->second.value).sethero(h));
+		if (it->second->goalType == Goals::GATHER_ARMY && it->second->value <= h->getArmyStrength())
+			completeGoal(sptr(Goals::GatherArmy(it->second->value).sethero(h)));
 	}
 }
 
@@ -2627,7 +2618,7 @@ int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
 		if(!preds[dst])
 		{
 			write("test.txt");
-			ai->completeGoal (Goals::Explore(h)); //if we can't find the way, seemingly all tiles were explored
+			ai->completeGoal (sptr(Goals::Explore(h))); //if we can't find the way, seemingly all tiles were explored
 			//TODO: more organized way?
             throw cannotFulfillGoalException(boost::str(boost::format("Cannot find connection between sectors %d and %d") % src->id % dst->id));
 		}

+ 19 - 20
AI/VCAI/VCAI.h

@@ -128,7 +128,7 @@ public:
 	//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
 	std::map<HeroPtr, std::vector<const CGTownInstance *> > townVisitsThisWeek;
 
-	std::map<HeroPtr, Goals::AbstractGoal> lockedHeroes; //TODO: allow non-elementar objectives
+	std::map<HeroPtr, Goals::TSubgoal> lockedHeroes; //TODO: allow non-elementar objectives
 	std::map<HeroPtr, std::vector<const CGObjectInstance *> > reservedHeroesMap; //objects reserved by specific heroes
 
 	std::vector<const CGObjectInstance *> visitableObjs;
@@ -147,16 +147,17 @@ public:
 	VCAI(void);
 	~VCAI(void);
 
-	void tryRealize(Goals::AbstractGoal g);
-	void tryRealize(Goals::Explore g);
-	void tryRealize(Goals::RecruitHero g);
-	void tryRealize(Goals::VisitTile g);
-	void tryRealize(Goals::VisitHero g);
-	void tryRealize(Goals::BuildThis g);
-	void tryRealize(Goals::DigAtTile g);
-	void tryRealize(Goals::CollectRes g);
-	void tryRealize(Goals::Build g);
-	void tryRealize(Goals::Invalid g);
+	//TODO: use only smart pointers?
+	void tryRealize(Goals::Explore & g);
+	void tryRealize(Goals::RecruitHero & g);
+	void tryRealize(Goals::VisitTile & g);
+	void tryRealize(Goals::VisitHero & g);
+	void tryRealize(Goals::BuildThis & g);
+	void tryRealize(Goals::DigAtTile & g);
+	void tryRealize(Goals::CollectRes & g);
+	void tryRealize(Goals::Build & g);
+	void tryRealize(Goals::Invalid & g);
+	void tryRealize(Goals::AbstractGoal & g);
 
 	int3 explorationBestNeighbour(int3 hpos, int radius, HeroPtr h);
 	int3 explorationNewPoint(int radius, HeroPtr h, std::vector<std::vector<int3> > &tiles);
@@ -234,12 +235,10 @@ public:
 	void striveToGoal(Goals::TSubgoal ultimateGoal);
 	void endTurn();
 	void wander(HeroPtr h);
-	void setGoal(HeroPtr h, const Goals::AbstractGoal &goal);
-	void setGoal(HeroPtr h, Goals::EGoals goalType = Goals::INVALID);
-	void completeGoal (const Goals::AbstractGoal &goal); //safely removes goal from reserved hero
+	void setGoal(HeroPtr h, Goals::TSubgoal goal);
+	void completeGoal (Goals::TSubgoal goal); //safely removes goal from reserved hero
 	void striveToQuest (const QuestInfo &q);
-	bool fulfillsGoal (Goals::AbstractGoal &goal, Goals::AbstractGoal &mainGoal);
-	bool fulfillsGoal (Goals::AbstractGoal &goal, const Goals::AbstractGoal &mainGoal); //TODO: something smarter
+	bool fulfillsGoal (Goals::TSubgoal goal, Goals::TSubgoal mainGoal);
 
 	void recruitHero(const CGTownInstance * t, bool throwing = false);
 	std::vector<const CGObjectInstance *> getPossibleDestinations(HeroPtr h);
@@ -291,7 +290,7 @@ public:
 
 	template <typename Handler> void serializeInternal(Handler &h, const int version)
 	{
-		h & knownSubterraneanGates & townVisitsThisWeek & lockedHeroes & reservedHeroesMap;
+		h & knownSubterraneanGates & townVisitsThisWeek;// & lockedHeroes & reservedHeroesMap; //FIXME: cannot instantiate abstract class
 		h & visitableObjs & alreadyVisited & reservedObjs;
 		h & saving & status & battlename;
 
@@ -320,9 +319,9 @@ public:
 class goalFulfilledException : public std::exception
 {
 public:
-	Goals::AbstractGoal goal;
+	Goals::TSubgoal goal;
 
-	explicit goalFulfilledException(Goals::AbstractGoal Goal) : goal(Goal)
+	explicit goalFulfilledException(Goals::TSubgoal Goal) : goal(Goal)
 	{
 	}
 
@@ -332,7 +331,7 @@ public:
 
 	const char *what() const throw () override
 	{
-		return goal.name().c_str();
+		return goal->name().c_str();
 	}
 };