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:
parent
d01b84b460
commit
d8933b5c36
@ -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)
|
||||
{
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user