1
0
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:
Andrii Danylchenko 2023-06-11 19:21:50 +03:00
parent b19ac01bf9
commit 6ba74f02bc
13 changed files with 286 additions and 85 deletions

View File

@ -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;
}

View File

@ -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,

View File

@ -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();

View File

@ -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;
};
}

View File

@ -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;

View File

@ -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);

View File

@ -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))

View File

@ -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());
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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; }
};
}