diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index 7d0104aa4..78818934f 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -194,155 +194,15 @@ bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectIns return ln->cost < rn->cost; } -ui64 evaluateDanger(crint3 tile) -{ - const TerrainTile * t = cb->getTile(tile, false); - if(!t) //we can know about guard but can't check its tile (the edge of fow) - return 190000000; //MUCH - - ui64 objectDanger = 0; - ui64 guardDanger = 0; - - auto visObjs = cb->getVisitableObjs(tile); - if(visObjs.size()) - objectDanger = evaluateDanger(visObjs.back()); - - int3 guardPos = cb->getGuardingCreaturePosition(tile); - if(guardPos.x >= 0 && guardPos != tile) - guardDanger = evaluateDanger(guardPos); - - //TODO mozna odwiedzic blockvis nie ruszajac straznika - return std::max(objectDanger, guardDanger); -} - -ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor) -{ - return evaluateDanger(tile, visitor, cb.get()); -} - -ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const CPlayerSpecificInfoCallback * cb) -{ - const TerrainTile * t = cb->getTile(tile, false); - if(!t) //we can know about guard but can't check its tile (the edge of fow) - return 190000000; //MUCH - - ui64 objectDanger = 0; - ui64 guardDanger = 0; - - auto visitableObjects = cb->getVisitableObjs(tile); - // in some scenarios hero happens to be "under" the object (eg town). Then we consider ONLY the hero. - if(vstd::contains_if(visitableObjects, objWithID)) - { - vstd::erase_if(visitableObjects, [](const CGObjectInstance * obj) - { - return !objWithID(obj); - }); - } - - if(const CGObjectInstance * dangerousObject = vstd::backOrNull(visitableObjects)) - { - objectDanger = evaluateDanger(dangerousObject); //unguarded objects can also be dangerous or unhandled - if(objectDanger) - { - //TODO: don't downcast objects AI shouldn't know about! - auto armedObj = dynamic_cast(dangerousObject); - if(armedObj) - { - float tacticalAdvantage = fh->tacticalAdvantageEngine.getTacticalAdvantage(visitor, armedObj); - objectDanger *= tacticalAdvantage; //this line tends to go infinite for allied towns (?) - } - } - if(dangerousObject->ID == Obj::SUBTERRANEAN_GATE) - { - //check guard on the other side of the gate - auto it = ai->knownSubterraneanGates.find(dangerousObject); - if(it != ai->knownSubterraneanGates.end()) - { - auto guards = cb->getGuardingCreatures(it->second->visitablePos()); - for(auto cre : guards) - { - vstd::amax(guardDanger, evaluateDanger(cre) * fh->tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast(cre))); - } - } - } - } - - auto guards = cb->getGuardingCreatures(tile); - for(auto cre : guards) - { - vstd::amax(guardDanger, evaluateDanger(cre) * fh->tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast(cre))); //we are interested in strongest monster around - } - - //TODO mozna odwiedzic blockvis nie ruszajac straznika - return std::max(objectDanger, guardDanger); -} - -ui64 evaluateDanger(const CGObjectInstance * obj) -{ - if(obj->tempOwner < PlayerColor::PLAYER_LIMIT && cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES) //owned or allied objects don't pose any threat - return 0; - - switch(obj->ID) - { - case Obj::HERO: - { - InfoAboutHero iah; - cb->getHeroInfo(obj, iah); - return iah.army.getStrength(); - } - case Obj::TOWN: - case Obj::GARRISON: - case Obj::GARRISON2: - { - InfoAboutTown iat; - cb->getTownInfo(obj, iat); - return iat.army.getStrength(); - } - case Obj::MONSTER: - { - //TODO!!!!!!!! - const CGCreature * cre = dynamic_cast(obj); - return cre->getArmyStrength(); - } - case Obj::CREATURE_GENERATOR1: - case Obj::CREATURE_GENERATOR4: - { - const CGDwelling * d = dynamic_cast(obj); - return d->getArmyStrength(); - } - case Obj::MINE: - case Obj::ABANDONED_MINE: - { - const CArmedInstance * a = dynamic_cast(obj); - return a->getArmyStrength(); - } - case Obj::CRYPT: //crypt - case Obj::CREATURE_BANK: //crebank - case Obj::DRAGON_UTOPIA: - case Obj::SHIPWRECK: //shipwreck - case Obj::DERELICT_SHIP: //derelict ship -// case Obj::PYRAMID: - return fh->estimateBankDanger(dynamic_cast(obj)); - case Obj::PYRAMID: - { - if(obj->subID == 0) - return fh->estimateBankDanger(dynamic_cast(obj)); - else - return 0; - } - default: - return 0; - } -} bool compareDanger(const CGObjectInstance * lhs, const CGObjectInstance * rhs) { - return evaluateDanger(lhs) < evaluateDanger(rhs); + return fh->evaluateDanger(lhs) < fh->evaluateDanger(rhs); } bool isSafeToVisit(HeroPtr h, crint3 tile) { - return isSafeToVisit(h, evaluateDanger(tile)); + return isSafeToVisit(h, fh->evaluateDanger(tile, h.get())); } bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength) diff --git a/AI/VCAI/AIUtility.h b/AI/VCAI/AIUtility.h index f121b378a..cc5b7dfad 100644 --- a/AI/VCAI/AIUtility.h +++ b/AI/VCAI/AIUtility.h @@ -166,9 +166,6 @@ bool isBlockVisitObj(const int3 & pos); bool isWeeklyRevisitable(const CGObjectInstance * obj); bool shouldVisit(HeroPtr h, const CGObjectInstance * obj); -ui64 evaluateDanger(const CGObjectInstance * obj); -ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const CPlayerSpecificInfoCallback * cb); -ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor); bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property! bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength); bool isSafeToVisit(HeroPtr h, crint3 tile); diff --git a/AI/VCAI/FuzzyEngines.cpp b/AI/VCAI/FuzzyEngines.cpp index 0f3f66f1b..4b505e4ae 100644 --- a/AI/VCAI/FuzzyEngines.cpp +++ b/AI/VCAI/FuzzyEngines.cpp @@ -19,6 +19,7 @@ #define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us extern boost::thread_specific_ptr ai; +extern FuzzyHelper * fh; engineBase::engineBase() { @@ -202,8 +203,6 @@ TacticalAdvantageEngine::TacticalAdvantageEngine() float TacticalAdvantageEngine::getTacticalAdvantage(const CArmedInstance * we, const CArmedInstance * enemy) { - boost::unique_lock lock(mx); - float output = 1; try { @@ -344,7 +343,7 @@ void HeroMovementGoalEngineBase::setSharedFuzzyVariables(Goals::AbstractGoal & g } float strengthRatioData = 10.0f; //we are much stronger than enemy - ui64 danger = evaluateDanger(goal.tile, goal.hero.h); + ui64 danger = fh->evaluateDanger(goal.tile, goal.hero.h); if(danger) strengthRatioData = (fl::scalar)goal.hero.h->getTotalStrength() / danger; diff --git a/AI/VCAI/FuzzyEngines.h b/AI/VCAI/FuzzyEngines.h index 583024316..cad20a48a 100644 --- a/AI/VCAI/FuzzyEngines.h +++ b/AI/VCAI/FuzzyEngines.h @@ -35,7 +35,6 @@ private: fl::InputVariable * bankPresent; fl::InputVariable * castleWalls; fl::OutputVariable * threat; - boost::mutex mx; }; class HeroMovementGoalEngineBase : public engineBase //in future - maybe derive from some (GoalEngineBase : public engineBase) class for handling non-movement goals with common utility for goal engines diff --git a/AI/VCAI/FuzzyHelper.cpp b/AI/VCAI/FuzzyHelper.cpp index 7b5ed25d3..288b435b5 100644 --- a/AI/VCAI/FuzzyHelper.cpp +++ b/AI/VCAI/FuzzyHelper.cpp @@ -17,6 +17,7 @@ FuzzyHelper * fh; extern boost::thread_specific_ptr ai; +extern boost::thread_specific_ptr cb; Goals::TSubgoal FuzzyHelper::chooseSolution(Goals::TGoalVec vec) { @@ -203,3 +204,123 @@ void FuzzyHelper::setPriority(Goals::TSubgoal & g) //calls evaluate - Visitor pa { g->setpriority(g->accept(this)); //this enforces returned value is set } + +ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor) +{ + return evaluateDanger(tile, visitor, cb.get()); +} + +ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const CPlayerSpecificInfoCallback * cb) +{ + const TerrainTile * t = cb->getTile(tile, false); + if(!t) //we can know about guard but can't check its tile (the edge of fow) + return 190000000; //MUCH + + ui64 objectDanger = 0; + ui64 guardDanger = 0; + + auto visitableObjects = cb->getVisitableObjs(tile); + // in some scenarios hero happens to be "under" the object (eg town). Then we consider ONLY the hero. + if(vstd::contains_if(visitableObjects, objWithID)) + { + vstd::erase_if(visitableObjects, [](const CGObjectInstance * obj) + { + return !objWithID(obj); + }); + } + + if(const CGObjectInstance * dangerousObject = vstd::backOrNull(visitableObjects)) + { + objectDanger = evaluateDanger(dangerousObject); //unguarded objects can also be dangerous or unhandled + if(objectDanger) + { + //TODO: don't downcast objects AI shouldn't know about! + auto armedObj = dynamic_cast(dangerousObject); + if(armedObj) + { + float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, armedObj); + objectDanger *= tacticalAdvantage; //this line tends to go infinite for allied towns (?) + } + } + if(dangerousObject->ID == Obj::SUBTERRANEAN_GATE) + { + //check guard on the other side of the gate + auto it = ai->knownSubterraneanGates.find(dangerousObject); + if(it != ai->knownSubterraneanGates.end()) + { + auto guards = cb->getGuardingCreatures(it->second->visitablePos()); + for(auto cre : guards) + { + vstd::amax(guardDanger, evaluateDanger(cre) * tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast(cre))); + } + } + } + } + + auto guards = cb->getGuardingCreatures(tile); + for(auto cre : guards) + { + vstd::amax(guardDanger, evaluateDanger(cre) * tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast(cre))); //we are interested in strongest monster around + } + + //TODO mozna odwiedzic blockvis nie ruszajac straznika + return std::max(objectDanger, guardDanger); +} + +ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj) +{ + if(obj->tempOwner < PlayerColor::PLAYER_LIMIT && cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES) //owned or allied objects don't pose any threat + return 0; + + switch(obj->ID) + { + case Obj::HERO: + { + InfoAboutHero iah; + cb->getHeroInfo(obj, iah); + return iah.army.getStrength(); + } + case Obj::TOWN: + case Obj::GARRISON: + case Obj::GARRISON2: + { + InfoAboutTown iat; + cb->getTownInfo(obj, iat); + return iat.army.getStrength(); + } + case Obj::MONSTER: + { + //TODO!!!!!!!! + const CGCreature * cre = dynamic_cast(obj); + return cre->getArmyStrength(); + } + case Obj::CREATURE_GENERATOR1: + case Obj::CREATURE_GENERATOR4: + { + const CGDwelling * d = dynamic_cast(obj); + return d->getArmyStrength(); + } + case Obj::MINE: + case Obj::ABANDONED_MINE: + { + const CArmedInstance * a = dynamic_cast(obj); + return a->getArmyStrength(); + } + case Obj::CRYPT: //crypt + case Obj::CREATURE_BANK: //crebank + case Obj::DRAGON_UTOPIA: + case Obj::SHIPWRECK: //shipwreck + case Obj::DERELICT_SHIP: //derelict ship + // case Obj::PYRAMID: + return estimateBankDanger(dynamic_cast(obj)); + case Obj::PYRAMID: + { + if(obj->subID == 0) + return estimateBankDanger(dynamic_cast(obj)); + else + return 0; + } + default: + return 0; + } +} \ No newline at end of file diff --git a/AI/VCAI/FuzzyHelper.h b/AI/VCAI/FuzzyHelper.h index 11552fab5..679c4f6d3 100644 --- a/AI/VCAI/FuzzyHelper.h +++ b/AI/VCAI/FuzzyHelper.h @@ -42,4 +42,8 @@ public: Goals::TSubgoal chooseSolution(Goals::TGoalVec vec); //std::shared_ptr chooseSolution (std::vector> & vec); + + ui64 evaluateDanger(const CGObjectInstance * obj); + ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const CPlayerSpecificInfoCallback * cb); + ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor); }; diff --git a/AI/VCAI/Goals/VisitObj.cpp b/AI/VCAI/Goals/VisitObj.cpp index 33e69325b..b9de283ed 100644 --- a/AI/VCAI/Goals/VisitObj.cpp +++ b/AI/VCAI/Goals/VisitObj.cpp @@ -53,7 +53,7 @@ TGoalVec VisitObj::getAllPossibleSubgoals() if(isSafeToVisit(hero, pos)) goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(hero))); else - goalList.push_back(sptr(GatherArmy(evaluateDanger(pos, hero.h) * SAFE_ATTACK_CONSTANT).sethero(hero).setisAbstract(true))); + goalList.push_back(sptr(GatherArmy(fh->evaluateDanger(pos, hero.h) * SAFE_ATTACK_CONSTANT).sethero(hero).setisAbstract(true))); return goalList; } @@ -67,7 +67,7 @@ TGoalVec VisitObj::getAllPossibleSubgoals() if(isSafeToVisit(potentialVisitor, pos)) goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(potentialVisitor))); else - goalList.push_back(sptr(GatherArmy(evaluateDanger(pos, potentialVisitor) * SAFE_ATTACK_CONSTANT).sethero(potentialVisitor).setisAbstract(true))); + goalList.push_back(sptr(GatherArmy(fh->evaluateDanger(pos, potentialVisitor) * SAFE_ATTACK_CONSTANT).sethero(potentialVisitor).setisAbstract(true))); } } if(!goalList.empty()) diff --git a/AI/VCAI/Goals/VisitTile.cpp b/AI/VCAI/Goals/VisitTile.cpp index ffac99195..f6bbc3e76 100644 --- a/AI/VCAI/Goals/VisitTile.cpp +++ b/AI/VCAI/Goals/VisitTile.cpp @@ -49,7 +49,7 @@ TSubgoal VisitTile::whatToDoToAchieve() } else { - return sptr(GatherArmy(evaluateDanger(tile, *ret->hero) * SAFE_ATTACK_CONSTANT) + return sptr(GatherArmy(fh->evaluateDanger(tile, *ret->hero) * SAFE_ATTACK_CONSTANT) .sethero(ret->hero).setisAbstract(true)); } } diff --git a/AI/VCAI/Pathfinding/AINodeStorage.cpp b/AI/VCAI/Pathfinding/AINodeStorage.cpp index 768069f78..42aac3e89 100644 --- a/AI/VCAI/Pathfinding/AINodeStorage.cpp +++ b/AI/VCAI/Pathfinding/AINodeStorage.cpp @@ -21,6 +21,7 @@ AINodeStorage::AINodeStorage(const int3 & Sizes) : sizes(Sizes) { nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS][NUM_CHAINS]); + dangerEvaluator.reset(new FuzzyHelper()); } AINodeStorage::~AINodeStorage() = default; @@ -358,6 +359,8 @@ std::vector AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) current = getAINode(current->theNodeBefore); } + path.targetObjectDanger = evaluateDanger(pos); + paths.push_back(path); } @@ -403,8 +406,7 @@ float AIPath::movementCost() const uint64_t AIPath::getTotalDanger(HeroPtr hero) const { uint64_t pathDanger = getPathDanger(); - uint64_t objDanger = evaluateDanger(nodes.front().coord, hero.get()); // bank danger is not checked by pathfinder - uint64_t danger = pathDanger > objDanger ? pathDanger : objDanger; + uint64_t danger = pathDanger > targetObjectDanger ? pathDanger : targetObjectDanger; return danger; } diff --git a/AI/VCAI/Pathfinding/AINodeStorage.h b/AI/VCAI/Pathfinding/AINodeStorage.h index 1cd90dd02..6c773bbb9 100644 --- a/AI/VCAI/Pathfinding/AINodeStorage.h +++ b/AI/VCAI/Pathfinding/AINodeStorage.h @@ -13,6 +13,7 @@ #include "../../../lib/CPathfinder.h" #include "../../../lib/mapObjects/CGHeroInstance.h" #include "../AIUtility.h" +#include "../FuzzyHelper.h" #include "../Goals/AbstractGoal.h" #include "Actions/ISpecialAction.h" @@ -38,6 +39,7 @@ struct AIPath { std::vector nodes; std::shared_ptr specialAction; + uint64_t targetObjectDanger; AIPath(); @@ -61,6 +63,7 @@ private: boost::multi_array nodes; const CPlayerSpecificInfoCallback * cb; const CGHeroInstance * hero; + std::unique_ptr dangerEvaluator; STRONG_INLINE void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility); @@ -110,6 +113,11 @@ public: return hero; } + uint64_t evaluateDanger(const int3 & tile) const + { + return dangerEvaluator->evaluateDanger(tile, hero, cb); + } + private: void calculateTownPortalTeleportations(const PathNodeInfo & source, std::vector & neighbours); }; diff --git a/AI/VCAI/Pathfinding/AIPathfinder.cpp b/AI/VCAI/Pathfinding/AIPathfinder.cpp index d1bf3ded6..de296ab3c 100644 --- a/AI/VCAI/Pathfinding/AIPathfinder.cpp +++ b/AI/VCAI/Pathfinding/AIPathfinder.cpp @@ -89,7 +89,7 @@ void AIPathfinder::updatePaths(std::vector heroes) boost::thread::hardware_concurrency(), (uint32_t)calculationTasks.size()); - if(threadsCount == 1) + if(threadsCount <= 1) { for(auto task : calculationTasks) { diff --git a/AI/VCAI/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp b/AI/VCAI/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp index 5b67a408d..83e51161b 100644 --- a/AI/VCAI/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp +++ b/AI/VCAI/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp @@ -121,8 +121,7 @@ namespace AIPathfinding return; } - auto hero = nodeStorage->getHero(); - auto danger = evaluateDanger(destination.coord, hero, cb); + auto danger = nodeStorage->evaluateDanger(destination.coord); destination.node = battleNode; nodeStorage->commit(destination, source);