123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- /*
- * Nullkiller.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 "Nullkiller.h"
- #include "../VCAI.h"
- #include "../Behaviors/CaptureObjectsBehavior.h"
- #include "../Behaviors/RecruitHeroBehavior.h"
- #include "../Behaviors/BuyArmyBehavior.h"
- #include "../Behaviors/StartupBehavior.h"
- #include "../Behaviors/DefenceBehavior.h"
- #include "../Behaviors/BuildingBehavior.h"
- #include "../Behaviors/GatherArmyBehavior.h"
- #include "../Behaviors/ClusterBehavior.h"
- #include "../Goals/Invalid.h"
- extern boost::thread_specific_ptr<CCallback> cb;
- extern boost::thread_specific_ptr<VCAI> ai;
- using namespace Goals;
- Nullkiller::Nullkiller()
- {
- memory.reset(new AIMemory());
- }
- void Nullkiller::init(std::shared_ptr<CCallback> cb, PlayerColor playerID)
- {
- this->cb = cb;
- this->playerID = playerID;
- priorityEvaluator.reset(new PriorityEvaluator(this));
- dangerHitMap.reset(new DangerHitMapAnalyzer(this));
- buildAnalyzer.reset(new BuildAnalyzer());
- objectClusterizer.reset(new ObjectClusterizer(this));
- dangerEvaluator.reset(new FuzzyHelper(this));
- pathfinder.reset(new AIPathfinder(cb.get(), this));
- armyManager.reset(new ArmyManager(cb.get(), this));
- heroManager.reset(new HeroManager(cb.get(), this));
- }
- Goals::TTask Nullkiller::choseBestTask(Goals::TTaskVec & tasks) const
- {
- Goals::TTask bestTask = *vstd::maxElementByFun(tasks, [](Goals::TTask task) -> float{
- return task->priority;
- });
- return bestTask;
- }
- Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const
- {
- logAi->debug("Checking behavior %s", behavior->toString());
- const int MAX_DEPTH = 10;
- Goals::TGoalVec goals[MAX_DEPTH + 1];
- Goals::TTaskVec tasks;
- std::map<Goals::TSubgoal, Goals::TSubgoal> decompositionMap;
- goals[0] = {behavior};
- int depth = 0;
- while(goals[0].size())
- {
- TSubgoal current = goals[depth].back();
- #if AI_TRACE_LEVEL >= 1
- logAi->trace("Decomposing %s, level: %d", current->toString(), depth);
- #endif
- TGoalVec subgoals = current->decompose();
- #if AI_TRACE_LEVEL >= 1
- logAi->trace("Found %d goals", subgoals.size());
- #endif
- if(depth < MAX_DEPTH)
- {
- goals[depth + 1].clear();
- }
- for(auto subgoal : subgoals)
- {
- if(subgoal->isElementar())
- {
- auto task = taskptr(*subgoal);
- #if AI_TRACE_LEVEL >= 1
- logAi->trace("Found task %s", task->toString());
- #endif
- if(task->priority <= 0)
- task->priority = priorityEvaluator->evaluate(subgoal);
- tasks.push_back(task);
- }
- else if(depth < MAX_DEPTH)
- {
- #if AI_TRACE_LEVEL >= 1
- logAi->trace("Found abstract goal %s", subgoal->toString());
- #endif
- goals[depth + 1].push_back(subgoal);
- }
- }
- if(depth < MAX_DEPTH && goals[depth + 1].size())
- {
- depth++;
- }
- else
- {
- goals[depth].pop_back();
- while(depth > 0 && goals[depth].empty())
- {
- depth--;
- goals[depth].pop_back();
- }
- }
- }
- if(tasks.empty())
- {
- logAi->debug("Behavior %s found no tasks", behavior->toString());
- return Goals::taskptr(Goals::Invalid());
- }
- auto task = choseBestTask(tasks);
- logAi->debug("Behavior %s returns %s, priority %f", behavior->toString(), task->toString(), task->priority);
- return task;
- }
- void Nullkiller::resetAiState()
- {
- playerID = ai->playerID;
- lockedHeroes.clear();
- dangerHitMap->reset();
- }
- void Nullkiller::updateAiState()
- {
- activeHero = nullptr;
- memory->removeInvisibleObjects(cb.get());
- dangerHitMap->updateHitMap();
- auto activeHeroes = cb->getHeroesInfo();
- vstd::erase_if(activeHeroes, [this](const CGHeroInstance * hero) -> bool
- {
- auto lockedReason = getHeroLockedReason(hero);
- return lockedReason == HeroLockedReason::DEFENCE;
- });
- pathfinder->updatePaths(activeHeroes, true);
- heroManager->update();
- armyManager->update();
- objectClusterizer->clusterize();
- buildAnalyzer->update();
- }
- bool Nullkiller::isHeroLocked(const CGHeroInstance * hero) const
- {
- return getHeroLockedReason(hero) != HeroLockedReason::NOT_LOCKED;
- }
- bool Nullkiller::arePathHeroesLocked(const AIPath & path) const
- {
- if(getHeroLockedReason(path.targetHero) == HeroLockedReason::STARTUP)
- {
- #if AI_TRACE_LEVEL >= 1
- logAi->trace("Hero %s is locked by STARTUP. Discarding %s", path.targetHero->name, path.toString());
- #endif
- return true;
- }
- for(auto & node : path.nodes)
- {
- auto lockReason = getHeroLockedReason(node.targetHero);
- if(lockReason != HeroLockedReason::NOT_LOCKED)
- {
- #if AI_TRACE_LEVEL >= 1
- logAi->trace("Hero %s is locked by STARTUP. Discarding %s", path.targetHero->name, path.toString());
- #endif
- return true;
- }
- }
- return false;
- }
- HeroLockedReason Nullkiller::getHeroLockedReason(const CGHeroInstance * hero) const
- {
- auto found = lockedHeroes.find(hero);
- return found != lockedHeroes.end() ? found->second : HeroLockedReason::NOT_LOCKED;
- }
- void Nullkiller::makeTurn()
- {
- resetAiState();
- while(true)
- {
- updateAiState();
- Goals::TTaskVec bestTasks = {
- choseBestTask(sptr(BuyArmyBehavior())),
- choseBestTask(sptr(CaptureObjectsBehavior())),
- choseBestTask(sptr(ClusterBehavior())),
- choseBestTask(sptr(RecruitHeroBehavior())),
- choseBestTask(sptr(DefenceBehavior())),
- choseBestTask(sptr(BuildingBehavior())),
- choseBestTask(sptr(GatherArmyBehavior()))
- };
- if(cb->getDate(Date::DAY) == 1)
- {
- bestTasks.push_back(choseBestTask(sptr(StartupBehavior())));
- }
- Goals::TTask bestTask = choseBestTask(bestTasks);
- /*if(bestTask->invalid())
- {
- logAi->trace("No goals found. Ending turn.");
- return;
- }*/
- if(bestTask->priority < MIN_PRIORITY)
- {
- logAi->trace("Goal %s has too low priority. It is not worth doing it. Ending turn.", bestTask->toString());
- return;
- }
- logAi->debug("Trying to realize %s (value %2.3f)", bestTask->toString(), bestTask->priority);
- try
- {
- bestTask->accept(ai.get());
- }
- catch(goalFulfilledException &)
- {
- logAi->trace("Task %s completed", bestTask->toString());
- }
- catch(std::exception & e)
- {
- logAi->debug("Failed to realize subgoal of type %s, I will stop.", bestTask->toString());
- logAi->debug("The error message was: %s", e.what());
- return;
- }
- }
- }
|