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:
parent
b261734905
commit
6bebb766a6
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user