/* * FuzzyHelper.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #include "../StdInc.h" #include "FuzzyHelper.h" #include "../Goals/Goals.h" #include "Nullkiller.h" #include "../../../lib/mapObjectConstructors/AObjectTypeHandler.h" #include "../../../lib/mapObjectConstructors/CObjectClassesHandler.h" #include "../../../lib/mapObjectConstructors/CBankInstanceConstructor.h" namespace NKAI { ui64 FuzzyHelper::estimateBankDanger(const CBank * bank) { //this one is not fuzzy anymore, just calculate weighted average auto objectInfo = bank->getObjectHandler()->getObjectInfo(bank->appearance); CBankInfo * bankInfo = dynamic_cast(objectInfo.get()); ui64 totalStrength = 0; ui8 totalChance = 0; for(auto config : bankInfo->getPossibleGuards(bank->cb)) { totalStrength += config.second.totalStrength * config.first; totalChance += config.first; } return totalStrength / std::max(totalChance, 1); //avoid division by zero } ui64 FuzzyHelper::evaluateDanger(const int3 & tile, const CGHeroInstance * visitor, bool checkGuards) { auto cb = ai->cb.get(); 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) -> bool { return !objWithID(obj); }); } if(const CGObjectInstance * dangerousObject = vstd::backOrNull(visitableObjects)) { objectDanger = evaluateDanger(dangerousObject); //unguarded objects can also be dangerous or unhandled if(objWithID(dangerousObject)) { auto hero = dynamic_cast(dangerousObject); if(hero->visitedTown && !hero->visitedTown->garrisonHero) { objectDanger += evaluateDanger(hero->visitedTown.get()); } } 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->memory->knownSubterraneanGates.find(dangerousObject); if(it != ai->memory->knownSubterraneanGates.end()) { auto guards = cb->getGuardingCreatures(it->second->visitablePos()); for(auto cre : guards) { float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage( visitor, dynamic_cast(cre)); vstd::amax(guardDanger, evaluateDanger(cre) * tacticalAdvantage); } } } } if(checkGuards) { auto guards = cb->getGuardingCreatures(tile); for(auto cre : guards) { float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast(cre)); vstd::amax(guardDanger, evaluateDanger(cre) * tacticalAdvantage); //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) { auto cb = ai->cb.get(); if(obj->tempOwner.isValidPlayer() && cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES) //owned or allied objects don't pose any threat return 0; switch(obj->ID) { case Obj::TOWN: { const CGTownInstance * town = dynamic_cast(obj); auto danger = town->getUpperArmy()->getArmyStrength(); if(danger || town->visitingHero) { auto fortLevel = town->fortLevel(); if(fortLevel == CGTownInstance::EFortLevel::CASTLE) danger += 10000; else if(fortLevel == CGTownInstance::EFortLevel::CITADEL) danger += 4000; } return danger; } case Obj::HERO: { const CGHeroInstance * hero = dynamic_cast(obj); return getHeroArmyStrengthWithCommander(hero, hero); } case Obj::ARTIFACT: case Obj::RESOURCE: { if(!vstd::contains(ai->memory->alreadyVisited, obj)) return 0; [[fallthrough]]; } case Obj::MONSTER: case Obj::GARRISON: case Obj::GARRISON2: case Obj::CREATURE_GENERATOR1: case Obj::CREATURE_GENERATOR4: case Obj::MINE: case Obj::ABANDONED_MINE: case Obj::PANDORAS_BOX: case Obj::CRYPT: //crypt case Obj::CREATURE_BANK: //crebank case Obj::DRAGON_UTOPIA: case Obj::SHIPWRECK: //shipwreck case Obj::DERELICT_SHIP: //derelict ship { const CArmedInstance * a = dynamic_cast(obj); return a->getArmyStrength(); } case Obj::PYRAMID: { return estimateBankDanger(dynamic_cast(obj)); } default: return 0; } } }