1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-04-23 12:08:45 +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); 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 int AIhelper::selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const
{ {
return heroManager->selectBestSkill(hero, skills); 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> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const override;
std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) 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<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; const std::map<HeroPtr, HeroRole> & getHeroRoles() const override;
HeroRole getHeroRole(const HeroPtr & hero) const override; HeroRole getHeroRole(const HeroPtr & hero) const override;

View File

@ -110,23 +110,31 @@ bool ArmyManager::canGetArmy(const CArmedInstance * target, const CArmedInstance
ui64 ArmyManager::howManyReinforcementsCanBuy(const CCreatureSet * h, const CGDwelling * t) const ui64 ArmyManager::howManyReinforcementsCanBuy(const CCreatureSet * h, const CGDwelling * t) const
{ {
ui64 aivalue = 0; ui64 aivalue = 0;
TResources availableRes = cb->getResourceAmount(); auto army = getArmyAvailableToBuy(h, t);
int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount();
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) if(!ci.count || ci.creID == -1)
continue; continue;
vstd::amin(ci.count, availableRes / ci.cre->cost); //max count we can afford SlotID dst = hero->getSlotFor(ci.creID);
if(!hero->hasStackAtSlot(dst)) //need another new slot for this stack
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 if(!freeHeroSlots) //no more place for stacks
continue; continue;
@ -134,13 +142,17 @@ ui64 ArmyManager::howManyReinforcementsCanBuy(const CCreatureSet * h, const CGDw
freeHeroSlots--; //new slot will be occupied freeHeroSlots--; //new slot will be occupied
} }
//we found matching occupied or free slot vstd::amin(ci.count, availableRes / ci.cre->cost); //max count we can afford
aivalue += ci.count * ci.cre->AIValue;
if(!ci.count)
continue;
ci.level = i; //this is important for Dungeon Summoning Portal
creaturesInDwellings.push_back(ci);
availableRes -= ci.cre->cost * ci.count; availableRes -= ci.cre->cost * ci.count;
} }
}
return aivalue; return creaturesInDwellings;
} }
ui64 ArmyManager::howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const 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> 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>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const = 0;
virtual std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) 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 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> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const override;
std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) 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<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,15 +103,16 @@ void ExecuteHeroChain::accept(VCAI * ai)
if(!targetNode->accessible || targetNode->turns != 0) if(!targetNode->accessible || targetNode->turns != 0)
{ {
logAi->error( 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, hero.name,
node.coord.toString(), node.coord.toString());
hero->visitablePos().toString());
return; return;
} }
} }
if(hero->movement)
{
try try
{ {
Goals::VisitTile(node.coord).sethero(hero).accept(ai); Goals::VisitTile(node.coord).sethero(hero).accept(ai);
@ -135,6 +136,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
throw; throw;
} }
} }
}
if(node.turns == 0) if(node.turns == 0)
{ {

View File

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

View File

@ -1068,7 +1068,7 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
{ {
makePossibleUpgrades(h.get()); makePossibleUpgrades(h.get());
if(!h->visitedTown->garrisonHero) if(!nullkiller || !h->visitedTown->garrisonHero || !nullkiller->isHeroLocked(h->visitedTown->garrisonHero))
moveCreaturesToHero(h->visitedTown); moveCreaturesToHero(h->visitedTown);
townVisitsThisWeek[h].insert(h->visitedTown); townVisitsThisWeek[h].insert(h->visitedTown);
@ -2192,47 +2192,34 @@ void VCAI::tryRealize(Goals::BuyArmy & g)
makePossibleUpgrades(t); makePossibleUpgrades(t);
while (valueBought < g.value) auto armyToBuy = ah->getArmyAvailableToBuy(t->getUpperArmy(), t);
if(armyToBuy.empty())
{ {
auto res = ah->allResources(); throw cannotFulfillGoalException("No creatures to buy.");
std::vector<creInfo> creaturesInDwellings;
for (int i = t->creatures.size() - 1; i >= 0; i--)
{
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);
} }
if (creaturesInDwellings.empty()) for (int i = 0; valueBought < g.value && i < armyToBuy.size(); i++)
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 auto res = ah->allResources();
int value1 = lhs.cre->AIValue * lhs.count, auto & ci = armyToBuy[i];
value2 = rhs.cre->AIValue * rhs.count;
return value1 < value2; if(g.objid != -1 && ci.creID != g.objid)
}); continue;
vstd::amin(ci.count, res / ci.cre->cost);
if(ci.count)
{
cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level); cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level);
valueBought += ci.count * ci.cre->AIValue; valueBought += ci.count * ci.cre->AIValue;
} }
}
if(!valueBought)
{
throw cannotFulfillGoalException("No creatures to buy.");
}
if(t->visitingHero) if(t->visitingHero)
{ {