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

AI: GATHER_TROOPS improvements and fixes

This commit is contained in:
Andrii Danylchenko 2018-11-11 23:02:05 +02:00
parent 614cde4a55
commit 60c5f584a8
4 changed files with 89 additions and 55 deletions

View File

@ -137,7 +137,7 @@ bool Goals::GatherArmy::operator==(const GatherArmy & other) const
bool Goals::BuyArmy::operator==(const BuyArmy & other) const
{
return town == other.town;
return town == other.town && objid == other.objid;
}
bool Goals::BoostHero::operator==(const BoostHero & other) const
@ -1219,11 +1219,58 @@ bool CollectRes::fulfillsMe(TSubgoal goal)
return false;
}
int GatherTroops::getCreaturesCount(const CArmedInstance * army)
{
int count = 0;
for(auto stack : army->Slots())
{
if(objid == stack.second->getCreatureID().num)
{
count += stack.second->count;
}
}
return count;
}
TSubgoal GatherTroops::whatToDoToAchieve()
{
std::vector<const CGDwelling *> dwellings;
auto heroes = cb->getHeroesInfo(true);
for(auto hero : heroes)
{
if(getCreaturesCount(hero) >= this->value)
{
logAi->trace("Completing GATHER_TROOPS by hero %s", hero->name);
throw goalFulfilledException(sptr(*this));
}
}
TGoalVec solutions = getAllPossibleSubgoals();
if(solutions.empty())
return sptr(Goals::Explore());
return fh->chooseSolution(solutions);
}
TGoalVec GatherTroops::getAllPossibleSubgoals()
{
TGoalVec solutions;
for(const CGTownInstance * t : cb->getTownsInfo())
{
int count = getCreaturesCount(t->getUpperArmy());
if(count >= this->value)
{
vstd::concatenate(solutions, ai->ah->howToVisitObj(t));
continue;
}
auto creature = VLC->creh->creatures[objid];
if(t->subID == creature->faction) //TODO: how to force AI to build unupgraded creatures? :O
{
@ -1238,7 +1285,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
BuildingID bid(BuildingID::DWELL_FIRST + creature->level - 1 + upgradeNumber * GameConstants::CREATURES_PER_TOWN);
if(t->hasBuilt(bid)) //this assumes only creatures with dwellings are assigned to faction
{
dwellings.push_back(t);
solutions.push_back(sptr(Goals::BuyArmy(t, creature->AIValue * this->value).setobjid(objid)));
}
/*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm
{
@ -1248,10 +1295,11 @@ TSubgoal GatherTroops::whatToDoToAchieve()
}
for(auto obj : ai->visitableObjs)
{
if(obj->ID != Obj::CREATURE_GENERATOR1) //TODO: what with other creature generators?
auto d = dynamic_cast<const CGDwelling *>(obj);
if(!d || obj->ID == Obj::TOWN)
continue;
auto d = dynamic_cast<const CGDwelling *>(obj);
for(auto creature : d->creatures)
{
if(creature.first) //there are more than 0 creatures avaliabe
@ -1259,49 +1307,13 @@ TSubgoal GatherTroops::whatToDoToAchieve()
for(auto type : creature.second)
{
if(type == objid && ai->ah->freeResources().canAfford(VLC->creh->creatures[type]->cost))
dwellings.push_back(d);
vstd::concatenate(solutions, ai->ah->howToVisitObj(obj));
}
}
}
}
if(dwellings.size())
{
typedef std::map<const CGHeroInstance *, const CGDwelling *> TDwellMap;
// sorted helper
auto comparator = [](const TDwellMap::value_type & a, const TDwellMap::value_type & b) -> bool
{
const CGPathNode * ln = ai->myCb->getPathsInfo(a.first)->getPathInfo(a.second->visitablePos());
const CGPathNode * rn = ai->myCb->getPathsInfo(b.first)->getPathInfo(b.second->visitablePos());
if(ln->turns != rn->turns)
return ln->turns < rn->turns;
return (ln->moveRemains > rn->moveRemains);
};
// for all owned heroes generate map <hero -> nearest dwelling>
TDwellMap nearestDwellings;
for(const CGHeroInstance * hero : cb->getHeroesInfo(true))
{
nearestDwellings[hero] = *boost::range::min_element(dwellings, CDistanceSorter(hero));
}
if(nearestDwellings.size())
{
// find hero who is nearest to a dwelling
const CGDwelling * nearest = boost::range::min_element(nearestDwellings, comparator)->second;
if(!nearest)
throw cannotFulfillGoalException("Cannot find nearest dwelling!");
return sptr(Goals::VisitObj(nearest->id.getNum()));
}
else
return sptr(Goals::Explore());
}
else
{
return sptr(Goals::Explore());
}
return solutions;
//TODO: exchange troops between heroes
}

View File

@ -535,13 +535,13 @@ public:
value = val;
priority = 2;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override;
bool fulfillsMe(TSubgoal goal) override;
virtual bool operator==(const GatherTroops & other) const override;
private:
int getCreaturesCount(const CArmedInstance * army);
};
class DLL_EXPORT VisitObj : public CGoal<VisitObj> //this goal was previously known as GetObj

View File

@ -144,6 +144,9 @@ Goals::TGoalVec PathfindingManager::findPath(
: clearWayTo(hero, firstTileToGet);
}
if(solution->invalid())
continue;
if(solution->evaluationContext.danger < danger)
solution->evaluationContext.danger = danger;
@ -212,6 +215,7 @@ Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet
//ret.push_back(ai->questToGoal());
//however, visiting obj for firts time will give us quest
//do not access quets guard if we can't complete the quest
logAi->trace("Can not visit this quest guard! Not ready!");
return sptr(Goals::Invalid());
}
}

View File

@ -2193,16 +2193,18 @@ void VCAI::tryRealize(Goals::BuyArmy & g)
{
auto res = ah->allResources();
std::vector<creInfo> creaturesInDwellings;
for (int i = 0; i < t->creatures.size(); i++)
{
auto ci = infoFromDC(t->creatures[i]);
if(!ci.count || ci.creID == -1 || (g.objid != -1 && ci.creID != g.objid))
continue;
ci.level = i; //this is important for Dungeon Summoning Portal
creaturesInDwellings.push_back(ci);
}
vstd::erase_if(creaturesInDwellings, [](const creInfo & ci) -> bool
{
return !ci.count || ci.creID == -1;
});
if (creaturesInDwellings.empty())
throw cannotFulfillGoalException("Can't buy any more creatures!");
@ -2433,6 +2435,8 @@ Goals::TSubgoal VCAI::decomposeGoal(Goals::TSubgoal ultimateGoal)
Goals::TSubgoal VCAI::questToGoal(const QuestInfo & q)
{
Goals::TSubgoal result = sptr(Goals::Invalid());
if (q.quest->missionType && q.quest->progress != CQuest::COMPLETE)
{
MetaString ms;
@ -2476,12 +2480,18 @@ Goals::TSubgoal VCAI::questToGoal(const QuestInfo & q)
{
if (q.quest->checkQuest(hero)) //very bad info - stacks can be split between multiple heroes :(
{
return sptr(Goals::VisitObj(q.obj->id.getNum()).sethero(hero));
result = sptr(Goals::VisitObj(q.obj->id.getNum()).sethero(hero));
break;
}
}
for (auto creature : q.quest->m6creatures)
if(result->invalid())
{
return sptr(Goals::GatherTroops(creature.type->idNumber, creature.count));
for(auto creature : q.quest->m6creatures)
{
result = sptr(Goals::GatherTroops(creature.type->idNumber, creature.count));
break;
}
}
//TODO: exchange armies... oh my
//BNLOG ("Don't know how to recruit %d of %s\n", (int)(creature.count) % creature.type->namePl);
@ -2576,7 +2586,15 @@ Goals::TSubgoal VCAI::questToGoal(const QuestInfo & q)
}
} //end of switch
}
return sptr(Goals::Invalid());
logAi->trace(
"Returning %s, tile: %s, objid: %d, hero: %s",
result->name(),
result->tile.toString(),
result->objid,
result->hero.validAndSet() ? result->hero->name : "not specified");
return result;
}
void VCAI::performTypicalActions()