1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-26 22:57:00 +02:00

- Introduced a mechanism to set abstract goals for AI

- Fixed new crash when AI hero lost a battle
This commit is contained in:
DjWarmonger 2012-05-08 08:10:40 +00:00
parent 9cbbfcf258
commit b316be1701
2 changed files with 106 additions and 17 deletions

View File

@ -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(); cb->recalculatePaths();
if (startHpos == h->visitablePos()) if (startHpos == h->visitablePos())
{ {
throw cannotFulfillGoalException("Invalid path found!"); //FIXME throw cannotFulfillGoalException("Invalid path found!"); //FIXME
cb->recalculatePaths(); }
} }
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,6 +1820,62 @@ 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
} }
} }
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();
}
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;
}
}
//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); tryRealize(goal);
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
} }
@ -1799,6 +1886,7 @@ 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;
@ -1811,6 +1899,7 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal)
} }
} }
} }
}
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); return (*this).sethero(h).setisAbstract(true);
ret.sethero(h);
//throw goalFulfilledException("Found hero for exploration"); // FIXME: prevent all teh heroes to try explore same place
return ret.settile(whereToExplore(h));
} }
I_AM_ELEMENTAR; I_AM_ELEMENTAR;

View File

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