mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
NKAI: playing around with defence
This commit is contained in:
parent
b19ac01bf9
commit
6ba74f02bc
@ -33,6 +33,45 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void ArmyUpgradeInfo::addArmyToBuy(std::vector<SlotInfo> army)
|
||||
{
|
||||
for(auto slot : army)
|
||||
{
|
||||
resultingArmy.push_back(slot);
|
||||
|
||||
upgradeValue += slot.power;
|
||||
upgradeCost += slot.creature->getFullRecruitCost() * slot.count;
|
||||
}
|
||||
}
|
||||
|
||||
void ArmyUpgradeInfo::addArmyToGet(std::vector<SlotInfo> army)
|
||||
{
|
||||
for(auto slot : army)
|
||||
{
|
||||
resultingArmy.push_back(slot);
|
||||
|
||||
upgradeValue += slot.power;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SlotInfo> ArmyManager::toSlotInfo(std::vector<creInfo> army) const
|
||||
{
|
||||
std::vector<SlotInfo> result;
|
||||
|
||||
for(auto i : army)
|
||||
{
|
||||
SlotInfo slot;
|
||||
|
||||
slot.creature = VLC->creh->objects[i.cre->getId()];
|
||||
slot.count = i.count;
|
||||
slot.power = evaluateStackPower(i.cre, i.count);
|
||||
|
||||
result.push_back(slot);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t ArmyManager::howManyReinforcementsCanGet(const CGHeroInstance * hero, const CCreatureSet * source) const
|
||||
{
|
||||
return howManyReinforcementsCanGet(hero, hero, source);
|
||||
@ -136,7 +175,7 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
||||
{
|
||||
if(vstd::contains(allowedFactions, slot.creature->getFaction()))
|
||||
{
|
||||
auto slotID = newArmyInstance.getSlotFor(slot.creature);
|
||||
auto slotID = newArmyInstance.getSlotFor(slot.creature->getId());
|
||||
|
||||
if(slotID.validSlot())
|
||||
{
|
||||
@ -319,7 +358,7 @@ ui64 ArmyManager::howManyReinforcementsCanGet(const IBonusBearer * armyCarrier,
|
||||
return newArmy > oldArmy ? newArmy - oldArmy : 0;
|
||||
}
|
||||
|
||||
uint64_t ArmyManager::evaluateStackPower(const CCreature * creature, int count) const
|
||||
uint64_t ArmyManager::evaluateStackPower(const Creature * creature, int count) const
|
||||
{
|
||||
return creature->getAIValue() * count;
|
||||
}
|
||||
|
@ -34,6 +34,9 @@ struct ArmyUpgradeInfo
|
||||
std::vector<SlotInfo> resultingArmy;
|
||||
uint64_t upgradeValue = 0;
|
||||
TResources upgradeCost;
|
||||
|
||||
void addArmyToBuy(std::vector<SlotInfo> army);
|
||||
void addArmyToGet(std::vector<SlotInfo> army);
|
||||
};
|
||||
|
||||
class DLL_EXPORT IArmyManager //: public: IAbstractManager
|
||||
@ -57,6 +60,7 @@ public:
|
||||
virtual std::vector<SlotInfo> getBestArmy(const IBonusBearer * armyCarrier, 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<SlotInfo> toSlotInfo(std::vector<creInfo> creatures) const = 0;
|
||||
|
||||
virtual std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const = 0;
|
||||
virtual std::vector<creInfo> getArmyAvailableToBuy(
|
||||
@ -65,7 +69,7 @@ public:
|
||||
TResources availableRes,
|
||||
uint8_t turn = 0) const = 0;
|
||||
|
||||
virtual uint64_t evaluateStackPower(const CCreature * creature, int count) const = 0;
|
||||
virtual uint64_t evaluateStackPower(const Creature * creature, int count) const = 0;
|
||||
virtual SlotInfo getTotalCreaturesAvailable(CreatureID creatureID) const = 0;
|
||||
virtual ArmyUpgradeInfo calculateCreaturesUpgrade(
|
||||
const CCreatureSet * army,
|
||||
@ -99,6 +103,7 @@ public:
|
||||
std::vector<SlotInfo> getBestArmy(const IBonusBearer * armyCarrier, 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<SlotInfo> toSlotInfo(std::vector<creInfo> creatures) const override;
|
||||
|
||||
std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const override;
|
||||
std::vector<creInfo> getArmyAvailableToBuy(
|
||||
@ -108,7 +113,7 @@ public:
|
||||
uint8_t turn = 0) const override;
|
||||
|
||||
std::shared_ptr<CCreatureSet> getArmyAvailableToBuyAsCCreatureSet(const CGDwelling * dwelling, TResources availableRes) const override;
|
||||
uint64_t evaluateStackPower(const CCreature * creature, int count) const override;
|
||||
uint64_t evaluateStackPower(const Creature * creature, int count) const override;
|
||||
SlotInfo getTotalCreaturesAvailable(CreatureID creatureID) const override;
|
||||
ArmyUpgradeInfo calculateCreaturesUpgrade(
|
||||
const CCreatureSet * army,
|
||||
|
@ -17,6 +17,11 @@ namespace NKAI
|
||||
|
||||
HitMapInfo HitMapInfo::NoTreat;
|
||||
|
||||
double HitMapInfo::value() const
|
||||
{
|
||||
return danger / std::sqrt(turn / 3.0f + 1);
|
||||
}
|
||||
|
||||
void DangerHitMapAnalyzer::updateHitMap()
|
||||
{
|
||||
if(hitMapUpToDate)
|
||||
@ -29,8 +34,12 @@ void DangerHitMapAnalyzer::updateHitMap()
|
||||
|
||||
auto cb = ai->cb.get();
|
||||
auto mapSize = ai->cb->getMapSize();
|
||||
hitMap.resize(boost::extents[mapSize.x][mapSize.y][mapSize.z]);
|
||||
|
||||
if(hitMap.shape()[0] != mapSize.x || hitMap.shape()[1] != mapSize.y || hitMap.shape()[2] != mapSize.z)
|
||||
hitMap.resize(boost::extents[mapSize.x][mapSize.y][mapSize.z]);
|
||||
|
||||
enemyHeroAccessibleObjects.clear();
|
||||
townTreats.clear();
|
||||
|
||||
std::map<PlayerColor, std::map<const CGHeroInstance *, HeroRole>> heroes;
|
||||
|
||||
@ -67,29 +76,26 @@ void DangerHitMapAnalyzer::updateHitMap()
|
||||
if(path.getFirstBlockedAction())
|
||||
continue;
|
||||
|
||||
auto tileDanger = path.getHeroStrength();
|
||||
auto turn = path.turn();
|
||||
auto & node = hitMap[pos.x][pos.y][pos.z];
|
||||
|
||||
auto newMaxDanger = tileDanger / std::sqrt(turn / 3.0f + 1);
|
||||
auto currentMaxDanger = node.maximumDanger.danger / std::sqrt(node.maximumDanger.turn / 3.0f + 1);
|
||||
HitMapInfo newTreat;
|
||||
|
||||
if(newMaxDanger > currentMaxDanger)
|
||||
newTreat.hero = path.targetHero;
|
||||
newTreat.turn = path.turn();
|
||||
newTreat.danger = path.getHeroStrength();
|
||||
|
||||
if(newTreat.value() > node.maximumDanger.value())
|
||||
{
|
||||
node.maximumDanger.danger = tileDanger;
|
||||
node.maximumDanger.turn = turn;
|
||||
node.maximumDanger.hero = path.targetHero;
|
||||
node.maximumDanger = newTreat;
|
||||
}
|
||||
|
||||
if(turn < node.fastestDanger.turn
|
||||
|| (turn == node.fastestDanger.turn && node.fastestDanger.danger < tileDanger))
|
||||
if(newTreat.turn < node.fastestDanger.turn
|
||||
|| (newTreat.turn == node.fastestDanger.turn && node.fastestDanger.danger < newTreat.danger))
|
||||
{
|
||||
node.fastestDanger.danger = tileDanger;
|
||||
node.fastestDanger.turn = turn;
|
||||
node.fastestDanger.hero = path.targetHero;
|
||||
node.fastestDanger = newTreat;
|
||||
}
|
||||
|
||||
if(turn == 0)
|
||||
if(newTreat.turn == 0)
|
||||
{
|
||||
auto objects = cb->getVisitableObjs(pos, false);
|
||||
|
||||
@ -97,6 +103,26 @@ void DangerHitMapAnalyzer::updateHitMap()
|
||||
{
|
||||
if(cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES)
|
||||
enemyHeroAccessibleObjects[path.targetHero].insert(obj);
|
||||
|
||||
if(obj->ID == Obj::TOWN && obj->getOwner() == ai->playerID)
|
||||
{
|
||||
auto & treats = townTreats[obj->id];
|
||||
auto treat = std::find_if(treats.begin(), treats.end(), [&](const HitMapInfo & i) -> bool
|
||||
{
|
||||
return i.hero.hid == path.targetHero->id;
|
||||
});
|
||||
|
||||
if(treat == treats.end())
|
||||
{
|
||||
treats.emplace_back();
|
||||
treat = std::prev(treats.end(), 1);
|
||||
}
|
||||
|
||||
if(newTreat.value() > treat->value())
|
||||
{
|
||||
*treat = newTreat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -115,7 +141,8 @@ void DangerHitMapAnalyzer::calculateTileOwners()
|
||||
auto cb = ai->cb.get();
|
||||
auto mapSize = ai->cb->getMapSize();
|
||||
|
||||
tileOwners.resize(boost::extents[mapSize.x][mapSize.y][mapSize.z]);
|
||||
if(hitMap.shape()[0] != mapSize.x || hitMap.shape()[1] != mapSize.y || hitMap.shape()[2] != mapSize.z)
|
||||
hitMap.resize(boost::extents[mapSize.x][mapSize.y][mapSize.z]);
|
||||
|
||||
std::map<const CGHeroInstance *, HeroRole> townHeroes;
|
||||
std::map<const CGHeroInstance *, const CGTownInstance *> heroTownMap;
|
||||
@ -157,6 +184,7 @@ void DangerHitMapAnalyzer::calculateTileOwners()
|
||||
float ourDistance = std::numeric_limits<float>::max();
|
||||
float enemyDistance = std::numeric_limits<float>::max();
|
||||
const CGTownInstance * enemyTown = nullptr;
|
||||
const CGTownInstance * ourTown = nullptr;
|
||||
|
||||
for(AIPath & path : ai->pathfinder->getPathInfo(pos))
|
||||
{
|
||||
@ -167,7 +195,11 @@ void DangerHitMapAnalyzer::calculateTileOwners()
|
||||
|
||||
if(town->getOwner() == ai->playerID)
|
||||
{
|
||||
vstd::amin(ourDistance, path.movementCost());
|
||||
if(ourDistance > path.movementCost())
|
||||
{
|
||||
ourDistance = path.movementCost();
|
||||
ourTown = town;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -181,19 +213,40 @@ void DangerHitMapAnalyzer::calculateTileOwners()
|
||||
|
||||
if(ourDistance == enemyDistance)
|
||||
{
|
||||
tileOwners[pos.x][pos.y][pos.z] = PlayerColor::NEUTRAL;
|
||||
hitMap[pos.x][pos.y][pos.z].closestTown = nullptr;
|
||||
}
|
||||
else if(!enemyTown || ourDistance < enemyDistance)
|
||||
{
|
||||
tileOwners[pos.x][pos.y][pos.z] = ai->playerID;
|
||||
hitMap[pos.x][pos.y][pos.z].closestTown = ourTown;
|
||||
}
|
||||
else
|
||||
{
|
||||
tileOwners[pos.x][pos.y][pos.z] = enemyTown->getOwner();
|
||||
hitMap[pos.x][pos.y][pos.z].closestTown = enemyTown;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const std::vector<HitMapInfo> & DangerHitMapAnalyzer::getTownTreats(const CGTownInstance * town) const
|
||||
{
|
||||
static const std::vector<HitMapInfo> empty = {};
|
||||
|
||||
auto result = townTreats.find(town->id);
|
||||
|
||||
return result == townTreats.end() ? empty : result->second;
|
||||
}
|
||||
|
||||
PlayerColor DangerHitMapAnalyzer::getTileOwner(const int3 & tile) const
|
||||
{
|
||||
auto town = hitMap[tile.x][tile.y][tile.z].closestTown;
|
||||
|
||||
return town ? town->getOwner() : PlayerColor::NEUTRAL;
|
||||
}
|
||||
|
||||
const CGTownInstance * DangerHitMapAnalyzer::getClosestTown(const int3 & tile) const
|
||||
{
|
||||
return hitMap[tile.x][tile.y][tile.z].closestTown;
|
||||
}
|
||||
|
||||
uint64_t DangerHitMapAnalyzer::enemyCanKillOurHeroesAlongThePath(const AIPath & path) const
|
||||
{
|
||||
int3 tile = path.targetTile();
|
||||
|
@ -35,6 +35,8 @@ struct HitMapInfo
|
||||
turn = 255;
|
||||
hero = HeroPtr();
|
||||
}
|
||||
|
||||
double value() const;
|
||||
};
|
||||
|
||||
struct HitMapNode
|
||||
@ -42,6 +44,8 @@ struct HitMapNode
|
||||
HitMapInfo maximumDanger;
|
||||
HitMapInfo fastestDanger;
|
||||
|
||||
const CGTownInstance * closestTown = nullptr;
|
||||
|
||||
HitMapNode() = default;
|
||||
|
||||
void reset()
|
||||
@ -51,15 +55,19 @@ struct HitMapNode
|
||||
}
|
||||
};
|
||||
|
||||
struct TileOwner
|
||||
{
|
||||
};
|
||||
|
||||
class DangerHitMapAnalyzer
|
||||
{
|
||||
private:
|
||||
boost::multi_array<HitMapNode, 3> hitMap;
|
||||
boost::multi_array<PlayerColor, 3> tileOwners;
|
||||
std::map<const CGHeroInstance *, std::set<const CGObjectInstance *>> enemyHeroAccessibleObjects;
|
||||
bool hitMapUpToDate = false;
|
||||
bool tileOwnersUpToDate = false;
|
||||
const Nullkiller * ai;
|
||||
std::map<ObjectInstanceID, std::vector<HitMapInfo>> townTreats;
|
||||
|
||||
public:
|
||||
DangerHitMapAnalyzer(const Nullkiller * ai) :ai(ai) {}
|
||||
@ -72,6 +80,9 @@ public:
|
||||
const std::set<const CGObjectInstance *> & getOneTurnAccessibleObjects(const CGHeroInstance * enemy) const;
|
||||
void reset();
|
||||
void resetTileOwners() { tileOwnersUpToDate = false; }
|
||||
PlayerColor getTileOwner(const int3 & tile) const;
|
||||
const CGTownInstance * getClosestTown(const int3 & tile) const;
|
||||
const std::vector<HitMapInfo> & getTownTreats(const CGTownInstance * town) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -54,7 +54,9 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
logAi->trace("Evaluating defence for %s", town->getNameTranslated());
|
||||
|
||||
auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
|
||||
auto treats = { treatNode.maximumDanger, treatNode.fastestDanger };
|
||||
std::vector<HitMapInfo> treats = ai->nullkiller->dangerHitMap->getTownTreats(town);
|
||||
|
||||
treats.push_back(treatNode.fastestDanger); // no guarantee that fastest danger will be there
|
||||
|
||||
int dayOfWeek = cb->getDate(Date::DAY_OF_WEEK);
|
||||
|
||||
@ -131,14 +133,24 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
|
||||
if(treat.hero.validAndSet()
|
||||
&& treat.turn <= 1
|
||||
&& (treat.danger == treatNode.maximumDanger.danger || treat.turn < treatNode.maximumDanger.turn)
|
||||
&& isSafeToVisit(path.targetHero, path.heroArmy, treat.danger))
|
||||
&& (treat.danger == treatNode.maximumDanger.danger || treat.turn < treatNode.maximumDanger.turn))
|
||||
{
|
||||
Composition composition;
|
||||
auto heroCapturingPaths = ai->nullkiller->pathfinder->getPathInfo(treat.hero->visitablePos());
|
||||
auto goals = CaptureObjectsBehavior::getVisitGoals(heroCapturingPaths, treat.hero.get());
|
||||
|
||||
composition.addNext(DefendTown(town, treat, path)).addNext(CaptureObject(treat.hero.get()));
|
||||
for(int i = 0; i < heroCapturingPaths.size(); i++)
|
||||
{
|
||||
AIPath & path = heroCapturingPaths[i];
|
||||
TSubgoal goal = goals[i];
|
||||
|
||||
tasks.push_back(Goals::sptr(composition));
|
||||
if(!goal || goal->invalid() || !goal->isElementar()) continue;
|
||||
|
||||
Composition composition;
|
||||
|
||||
composition.addNext(DefendTown(town, treat, path, true)).addNext(goal);
|
||||
|
||||
tasks.push_back(Goals::sptr(composition));
|
||||
}
|
||||
}
|
||||
|
||||
bool treatIsWeak = path.getHeroStrength() / (float)treat.danger > TREAT_IGNORE_RATIO;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "../Goals/ExecuteHeroChain.h"
|
||||
#include "../Goals/Composition.h"
|
||||
#include "../Goals/RecruitHero.h"
|
||||
#include "../Markers/HeroExchange.h"
|
||||
#include "../Markers/ArmyUpgrade.h"
|
||||
#include "GatherArmyBehavior.h"
|
||||
@ -240,12 +241,22 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
||||
logAi->trace("Found %d paths", paths.size());
|
||||
#endif
|
||||
|
||||
bool hasMainAround = false;
|
||||
|
||||
for(const AIPath & path : paths)
|
||||
{
|
||||
auto heroRole = ai->nullkiller->heroManager->getHeroRole(path.targetHero);
|
||||
|
||||
if(heroRole == HeroRole::MAIN && path.turn() < SCOUT_TURN_DISTANCE_LIMIT)
|
||||
hasMainAround = true;
|
||||
}
|
||||
|
||||
for(const AIPath & path : paths)
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Path found %s, %s, %lld", path.toString(), path.targetHero->getObjectName(), path.heroArmy->getArmyStrength());
|
||||
#endif
|
||||
if(upgrader->visitingHero && upgrader->visitingHero.get() != path.targetHero)
|
||||
if(upgrader->visitingHero && (upgrader->visitingHero.get() != path.targetHero || path.exchangeCount == 1))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Ignore path. Town has visiting hero.");
|
||||
@ -283,25 +294,58 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
||||
|
||||
auto upgrade = ai->nullkiller->armyManager->calculateCreaturesUpgrade(path.heroArmy, upgrader, availableResources);
|
||||
|
||||
if(!upgrader->garrisonHero && ai->nullkiller->heroManager->getHeroRole(path.targetHero) == HeroRole::MAIN)
|
||||
if(!upgrader->garrisonHero
|
||||
&& (
|
||||
hasMainAround
|
||||
|| ai->nullkiller->heroManager->getHeroRole(path.targetHero) == HeroRole::MAIN))
|
||||
{
|
||||
upgrade.upgradeValue +=
|
||||
ai->nullkiller->armyManager->howManyReinforcementsCanGet(
|
||||
ArmyUpgradeInfo armyToGetOrBuy;
|
||||
|
||||
armyToGetOrBuy.addArmyToGet(
|
||||
ai->nullkiller->armyManager->getBestArmy(
|
||||
path.targetHero,
|
||||
path.heroArmy,
|
||||
upgrader->getUpperArmy());
|
||||
upgrader->getUpperArmy()));
|
||||
|
||||
upgrade.upgradeValue +=
|
||||
ai->nullkiller->armyManager->howManyReinforcementsCanBuy(
|
||||
path.heroArmy,
|
||||
upgrader,
|
||||
ai->nullkiller->getFreeResources(),
|
||||
path.turn());
|
||||
armyToGetOrBuy.addArmyToBuy(
|
||||
ai->nullkiller->armyManager->toSlotInfo(
|
||||
ai->nullkiller->armyManager->getArmyAvailableToBuy(
|
||||
path.heroArmy,
|
||||
upgrader,
|
||||
ai->nullkiller->getFreeResources(),
|
||||
path.turn())));
|
||||
|
||||
upgrade.upgradeValue += armyToGetOrBuy.upgradeValue;
|
||||
upgrade.upgradeCost += armyToGetOrBuy.upgradeCost;
|
||||
vstd::concatenate(upgrade.resultingArmy, armyToGetOrBuy.resultingArmy);
|
||||
|
||||
auto getOrBuyArmyValue = (float)armyToGetOrBuy.upgradeValue / path.getHeroStrength();
|
||||
|
||||
if(!upgrade.upgradeValue
|
||||
&& armyToGetOrBuy.upgradeValue > 20000
|
||||
&& ai->nullkiller->heroManager->canRecruitHero(town)
|
||||
&& path.turn() < SCOUT_TURN_DISTANCE_LIMIT)
|
||||
{
|
||||
for(auto hero : cb->getAvailableHeroes(town))
|
||||
{
|
||||
auto scoutReinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(hero, town)
|
||||
+ ai->nullkiller->armyManager->howManyReinforcementsCanGet(hero, town);
|
||||
|
||||
if(scoutReinforcement >= armyToGetOrBuy.upgradeValue
|
||||
&& ai->nullkiller->getFreeGold() >20000
|
||||
&& ai->nullkiller->buildAnalyzer->getGoldPreasure() < MAX_GOLD_PEASURE)
|
||||
{
|
||||
Composition recruitHero;
|
||||
|
||||
recruitHero.addNext(ArmyUpgrade(path.targetHero, town, armyToGetOrBuy)).addNext(RecruitHero(town, hero));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto armyValue = (float)upgrade.upgradeValue / path.getHeroStrength();
|
||||
|
||||
if((armyValue < 0.1f && armyValue < 20000) || upgrade.upgradeValue < 300) // avoid small upgrades
|
||||
if((armyValue < 0.1f && upgrade.upgradeValue < 20000) || upgrade.upgradeValue < 300) // avoid small upgrades
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Ignore path. Army value is too small (%f)", armyValue);
|
||||
|
@ -66,6 +66,27 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
|
||||
}
|
||||
}
|
||||
|
||||
int treasureSourcesCount = 0;
|
||||
|
||||
for(auto obj : ai->nullkiller->objectClusterizer->getNearbyObjects())
|
||||
{
|
||||
if((obj->ID == Obj::RESOURCE && obj->subID == GameResID(EGameResID::GOLD))
|
||||
|| obj->ID == Obj::TREASURE_CHEST
|
||||
|| obj->ID == Obj::CAMPFIRE
|
||||
|| obj->ID == Obj::WATER_WHEEL
|
||||
|| obj->ID ==Obj::ARTIFACT)
|
||||
{
|
||||
auto tile = obj->visitablePos();
|
||||
auto closestTown = ai->nullkiller->dangerHitMap->getClosestTown(tile);
|
||||
|
||||
if(town == closestTown)
|
||||
treasureSourcesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if(treasureSourcesCount < 10)
|
||||
continue;
|
||||
|
||||
if(cb->getHeroesInfo().size() < cb->getTownsInfo().size() + 1
|
||||
|| (ai->nullkiller->getFreeResources()[EGameResID::GOLD] > 10000
|
||||
&& ai->nullkiller->buildAnalyzer->getGoldPreasure() < MAX_GOLD_PEASURE))
|
||||
|
@ -282,18 +282,6 @@ uint64_t RewardEvaluator::getArmyReward(
|
||||
|
||||
switch(target->ID)
|
||||
{
|
||||
case Obj::TOWN:
|
||||
{
|
||||
auto town = dynamic_cast<const CGTownInstance *>(target);
|
||||
auto fortLevel = town->fortLevel();
|
||||
auto booster = isAnotherAi(town, *ai->cb) ? 1 : 2;
|
||||
|
||||
if(fortLevel < CGTownInstance::CITADEL)
|
||||
return town->hasFort() ? booster * 500 : 0;
|
||||
else
|
||||
return booster * (fortLevel == CGTownInstance::CASTLE ? 5000 : 2000);
|
||||
}
|
||||
|
||||
case Obj::HILL_FORT:
|
||||
return ai->armyManager->calculateCreaturesUpgrade(army, target, ai->cb->getResourceAmount()).upgradeValue;
|
||||
case Obj::CREATURE_BANK:
|
||||
@ -440,6 +428,22 @@ float RewardEvaluator::getTotalResourceRequirementStrength(int resType) const
|
||||
return std::min(ratio, 1.0f);
|
||||
}
|
||||
|
||||
uint64_t RewardEvaluator::townArmyGrowth(const CGTownInstance * town) const
|
||||
{
|
||||
uint64_t result = 0;
|
||||
|
||||
for(auto creatureInfo : town->creatures)
|
||||
{
|
||||
if(creatureInfo.second.empty())
|
||||
continue;
|
||||
|
||||
auto creature = creatureInfo.second.back().toCreature();
|
||||
result += creature->getAIValue() * town->getGrowthInfo(creature->getLevel() - 1).totalGrowth();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) const
|
||||
{
|
||||
if(!target)
|
||||
@ -475,13 +479,22 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons
|
||||
case Obj::TOWN:
|
||||
{
|
||||
if(ai->buildAnalyzer->getDevelopmentInfo().empty())
|
||||
return 1;
|
||||
return 10.0f;
|
||||
|
||||
auto town = dynamic_cast<const CGTownInstance *>(target);
|
||||
auto fortLevel = town->fortLevel();
|
||||
auto booster = isAnotherAi(town, *ai->cb) ? 0.3 : 1;
|
||||
|
||||
if(town->hasCapitol()) return 1;
|
||||
if(town->getOwner() == ai->playerID)
|
||||
{
|
||||
auto armyIncome = townArmyGrowth(town);
|
||||
auto dailyIncome = town->dailyIncome()[EGameResID::GOLD];
|
||||
|
||||
return std::min(1.0f, std::sqrt(armyIncome / 40000.0f)) + std::min(0.3f, dailyIncome / 10000.0f);
|
||||
}
|
||||
|
||||
auto fortLevel = town->fortLevel();
|
||||
auto booster = isAnotherAi(town, *ai->cb) ? 0.3f : 0.7f;
|
||||
|
||||
if(town->hasCapitol()) return booster;
|
||||
|
||||
if(fortLevel < CGTownInstance::CITADEL)
|
||||
return booster * (town->hasFort() ? 0.6 : 0.4);
|
||||
@ -690,23 +703,6 @@ void addTileDanger(EvaluationContext & evaluationContext, const int3 & tile, uin
|
||||
|
||||
class DefendTownEvaluator : public IEvaluationContextBuilder
|
||||
{
|
||||
private:
|
||||
uint64_t townArmyIncome(const CGTownInstance * town) const
|
||||
{
|
||||
uint64_t result = 0;
|
||||
|
||||
for(auto creatureInfo : town->creatures)
|
||||
{
|
||||
if(creatureInfo.second.empty())
|
||||
continue;
|
||||
|
||||
auto creature = creatureInfo.second.back().toCreature();
|
||||
result += creature->getAIValue() * town->getGrowthInfo(creature->getLevel() - 1).totalGrowth();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal task) const override
|
||||
{
|
||||
@ -717,10 +713,7 @@ public:
|
||||
const CGTownInstance * town = defendTown.town;
|
||||
auto & treat = defendTown.getTreat();
|
||||
|
||||
auto armyIncome = townArmyIncome(town);
|
||||
auto dailyIncome = town->dailyIncome()[EGameResID::GOLD];
|
||||
|
||||
auto strategicalValue = std::sqrt(armyIncome / 60000.0f) + dailyIncome / 10000.0f;
|
||||
auto strategicalValue = evaluationContext.evaluator.getStrategicalValue(town);
|
||||
|
||||
if(evaluationContext.evaluator.ai->buildAnalyzer->getDevelopmentInfo().size() == 1)
|
||||
vstd::amax(evaluationContext.strategicalValue, 10.0);
|
||||
@ -732,9 +725,20 @@ public:
|
||||
|
||||
multiplier /= 1.0f + treat.turn / 5.0f;
|
||||
|
||||
evaluationContext.armyGrowth += armyIncome * multiplier;
|
||||
if(defendTown.getTurn() > 0 && defendTown.isContrAttack())
|
||||
{
|
||||
auto ourSpeed = defendTown.hero->maxMovePoints(true);
|
||||
auto enemySpeed = treat.hero->maxMovePoints(true);
|
||||
|
||||
if(enemySpeed > ourSpeed) multiplier *= 0.7f;
|
||||
}
|
||||
|
||||
auto dailyIncome = town->dailyIncome()[EGameResID::GOLD];
|
||||
auto armyGrowth = evaluationContext.evaluator.townArmyGrowth(town);
|
||||
|
||||
evaluationContext.armyGrowth += armyGrowth * multiplier;
|
||||
evaluationContext.goldReward += dailyIncome * 5 * multiplier;
|
||||
evaluationContext.addNonCriticalStrategicalValue(strategicalValue * multiplier);
|
||||
evaluationContext.addNonCriticalStrategicalValue(1.7f * multiplier * strategicalValue);
|
||||
vstd::amax(evaluationContext.danger, defendTown.getTreat().danger);
|
||||
addTileDanger(evaluationContext, town->visitablePos(), defendTown.getTurn(), defendTown.getDefenceStrength());
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ public:
|
||||
int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero) const;
|
||||
uint64_t getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const;
|
||||
const HitMapInfo & getEnemyHeroDanger(const int3 & tile, uint8_t turn) const;
|
||||
uint64_t townArmyGrowth(const CGTownInstance * town) const;
|
||||
};
|
||||
|
||||
struct DLL_EXPORT EvaluationContext
|
||||
|
@ -28,6 +28,13 @@ ArmyUpgrade::ArmyUpgrade(const AIPath & upgradePath, const CGObjectInstance * up
|
||||
sethero(upgradePath.targetHero);
|
||||
}
|
||||
|
||||
ArmyUpgrade::ArmyUpgrade(const CGHeroInstance * targetMain, const CGObjectInstance * upgrader, const ArmyUpgradeInfo & upgrade)
|
||||
: CGoal(Goals::ARMY_UPGRADE), upgrader(upgrader), upgradeValue(upgrade.upgradeValue),
|
||||
initialValue(targetMain->getArmyStrength()), goldCost(upgrade.upgradeCost[EGameResID::GOLD])
|
||||
{
|
||||
sethero(targetMain);
|
||||
}
|
||||
|
||||
bool ArmyUpgrade::operator==(const ArmyUpgrade & other) const
|
||||
{
|
||||
return false;
|
||||
|
@ -27,6 +27,7 @@ namespace Goals
|
||||
|
||||
public:
|
||||
ArmyUpgrade(const AIPath & upgradePath, const CGObjectInstance * upgrader, const ArmyUpgradeInfo & upgrade);
|
||||
ArmyUpgrade(const CGHeroInstance * targetMain, const CGObjectInstance * upgrader, const ArmyUpgradeInfo & upgrade);
|
||||
|
||||
virtual bool operator==(const ArmyUpgrade & other) const override;
|
||||
virtual std::string toString() const override;
|
||||
|
@ -18,8 +18,8 @@ namespace NKAI
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
DefendTown::DefendTown(const CGTownInstance * town, const HitMapInfo & treat, const AIPath & defencePath)
|
||||
: CGoal(Goals::DEFEND_TOWN), treat(treat), defenceArmyStrength(defencePath.getHeroStrength()), turn(defencePath.turn())
|
||||
DefendTown::DefendTown(const CGTownInstance * town, const HitMapInfo & treat, const AIPath & defencePath, bool isContrattack)
|
||||
: CGoal(Goals::DEFEND_TOWN), treat(treat), defenceArmyStrength(defencePath.getHeroStrength()), turn(defencePath.turn()), contrattack(isContrattack)
|
||||
{
|
||||
settown(town);
|
||||
sethero(defencePath.targetHero);
|
||||
|
@ -24,9 +24,10 @@ namespace Goals
|
||||
uint64_t defenceArmyStrength;
|
||||
HitMapInfo treat;
|
||||
uint8_t turn;
|
||||
bool contrattack;
|
||||
|
||||
public:
|
||||
DefendTown(const CGTownInstance * town, const HitMapInfo & treat, const AIPath & defencePath);
|
||||
DefendTown(const CGTownInstance * town, const HitMapInfo & treat, const AIPath & defencePath, bool isContrattack = false);
|
||||
DefendTown(const CGTownInstance * town, const HitMapInfo & treat, const CGHeroInstance * defender);
|
||||
|
||||
virtual bool operator==(const DefendTown & other) const override;
|
||||
@ -37,6 +38,8 @@ namespace Goals
|
||||
uint64_t getDefenceStrength() const { return defenceArmyStrength; }
|
||||
|
||||
uint8_t getTurn() const { return turn; }
|
||||
|
||||
bool isContrAttack() { return contrattack; }
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user