1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-24 03:47:18 +02:00

- AI will now use SectorMap to find a way to guarded / covered objects.

- Improvements to SectorMap needed for use of multiple heroes
This commit is contained in:
DjWarmonger 2014-02-15 16:38:51 +00:00
parent d01b84b460
commit d8933b5c36
4 changed files with 97 additions and 54 deletions

View File

@ -476,7 +476,7 @@ float FuzzyHelper::evaluate (Goals::GatherArmy & g)
//the more army we need, the more important goal
//the more army we lack, the less important goal
float army = g.hero->getArmyStrength();
return g.value / std::min(g.value - army, 1000.0f);
return g.value / std::max(g.value - army, 1000.0f);
}
float FuzzyHelper::evaluate (Goals::BuildThis & g)
{

View File

@ -407,11 +407,12 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
continue;
cb->setSelection(h);
SectorMap sm;
int3 tileToHit = sm.firstTileToGet(hero ? hero : h, tile);
//if our hero is trapped, make sure we request clearing the way from OUR perspective
if (!tileToHit.valid())
continue;
if (isBlockedBorderGate(tileToHit))
{ //FIXME: this way we'll not visit gate and activate quest :?
@ -510,42 +511,35 @@ TGoalVec Explore::getAllPossibleSubgoals()
{
for (auto obj : objs) //double loop, performance risk?
{
if (ai->isAccessibleForHero(obj->visitablePos(), h) && isSafeToVisit(h, obj->visitablePos()))
cb->setSelection(h);
SectorMap sm; //seems to depend on hero
auto t = sm.firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded
if (t.valid())
{
ret.push_back (sptr (Goals::VisitTile(obj->visitablePos()).sethero(h)));
//auto topObj = backOrNull(cb->getVisitableObjs(t));
//if (topObj && topObj->ID == Obj::HERO && topObj != h)
// continue; //if the tile is occupied by another hero, we are not interested in going there
if (isSafeToVisit(h, t))
{
ret.push_back (sptr (Goals::VisitTile(t).sethero(h)));
}
else
{
ret.push_back (sptr (Goals::GatherArmy(evaluateDanger(t, h)*SAFE_ATTACK_CONSTANT).
sethero(h).setisAbstract(true)));
}
}
}
int3 t = whereToExplore(h);
if (cb->isInTheMap(t)) //valid tile was found - could be invalid (none)
if (t.valid())
ret.push_back (sptr (Goals::VisitTile(t).sethero(h)));
}
//we either don't have hero yet or none of heroes can explore
if ((!hero || ret.empty()) && ai->canRecruitAnyHero())
ret.push_back (sptr(Goals::RecruitHero()));
if (ret.empty())
{
HeroPtr h;
if (hero) //there is some hero set and it's us
{
if (hero == ai->primaryHero())
h = hero;
}
else //no hero is set, so we choose our main
h = ai->primaryHero();
//we may need to gather big army to break!
if (h.h)
{
//FIXME: it never finds anything :?
int3 t = ai->explorationNewPoint(h->getSightRadious(), h, true);
if (cb->isInTheMap(t))
ret.push_back (sptr(ClearWayTo(t).setisAbstract(true).sethero(h)));
else //just in case above fails - gather army if no further exploration possible
ret.push_back (sptr(GatherArmy(h->getArmyStrength() + 1).sethero(h)));
//do not set abstract to keep our hero free once he gets reinforcements
}
}
if (ret.empty())
{
throw goalFulfilledException (sptr(Goals::Explore().sethero(hero)));
@ -790,31 +784,50 @@ TGoalVec Conquer::getAllPossibleSubgoals()
{
TGoalVec ret;
std::vector<const CGObjectInstance *> objs; //here we'll gather enemy towns and heroes
ai->retreiveVisitableObjs(objs);
erase_if(objs, [&](const CGObjectInstance *obj)
std::vector<const CGObjectInstance *> objs;
for (auto obj : ai->visitableObjs)
{
return (obj->ID != Obj::TOWN && obj->ID != Obj::HERO && //not town/hero
obj->ID != Obj::CREATURE_GENERATOR1 && obj->ID != Obj::MINE) //not dwelling or mine
|| cb->getPlayerRelations(ai->playerID, obj->tempOwner) != PlayerRelations::ENEMIES; //only enemy objects are interesting
});
erase_if(objs, [&](const CGObjectInstance *obj)
{
return vstd::contains (ai->reservedObjs, obj);
//no need to capture same object twice
});
if (!vstd::contains (ai->reservedObjs, obj) && //no need to capture same object twice
cb->getPlayerRelations(ai->playerID, obj->tempOwner) == PlayerRelations::ENEMIES) //only enemy objects are interesting
{
switch (obj->ID.num)
{
case Obj::TOWN:
case Obj::HERO:
case Obj::CREATURE_GENERATOR1:
case Obj::MINE: //TODO: check ai->knownSubterraneanGates
objs.push_back (obj);
}
}
}
for (auto h : cb->getHeroesInfo())
{
for (auto obj : objs) //double loop, performance risk?
{
if (ai->isAccessibleForHero(obj->visitablePos(), h) && isSafeToVisit(h, obj->visitablePos()))
cb->setSelection(h);
SectorMap sm; //seems to depend on hero
auto t = sm.firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded
if (t.valid())
{
if (obj->ID == Obj::HERO)
ret.push_back (sptr (Goals::VisitHero(obj->id.getNum()).sethero(h).setisAbstract(true)));
//track enemy hero
//auto topObj = backOrNull(cb->getVisitableObjs(t));
//if (topObj && topObj->ID == Obj::HERO && topObj != h)
// continue; //if the tile is occupied by another hero, we are not interested in going there
//FIXME: should firstTileToGet return position of our other hero?
if (isSafeToVisit(h, t))
{
if (obj->ID == Obj::HERO)
ret.push_back (sptr (Goals::VisitHero(obj->id.getNum()).sethero(h).setisAbstract(true)));
//track enemy hero
else
ret.push_back (sptr (Goals::VisitTile(t).sethero(h)));
}
else
ret.push_back (sptr (Goals::VisitTile(obj->visitablePos()).sethero(h)));
{
ret.push_back (sptr (Goals::GatherArmy(evaluateDanger(t,h)*SAFE_ATTACK_CONSTANT).sethero(h).setisAbstract(true)));
}
}
}
}
@ -917,7 +930,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
for (auto obj : objs)
{ //find safe dwelling
auto pos = obj->visitablePos();
if (shouldVisit (h, obj) && isSafeToVisit(h, pos) && ai->isAccessibleForHero(pos, h))
if (ai->isGoodForVisit(obj, h))
ret.push_back (sptr (Goals::VisitTile(pos).sethero(h)));
}
}

View File

@ -2770,7 +2770,14 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
}
int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
/*
this functions returns one target tile or invalid tile. We will use it to poll possible destinations
For ship construction etc, another function (goal?) is needed
*/
{
int3 ret(-1,-1,-1);
cb->setSelection(h.h);
int sourceSector = retreiveTile(h->visitablePos()),
destinationSector = retreiveTile(dst);
@ -2806,7 +2813,9 @@ int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
write("test.txt");
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));
return ret;
//throw cannotFulfillGoalException(boost::str(boost::format("Cannot find connection between sectors %d and %d") % src->id % dst->id));
}
std::vector<const Sector*> toTraverse;
@ -2861,7 +2870,9 @@ int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
if(!shipyards.size())
{
//TODO consider possibility of building shipyard in a town
throw cannotFulfillGoalException("There is no known shipyard!");
return ret;
//throw cannotFulfillGoalException("There is no known shipyard!");
}
//we have only shipyards that possibly can build ships onto the appropriate EP
@ -2878,13 +2889,15 @@ int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
if(cb->getResourceAmount().canAfford(shipCost))
{
int3 ret = s->bestLocation();
cb->buildBoat(s);
cb->buildBoat(s); //TODO: move actions elsewhere
return ret;
}
else
{
//TODO gather res
throw cannotFulfillGoalException("Not enough resources to build a boat");
return ret;
//throw cannotFulfillGoalException("Not enough resources to build a boat");
}
}
else
@ -2898,17 +2911,20 @@ int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
{
//TODO
//disembark
return ret;
}
else
{
//TODO
//transition between two land/water sectors. Monolith? Whirlpool? ...
throw cannotFulfillGoalException("Land-land and water-water inter-sector transitions are not implemented!");
return ret;
//throw cannotFulfillGoalException("Land-land and water-water inter-sector transitions are not implemented!");
}
}
else
{
throw cannotFulfillGoalException("Inter-sector route detection failed: not connected sectors?");
return ret;
//throw cannotFulfillGoalException("Inter-sector route detection failed: not connected sectors?");
}
}
else
@ -2917,6 +2933,12 @@ int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
int3 curtile = dst;
while(curtile != h->visitablePos())
{
auto topObj = backOrNull(cb->getVisitableObjs(curtile));
if (topObj && topObj->ID == Obj::HERO && topObj != h.h)
{
logAi->warnStream() << ("Another allied hero stands in our way");
return ret;
}
if(cb->getPathInfo(curtile)->reachable())
{
return curtile;
@ -2930,13 +2952,17 @@ int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
curtile = i->second;
}
else
throw cannotFulfillGoalException("Unreachable tile in sector? Should not happen!");
{
return ret;
//throw cannotFulfillGoalException("Unreachable tile in sector? Should not happen!");
}
}
}
}
throw cannotFulfillGoalException("Impossible happened.");
//FIXME: find out why this line is reached
logAi->errorStream() << ("Impossible happened at SectorMap::firstTileToGet");
return ret;
}
void SectorMap::makeParentBFS(crint3 source)

View File

@ -84,6 +84,10 @@ public:
" " + boost::lexical_cast<std::string>(y) +
" " + boost::lexical_cast<std::string>(z) + ")";
}
inline bool valid() const
{
return z >= 0; //minimal condition that needs to be fulfilled for tiles in the map
}
template <typename Handler> void serialize(Handler &h, const int version)
{
h & x & y & z;