Pārlūkot izejas kodu

- Introduced a mechanism to set abstract goals for AI
- Fixed new crash when AI hero lost a battle

DjWarmonger 13 gadi atpakaļ
vecāks
revīzija
b316be1701
2 mainītis faili ar 106 papildinājumiem un 17 dzēšanām
  1. 102 16
      AI/VCAI/VCAI.cpp
  2. 4 1
      AI/VCAI/VCAI.h

+ 102 - 16
AI/VCAI/VCAI.cpp

@@ -964,8 +964,22 @@ void VCAI::makeTurnInternal()
 
 
 	try
 	try
 	{
 	{
+		//Pick objects reserved in previous turn - we expect only nerby objects there
+		BOOST_FOREACH (auto hero, reservedHeroesMap)
+		{
+			cb->setSelection(hero.first);
+			boost::sort (hero.second, isCloser);
+			BOOST_FOREACH (auto obj, hero.second)
+			{
+				const CGHeroInstance * h = hero.first;
+				striveToGoal (CGoal(VISIT_TILE).sethero(h).settile(obj->visitablePos()));
+			}
+		}
+
+		//now try to win
 		striveToGoal(CGoal(WIN));
 		striveToGoal(CGoal(WIN));
 
 
+		//finally, continue our abstract long-temr goals
 		std::vector<std::pair<const CGHeroInstance *, CGoal> > safeCopy; //heroes tend to die in the process and loose their goals, unsafe to iterate it
 		std::vector<std::pair<const CGHeroInstance *, CGoal> > safeCopy; //heroes tend to die in the process and loose their goals, unsafe to iterate it
 		BOOST_FOREACH (auto h, lockedHeroes)
 		BOOST_FOREACH (auto h, lockedHeroes)
 		{
 		{
@@ -1255,7 +1269,9 @@ void VCAI::wander(const CGHeroInstance * h)
 			{
 			{
 				BNLOG("Hero %s apparently used all MPs (%d left)\n", h->name % h->movement);
 				BNLOG("Hero %s apparently used all MPs (%d left)\n", h->name % h->movement);
 				reserveObject(h, dest); //reserve that object - we predict it will be reached soon
 				reserveObject(h, dest); //reserve that object - we predict it will be reached soon
-				setGoal(h, CGoal(VISIT_TILE).sethero(h).settile(dest->visitablePos()));
+
+				//removed - do not forget abstract goal so easily
+				//setGoal(h, CGoal(VISIT_TILE).sethero(h).settile(dest->visitablePos()));
 			}
 			}
 			break;
 			break;
 		}
 		}
@@ -1285,6 +1301,17 @@ void VCAI::setGoal (const CGHeroInstance *h, EGoals goalType)
 		lockedHeroes[h] = CGoal(goalType).setisElementar(false); //always evaluate goals before realizing;
 		lockedHeroes[h] = CGoal(goalType).setisElementar(false); //always evaluate goals before realizing;
 }
 }
 
 
+void VCAI::completeGoal (const CGoal goal)
+{
+	if (const CGHeroInstance * h = goal.hero)
+	{
+		auto it = lockedHeroes.find(h);
+		if (it != lockedHeroes.end())
+			if (it->second.goalType == goal.goalType)
+				lockedHeroes.erase(it); //goal fulfilled, free hero
+	}
+}
+
 void VCAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side)
 void VCAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side)
 {
 {
 	NET_EVENT_HANDLER;
 	NET_EVENT_HANDLER;
@@ -1324,7 +1351,7 @@ void VCAI::markObjectVisited (const CGObjectInstance *obj)
 void VCAI::reserveObject (const CGHeroInstance * h, const CGObjectInstance *obj)
 void VCAI::reserveObject (const CGHeroInstance * h, const CGObjectInstance *obj)
 {
 {
 	reservedObjs.push_back(obj);
 	reservedObjs.push_back(obj);
-	reservedHeroesMap[h].insert(obj);
+	reservedHeroesMap[h].push_back(obj);
 }
 }
 
 
 void VCAI::validateVisitableObjs()
 void VCAI::validateVisitableObjs()
@@ -1476,7 +1503,8 @@ bool VCAI::moveHeroToTile(int3 dst, const CGHeroInstance * h)
 		if(path.nodes.empty())
 		if(path.nodes.empty())
 		{
 		{
 			tlog1 << "Hero " << h->name << " cannot reach " << dst << std::endl;
 			tlog1 << "Hero " << h->name << " cannot reach " << dst << std::endl;
-			setGoal(h, INVALID);
+			//setGoal(h, INVALID);
+			completeGoal (CGoal(VISIT_TILE).sethero(h));
 			cb->recalculatePaths();
 			cb->recalculatePaths();
 			throw std::runtime_error("Wrong move order!");
 			throw std::runtime_error("Wrong move order!");
 		}
 		}
@@ -1527,11 +1555,12 @@ bool VCAI::moveHeroToTile(int3 dst, const CGHeroInstance * h)
 	}
 	}
 
 
 	if(h->tempOwner == playerID) //lost hero after last move
 	if(h->tempOwner == playerID) //lost hero after last move
-		cb->recalculatePaths();
-	if (startHpos == h->visitablePos())
 	{
 	{
-		throw cannotFulfillGoalException("Invalid path found!"); //FIXME
 		cb->recalculatePaths();
 		cb->recalculatePaths();
+		if (startHpos == h->visitablePos())
+		{
+			throw cannotFulfillGoalException("Invalid path found!"); //FIXME
+		}
 	}
 	}
 	BNLOG("Hero %s moved from %s to %s", h->name % startHpos % h->visitablePos());
 	BNLOG("Hero %s moved from %s to %s", h->name % startHpos % h->visitablePos());
 	return ret;
 	return ret;
@@ -1603,7 +1632,6 @@ void VCAI::tryRealize(CGoal g)
 			{
 			{
 				if (ai->moveHeroToTile(g.tile, g.hero))
 				if (ai->moveHeroToTile(g.tile, g.hero))
 				{
 				{
-					setGoal (g.hero, INVALID); //tile reached, we can unlock hero
 					throw goalFulfilledException("");
 					throw goalFulfilledException("");
 				}
 				}
 			}
 			}
@@ -1752,12 +1780,15 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal)
 {
 {
 	if (ultimateGoal.invalid())
 	if (ultimateGoal.invalid())
 		return;
 		return;
+
+	CGoal abstractGoal;
+
 	while(1)
 	while(1)
 	{
 	{
 		CGoal goal = ultimateGoal;
 		CGoal goal = ultimateGoal;
 		BNLOG("Striving to goal of type %s", goalName(ultimateGoal.goalType));
 		BNLOG("Striving to goal of type %s", goalName(ultimateGoal.goalType));
 		int maxGoals = 100; //preventing deadlock for mutually dependent goals
 		int maxGoals = 100; //preventing deadlock for mutually dependent goals
-		while(!goal.isElementar && maxGoals)
+		while(!goal.isElementar && !goal.isAbstract && maxGoals)
 		{
 		{
 			INDENT;
 			INDENT;
 			BNLOG("Considering goal %s", goalName(goal.goalType));
 			BNLOG("Considering goal %s", goalName(goal.goalType));
@@ -1777,11 +1808,11 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal)
 		try
 		try
 		{
 		{
 			boost::this_thread::interruption_point();
 			boost::this_thread::interruption_point();
+
 			if (goal.hero) //lock this hero to fulfill ultimate goal
 			if (goal.hero) //lock this hero to fulfill ultimate goal
 			{
 			{
 				if (maxGoals)
 				if (maxGoals)
 				{
 				{
-					//we shouldn't abandon high-level goal
 					setGoal (goal.hero, goal);
 					setGoal (goal.hero, goal);
 				}
 				}
 				else
 				else
@@ -1789,7 +1820,16 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal)
 					setGoal (goal.hero, INVALID); // we seemingly don't know what to do with hero
 					setGoal (goal.hero, INVALID); // we seemingly don't know what to do with hero
 				}
 				}
 			}
 			}
-			tryRealize(goal);
+
+			if (goal.isAbstract) 
+			{
+				abstractGoal = goal; //allow only one abstract goal per call
+				BNLOG("Choosing abstract goal %s", goalName(goal.goalType));
+				break;
+			}
+			else
+				tryRealize(goal);
+
 			boost::this_thread::interruption_point();
 			boost::this_thread::interruption_point();
 		}
 		}
 		catch(boost::thread_interrupted &e)
 		catch(boost::thread_interrupted &e)
@@ -1799,9 +1839,10 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal)
 		}
 		}
 		catch(goalFulfilledException &e)
 		catch(goalFulfilledException &e)
 		{
 		{
+			completeGoal (goal);
 			if (maxGoals > 98) //completed goal was main goal
 			if (maxGoals > 98) //completed goal was main goal
 				//TODO: find better condition
 				//TODO: find better condition
-				return;
+			return;
 		}
 		}
 		catch(std::exception &e)
 		catch(std::exception &e)
 		{
 		{
@@ -1810,6 +1851,54 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal)
 			break;
 			break;
 		}
 		}
 	}
 	}
+
+	//TODO: save abstract goals not related to hero
+	if (!abstractGoal.invalid()) //try to realize our one goal
+	{
+		while (1)
+		{
+			CGoal goal = CGoal(abstractGoal).setisAbstract(false);
+			int maxGoals = 50;
+			while (!goal.isElementar && maxGoals) //find elementar goal and fulfill it
+			{
+				try
+				{
+					boost::this_thread::interruption_point();
+					goal = goal.whatToDoToAchieve();
+					--maxGoals;
+				}
+				catch(std::exception &e)
+				{
+					BNLOG("Goal %s decomposition failed: %s", goalName(goal.goalType) % e.what());
+					return;
+				}
+			}
+			try
+			{
+				boost::this_thread::interruption_point();
+				tryRealize(goal);
+				boost::this_thread::interruption_point();
+			}
+			catch(boost::thread_interrupted &e)
+			{
+				BNLOG("Player %d: Making turn thread received an interruption!", playerID);
+				throw; //rethrow, we want to truly end this thread
+			}
+			catch(goalFulfilledException &e)
+			{
+				completeGoal (goal);
+				if (maxGoals > 98) //completed goal was main goal
+					//TODO: find better condition
+					return;
+			}
+			catch(std::exception &e)
+			{
+				BNLOG("Failed to realize subgoal of type %s (greater goal type was %s), I will stop.", goalName(goal.goalType) % goalName(ultimateGoal.goalType));
+				BNLOG("The error message was: %s", e.what());
+				break;
+			}
+		}
+	}
 }
 }
 
 
 void VCAI::performTypicalActions()
 void VCAI::performTypicalActions()
@@ -2275,7 +2364,7 @@ TSubgoal CGoal::whatToDoToAchieve()
 	case EXPLORE:
 	case EXPLORE:
 		{
 		{
 			if (hero)
 			if (hero)
-				return CGoal(VISIT_TILE).settile(whereToExplore(hero));
+				return CGoal(VISIT_TILE).settile(whereToExplore(hero)).sethero(hero);
 
 
 			auto hs = cb->getHeroesInfo();
 			auto hs = cb->getHeroesInfo();
 			int howManyHeroes = hs.size();
 			int howManyHeroes = hs.size();
@@ -2308,10 +2397,7 @@ TSubgoal CGoal::whatToDoToAchieve()
 
 
 			const CGHeroInstance *h = hs.front();
 			const CGHeroInstance *h = hs.front();
 
 
-			CGoal ret(VISIT_TILE);
-			ret.sethero(h);
-			//throw goalFulfilledException("Found hero for exploration"); // FIXME: prevent all teh heroes to try explore same place
-			return ret.settile(whereToExplore(h));
+			return (*this).sethero(h).setisAbstract(true);
 		}
 		}
 
 
 		I_AM_ELEMENTAR;
 		I_AM_ELEMENTAR;

+ 4 - 1
AI/VCAI/VCAI.h

@@ -73,6 +73,7 @@ struct CGoal
 {
 {
 	EGoals goalType;
 	EGoals goalType;
 	bool isElementar; SETTER(bool, isElementar)
 	bool isElementar; SETTER(bool, isElementar)
+	bool isAbstract; SETTER(bool, isAbstract) //allows to remember abstract goals
 	int priority; SETTER(bool, priority)
 	int priority; SETTER(bool, priority)
 
 
 	virtual TSubgoal whatToDoToAchieve();
 	virtual TSubgoal whatToDoToAchieve();
@@ -82,6 +83,7 @@ struct CGoal
 	{
 	{
 		priority = 0;
 		priority = 0;
 		isElementar = false;
 		isElementar = false;
+		isAbstract = false;
 		objid = -1;
 		objid = -1;
 		aid = -1;
 		aid = -1;
 		tile = int3(-1, -1, -1);
 		tile = int3(-1, -1, -1);
@@ -180,7 +182,7 @@ public:
 	std::map<const CGHeroInstance *, std::vector<const CGTownInstance *> > townVisitsThisWeek;
 	std::map<const CGHeroInstance *, std::vector<const CGTownInstance *> > townVisitsThisWeek;
 
 
 	std::map<const CGHeroInstance *, CGoal> lockedHeroes; //TODO: allow non-elementar objectives
 	std::map<const CGHeroInstance *, CGoal> lockedHeroes; //TODO: allow non-elementar objectives
-	std::map<const CGHeroInstance *, std::set<const CGObjectInstance *> > reservedHeroesMap; //objects reserved by specific heroes
+	std::map<const CGHeroInstance *, std::vector<const CGObjectInstance *> > reservedHeroesMap; //objects reserved by specific heroes
 
 
 	std::vector<const CGObjectInstance *> visitableObjs;
 	std::vector<const CGObjectInstance *> visitableObjs;
 	std::vector<const CGObjectInstance *> alreadyVisited;
 	std::vector<const CGObjectInstance *> alreadyVisited;
@@ -275,6 +277,7 @@ public:
 	void wander(const CGHeroInstance * h);
 	void wander(const CGHeroInstance * h);
 	void setGoal (const CGHeroInstance *h, const CGoal goal);
 	void setGoal (const CGHeroInstance *h, const CGoal goal);
 	void setGoal (const CGHeroInstance *h, EGoals goalType = INVALID);
 	void setGoal (const CGHeroInstance *h, EGoals goalType = INVALID);
+	void completeGoal (const CGoal goal); //safely removes goal from reserved hero
 
 
 	void recruitHero(const CGTownInstance * t);
 	void recruitHero(const CGTownInstance * t);
 	std::vector<const CGObjectInstance *> getPossibleDestinations(const CGHeroInstance *h);
 	std::vector<const CGObjectInstance *> getPossibleDestinations(const CGHeroInstance *h);