1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

Nullkiller AI: new prioritization engine stabilization

This commit is contained in:
Andrii Danylchenko 2021-05-16 14:13:48 +03:00 committed by Andrii Danylchenko
parent b261734905
commit 6bebb766a6
7 changed files with 84 additions and 80 deletions

View File

@ -192,6 +192,11 @@ std::vector<SlotInfo> AIhelper::getSortedSlots(const CCreatureSet * target, cons
return armyManager->getSortedSlots(target, source);
}
std::vector<creInfo> AIhelper::getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const
{
return armyManager->getArmyAvailableToBuy(hero, dwelling);
}
int AIhelper::selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const
{
return heroManager->selectBestSkill(hero, skills);

View File

@ -79,6 +79,7 @@ public:
std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const override;
std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const override;
std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override;
std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const override;
const std::map<HeroPtr, HeroRole> & getHeroRoles() const override;
HeroRole getHeroRole(const HeroPtr & hero) const override;

View File

@ -110,37 +110,49 @@ bool ArmyManager::canGetArmy(const CArmedInstance * target, const CArmedInstance
ui64 ArmyManager::howManyReinforcementsCanBuy(const CCreatureSet * h, const CGDwelling * t) const
{
ui64 aivalue = 0;
TResources availableRes = cb->getResourceAmount();
int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount();
auto army = getArmyAvailableToBuy(h, t);
for(auto const dc : t->creatures)
for(const creInfo & ci : army)
{
creInfo ci = infoFromDC(dc);
aivalue += ci.count * ci.cre->AIValue;
}
return aivalue;
}
std::vector<creInfo> ArmyManager::getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const
{
auto availableRes = cb->getResourceAmount();
std::vector<creInfo> creaturesInDwellings;
int freeHeroSlots = GameConstants::ARMY_SIZE - hero->stacksCount();
for(int i = dwelling->creatures.size() - 1; i >= 0; i--)
{
auto ci = infoFromDC(dwelling->creatures[i]);
if(!ci.count || ci.creID == -1)
continue;
SlotID dst = hero->getSlotFor(ci.creID);
if(!hero->hasStackAtSlot(dst)) //need another new slot for this stack
{
if(!freeHeroSlots) //no more place for stacks
continue;
else
freeHeroSlots--; //new slot will be occupied
}
vstd::amin(ci.count, availableRes / ci.cre->cost); //max count we can afford
if(ci.count && ci.creID != -1) //valid creature at this level
{
//can be merged with another stack?
SlotID dst = h->getSlotFor(ci.creID);
if(!h->hasStackAtSlot(dst)) //need another new slot for this stack
{
if(!freeHeroSlots) //no more place for stacks
continue;
else
freeHeroSlots--; //new slot will be occupied
}
if(!ci.count)
continue;
//we found matching occupied or free slot
aivalue += ci.count * ci.cre->AIValue;
availableRes -= ci.cre->cost * ci.count;
}
ci.level = i; //this is important for Dungeon Summoning Portal
creaturesInDwellings.push_back(ci);
availableRes -= ci.cre->cost * ci.count;
}
return aivalue;
return creaturesInDwellings;
}
ui64 ArmyManager::howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const

View File

@ -36,6 +36,7 @@ public:
virtual std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const = 0;
virtual std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const = 0;
virtual std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const = 0;
virtual std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const = 0;
};
class DLL_EXPORT ArmyManager : public IArmyManager
@ -54,4 +55,5 @@ public:
std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const override;
std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const override;
std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override;
std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const override;
};

View File

@ -103,36 +103,38 @@ void ExecuteHeroChain::accept(VCAI * ai)
if(!targetNode->accessible || targetNode->turns != 0)
{
logAi->error(
"Enable to complete chain. Expected hero %s to arive to %s but he in 0 turns but he can not do this",
"Enable to complete chain. Expected hero %s to arive to %s in 0 turns but he can not do this",
hero.name,
node.coord.toString(),
hero->visitablePos().toString());
node.coord.toString());
return;
}
}
try
if(hero->movement)
{
Goals::VisitTile(node.coord).sethero(hero).accept(ai);
}
catch(cannotFulfillGoalException)
{
if(hero->movement > 0)
try
{
CGPath path;
bool isOk = cb->getPathsInfo(hero.get())->getPath(path, node.coord);
if(isOk && path.nodes.back().turns > 0)
{
logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero.name, hero->movement, node.coord.toString());
ai->nullkiller->lockHero(hero.get());
return;
}
Goals::VisitTile(node.coord).sethero(hero).accept(ai);
}
catch(cannotFulfillGoalException)
{
if(hero->movement > 0)
{
CGPath path;
bool isOk = cb->getPathsInfo(hero.get())->getPath(path, node.coord);
throw;
if(isOk && path.nodes.back().turns > 0)
{
logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero.name, hero->movement, node.coord.toString());
ai->nullkiller->lockHero(hero.get());
return;
}
}
throw;
}
}
}

View File

@ -201,11 +201,6 @@ void AINodeStorage::commit(
int movementLeft,
float cost) const
{
if(destination->actor->chainMask == 195 && turn == 0)
{
throw std::exception();
}
destination->action = action;
destination->cost = cost;
destination->moveRemains = movementLeft;

View File

@ -1068,7 +1068,7 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
{
makePossibleUpgrades(h.get());
if(!h->visitedTown->garrisonHero)
if(!nullkiller || !h->visitedTown->garrisonHero || !nullkiller->isHeroLocked(h->visitedTown->garrisonHero))
moveCreaturesToHero(h->visitedTown);
townVisitsThisWeek[h].insert(h->visitedTown);
@ -2192,46 +2192,33 @@ void VCAI::tryRealize(Goals::BuyArmy & g)
makePossibleUpgrades(t);
while (valueBought < g.value)
auto armyToBuy = ah->getArmyAvailableToBuy(t->getUpperArmy(), t);
if(armyToBuy.empty())
{
throw cannotFulfillGoalException("No creatures to buy.");
}
for (int i = 0; valueBought < g.value && i < armyToBuy.size(); i++)
{
auto res = ah->allResources();
std::vector<creInfo> creaturesInDwellings;
auto & ci = armyToBuy[i];
for (int i = t->creatures.size() - 1; i >= 0; i--)
if(g.objid != -1 && ci.creID != g.objid)
continue;
vstd::amin(ci.count, res / ci.cre->cost);
if(ci.count)
{
auto ci = infoFromDC(t->creatures[i]);
if(!ci.count
|| ci.creID == -1
|| (g.objid != -1 && ci.creID != g.objid)
|| t->getUpperArmy()->getSlotFor(ci.creID) == SlotID())
continue;
vstd::amin(ci.count, res / ci.cre->cost); //max count we can afford
if(!ci.count)
continue;
ci.level = i; //this is important for Dungeon Summoning Portal
creaturesInDwellings.push_back(ci);
cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level);
valueBought += ci.count * ci.cre->AIValue;
}
}
if (creaturesInDwellings.empty())
throw cannotFulfillGoalException("Can't buy any more creatures!");
creInfo ci =
*boost::max_element(creaturesInDwellings, [](const creInfo & lhs, const creInfo & rhs)
{
//max value of creatures we can buy with our res
int value1 = lhs.cre->AIValue * lhs.count,
value2 = rhs.cre->AIValue * rhs.count;
return value1 < value2;
});
cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level);
valueBought += ci.count * ci.cre->AIValue;
if(!valueBought)
{
throw cannotFulfillGoalException("No creatures to buy.");
}
if(t->visitingHero)