diff --git a/AI/Nullkiller/Behaviors/Behavior.h b/AI/Nullkiller/Behaviors/Behavior.h index b47a5b4c8..3e50a94a5 100644 --- a/AI/Nullkiller/Behaviors/Behavior.h +++ b/AI/Nullkiller/Behaviors/Behavior.h @@ -10,10 +10,10 @@ #pragma once #include "../VCAI.h" +#error REMOVE THIS FILE -class Behavior +class Behavior : public Goals::AbstractGoal { public: - virtual Goals::TGoalVec getTasks() = 0; virtual std::string toString() const = 0; }; diff --git a/AI/Nullkiller/Behaviors/BuildingBehavior.cpp b/AI/Nullkiller/Behaviors/BuildingBehavior.cpp index 7d53e7963..0e5c5b09a 100644 --- a/AI/Nullkiller/Behaviors/BuildingBehavior.cpp +++ b/AI/Nullkiller/Behaviors/BuildingBehavior.cpp @@ -29,7 +29,7 @@ std::string BuildingBehavior::toString() const return "Build"; } -Goals::TGoalVec BuildingBehavior::getTasks() +Goals::TGoalVec BuildingBehavior::decompose() const { Goals::TGoalVec tasks; diff --git a/AI/Nullkiller/Behaviors/BuildingBehavior.h b/AI/Nullkiller/Behaviors/BuildingBehavior.h index be0ec7e42..6b7f28025 100644 --- a/AI/Nullkiller/Behaviors/BuildingBehavior.h +++ b/AI/Nullkiller/Behaviors/BuildingBehavior.h @@ -10,17 +10,24 @@ #pragma once #include "lib/VCMI_Lib.h" -#include "Behavior.h" #include "../AIUtility.h" +#include "../Goals/CGoal.h" -class BuildingBehavior : public Behavior +namespace Goals { -public: - BuildingBehavior() + class BuildingBehavior : public CGoal { - } + public: + BuildingBehavior() + { + } - virtual Goals::TGoalVec getTasks() override; - virtual std::string toString() const override; -}; + virtual Goals::TGoalVec decompose() const override; + virtual std::string toString() const override; + virtual bool operator==(const BuildingBehavior & other) const override + { + return true; + } + }; +} diff --git a/AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp b/AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp index f6e7e75cc..d37bfe323 100644 --- a/AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp +++ b/AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp @@ -29,7 +29,7 @@ std::string BuyArmyBehavior::toString() const return "Buy army"; } -Goals::TGoalVec BuyArmyBehavior::getTasks() +Goals::TGoalVec BuyArmyBehavior::decompose() const { Goals::TGoalVec tasks; diff --git a/AI/Nullkiller/Behaviors/BuyArmyBehavior.h b/AI/Nullkiller/Behaviors/BuyArmyBehavior.h index 528fe3371..4c0aa8f3d 100644 --- a/AI/Nullkiller/Behaviors/BuyArmyBehavior.h +++ b/AI/Nullkiller/Behaviors/BuyArmyBehavior.h @@ -10,17 +10,23 @@ #pragma once #include "lib/VCMI_Lib.h" -#include "Behavior.h" #include "../AIUtility.h" +#include "../Goals/CGoal.h" -class BuyArmyBehavior : public Behavior +namespace Goals { -public: - BuyArmyBehavior() + class BuyArmyBehavior : public CGoal { - } - - virtual Goals::TGoalVec getTasks() override; - virtual std::string toString() const override; -}; + public: + BuyArmyBehavior() + { + } + virtual Goals::TGoalVec decompose() const override; + virtual std::string toString() const override; + virtual bool operator==(const BuyArmyBehavior & other) const override + { + return true; + } + }; +} diff --git a/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp b/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp index 0c04ce18a..435a22252 100644 --- a/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp +++ b/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp @@ -28,7 +28,7 @@ std::string CaptureObjectsBehavior::toString() const return "Capture objects"; } -Goals::TGoalVec CaptureObjectsBehavior::getTasks() +Goals::TGoalVec CaptureObjectsBehavior::decompose() const { Goals::TGoalVec tasks; @@ -42,7 +42,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks() for(auto objToVisit : objs) { -#ifdef AI_TRACE_LEVEL >= 1 +#if AI_TRACE_LEVEL >= 1 logAi->trace("Checking object %s, %s", objToVisit->getObjectName(), objToVisit->visitablePos().toString()); #endif @@ -55,19 +55,19 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks() std::vector> waysToVisitObj; std::shared_ptr closestWay; -#ifdef AI_TRACE_LEVEL >= 1 +#if AI_TRACE_LEVEL >= 1 logAi->trace("Found %d paths", paths.size()); #endif for(auto & path : paths) { -#ifdef AI_TRACE_LEVEL >= 2 +#if AI_TRACE_LEVEL >= 2 logAi->trace("Path found %s", path.toString()); #endif if(path.getFirstBlockedAction()) { -#ifdef AI_TRACE_LEVEL >= 2 +#if AI_TRACE_LEVEL >= 2 // TODO: decomposition? logAi->trace("Ignore path. Action is blocked."); #endif @@ -76,7 +76,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks() if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path)) { -#ifdef AI_TRACE_LEVEL >= 2 +#if AI_TRACE_LEVEL >= 2 logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength()); #endif continue; @@ -98,7 +98,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks() auto isSafe = isSafeToVisit(hero, path.heroArmy, danger); -#ifdef AI_TRACE_LEVEL >= 2 +#if AI_TRACE_LEVEL >= 2 logAi->trace( "It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld", isSafe ? "safe" : "not safe", diff --git a/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.h b/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.h index b3c2bbcef..c7ca9f774 100644 --- a/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.h +++ b/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.h @@ -10,51 +10,62 @@ #pragma once #include "lib/VCMI_Lib.h" -#include "Behavior.h" #include "../AIUtility.h" +#include "../Goals/CGoal.h" -class CaptureObjectsBehavior : public Behavior { -private: - std::vector objectTypes; - std::vector objectSubTypes; - std::vector objectsToCapture; - bool specificObjects; -public: - CaptureObjectsBehavior() +namespace Goals +{ + class CaptureObjectsBehavior : public CGoal { - objectTypes = std::vector(); - specificObjects = false; - } + private: + std::vector objectTypes; + std::vector objectSubTypes; + std::vector objectsToCapture; + bool specificObjects; + public: + CaptureObjectsBehavior() + { + objectTypes = std::vector(); + specificObjects = false; + } - CaptureObjectsBehavior(std::vector objectsToCapture) - { - this->objectsToCapture = objectsToCapture; - specificObjects = true; - } + CaptureObjectsBehavior(std::vector objectsToCapture) + { + this->objectsToCapture = objectsToCapture; + specificObjects = true; + } - CaptureObjectsBehavior(const CGObjectInstance * objectToCapture) - { - objectsToCapture = std::vector(); - objectsToCapture.push_back(objectToCapture); - specificObjects = true; - } + CaptureObjectsBehavior(const CGObjectInstance * objectToCapture) + { + objectsToCapture = std::vector(); + objectsToCapture.push_back(objectToCapture); + specificObjects = true; + } - virtual Goals::TGoalVec getTasks() override; - virtual std::string toString() const override; + virtual Goals::TGoalVec decompose() const override; + virtual std::string toString() const override; - CaptureObjectsBehavior & ofType(int type) { - objectTypes.push_back(type); + CaptureObjectsBehavior & ofType(int type) + { + objectTypes.push_back(type); - return *this; - } - CaptureObjectsBehavior & ofType(int type, int subType) { - objectTypes.push_back(type); - objectSubTypes.push_back(subType); + return *this; + } + CaptureObjectsBehavior & ofType(int type, int subType) + { + objectTypes.push_back(type); + objectSubTypes.push_back(subType); - return *this; - } + return *this; + } -private: - bool shouldVisitObject(ObjectIdRef obj) const; -}; + virtual bool operator==(const CaptureObjectsBehavior & other) const override + { + return false; + } + + private: + bool shouldVisitObject(ObjectIdRef obj) const; + }; +} diff --git a/AI/Nullkiller/Behaviors/CompleteQuestBehavior.cpp b/AI/Nullkiller/Behaviors/CompleteQuestBehavior.cpp new file mode 100644 index 000000000..718dfd473 --- /dev/null +++ b/AI/Nullkiller/Behaviors/CompleteQuestBehavior.cpp @@ -0,0 +1,237 @@ +/* +* CompleteQuestBehavior.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 "CompleteQuestBehavior.h" +#include "CaptureObjectsBehavior.h" +#include "../VCAI.h" +#include "../AIhelper.h" +#include "../../../lib/mapping/CMap.h" //for victory conditions +#include "../../../lib/CPathfinder.h" + +extern boost::thread_specific_ptr cb; +extern boost::thread_specific_ptr ai; +extern FuzzyHelper * fh; + +using namespace Goals; + +std::string CompleteQuestBehavior::toString() const +{ + return "Complete Quests"; +} + +TGoalVec CompleteQuestBehavior::decompose() const +{ + TGoalVec solutions; + + auto quests = cb->getMyQuests(); + + for(auto & q : quests) + { + if(q.quest->missionType == CQuest::MISSION_NONE || q.quest->progress == CQuest::COMPLETE) + { + continue; + } + + vstd::concatenate(solutions, getQuestTasks(q)); + } + + return solutions; +} + +TGoalVec CompleteQuestBehavior::getQuestTasks(const QuestInfo & q) const +{ + logAi->debug("Trying to realize quest: %s", questToString(q)); + + switch(q.quest->missionType) + { + case CQuest::MISSION_ART: + return missionArt(q); + + case CQuest::MISSION_HERO: + return missionHero(q); + + case CQuest::MISSION_ARMY: + return missionArmy(q); + + case CQuest::MISSION_RESOURCES: + return missionResources(q); + + case CQuest::MISSION_KILL_HERO: + case CQuest::MISSION_KILL_CREATURE: + return missionDestroyObj(q); + + case CQuest::MISSION_PRIMARY_STAT: + return missionIncreasePrimaryStat(q); + + case CQuest::MISSION_LEVEL: + return missionLevel(q); + + case CQuest::MISSION_PLAYER: + if(ai->playerID.getNum() != q.quest->m13489val) + logAi->debug("Can't be player of color %d", q.quest->m13489val); + + break; + + case CQuest::MISSION_KEYMASTER: + return missionKeymaster(q); + + } //end of switch + + return TGoalVec(); +} + +std::string CompleteQuestBehavior::questToString(const QuestInfo & q) const +{ + if(q.quest->missionType == CQuest::MISSION_NONE) + return "inactive quest"; + + MetaString ms; + q.quest->getRolloverText(ms, false); + + return ms.toString(); +} + +TGoalVec CompleteQuestBehavior::tryCompleteQuest(const QuestInfo & q) const +{ + TGoalVec solutions; + + auto tasks = CaptureObjectsBehavior(q.obj).decompose(); //TODO: choose best / free hero from among many possibilities? + + for(auto task : tasks) + { + if(task->hero && q.quest->checkQuest(task->hero.get())) + { + solutions.push_back(task); + } + } + + return solutions; +} + +TGoalVec CompleteQuestBehavior::missionArt(const QuestInfo & q) const +{ + TGoalVec solutions = tryCompleteQuest(q); + + if(!solutions.empty()) + return solutions; + + /*for(auto art : q.quest->m5arts) + { + solutions.push_back(sptr(GetArtOfType(art))); //TODO: transport? + }*/ + + return solutions; +} + +TGoalVec CompleteQuestBehavior::missionHero(const QuestInfo & q) const +{ + TGoalVec solutions = tryCompleteQuest(q); + + if(solutions.empty()) + { + //rule of a thumb - quest heroes usually are locked in prisons + return CaptureObjectsBehavior().ofType(Obj::PRISON).decompose(); + } + + return solutions; +} + +TGoalVec CompleteQuestBehavior::missionArmy(const QuestInfo & q) const +{ + TGoalVec solutions = tryCompleteQuest(q); + + if(!solutions.empty()) + return solutions; + /* + for(auto creature : q.quest->m6creatures) + { + solutions.push_back(sptr(GatherTroops(creature.type->idNumber, creature.count))); + }*/ + + return solutions; +} + +TGoalVec CompleteQuestBehavior::missionIncreasePrimaryStat(const QuestInfo & q) const +{ + return tryCompleteQuest(q); +} + +TGoalVec CompleteQuestBehavior::missionLevel(const QuestInfo & q) const +{ + return tryCompleteQuest(q); +} + +TGoalVec CompleteQuestBehavior::missionKeymaster(const QuestInfo & q) const +{ + TGoalVec solutions = tryCompleteQuest(q); + + if(solutions.empty()) + { + return CaptureObjectsBehavior().ofType(Obj::KEYMASTER, q.obj->subID).decompose(); + } + + return solutions; +} + +TGoalVec CompleteQuestBehavior::missionResources(const QuestInfo & q) const +{ + TGoalVec solutions; + + /*auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities? + + if(heroes.size()) + { + if(q.quest->checkQuest(heroes.front())) //it doesn't matter which hero it is + { + return solutions;// ai->ah->howToVisitObj(q.obj); + } + else + { + for(int i = 0; i < q.quest->m7resources.size(); ++i) + { + if(q.quest->m7resources[i]) + solutions.push_back(sptr(CollectRes(i, q.quest->m7resources[i]))); + } + } + } + else + { + solutions.push_back(sptr(Goals::RecruitHero())); //FIXME: checkQuest requires any hero belonging to player :( + }*/ + + return solutions; +} + +TGoalVec CompleteQuestBehavior::missionDestroyObj(const QuestInfo & q) const +{ + auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val); + + if(!obj) + return CaptureObjectsBehavior(q.obj).decompose(); + + if(obj->ID == Obj::HERO) + { + auto relations = cb->getPlayerRelations(ai->playerID, obj->tempOwner); + + //if(relations == PlayerRelations::SAME_PLAYER) + //{ + // auto heroToProtect = cb->getHero(obj->id); + + // //solutions.push_back(sptr(GatherArmy().sethero(heroToProtect))); + //} + //else + if(relations == PlayerRelations::ENEMIES) + { + return CaptureObjectsBehavior(obj).decompose(); + } + } + + return TGoalVec(); +} \ No newline at end of file diff --git a/AI/Nullkiller/Behaviors/CompleteQuestBehavior.h b/AI/Nullkiller/Behaviors/CompleteQuestBehavior.h new file mode 100644 index 000000000..7e1a1d758 --- /dev/null +++ b/AI/Nullkiller/Behaviors/CompleteQuestBehavior.h @@ -0,0 +1,48 @@ +/* +* CompleteQuestBehavior.h, 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 +* +*/ +#pragma once + +#include "lib/VCMI_Lib.h" +#include "../AIUtility.h" +#include "../../../lib/VCMI_Lib.h" +#include "../../../CCallback.h" +#include "../Goals/CGoal.h" + +namespace Goals +{ + class CompleteQuestBehavior : public CGoal + { + public: + CompleteQuestBehavior() + { + } + + virtual Goals::TGoalVec decompose() const override; + virtual std::string toString() const override; + + virtual bool operator==(const CompleteQuestBehavior & other) const override + { + return true; + } + + private: + TGoalVec getQuestTasks(const QuestInfo & q) const; + TGoalVec tryCompleteQuest(const QuestInfo & q) const; + TGoalVec missionArt(const QuestInfo & q) const; + TGoalVec missionHero(const QuestInfo & q) const; + TGoalVec missionArmy(const QuestInfo & q) const; + TGoalVec missionResources(const QuestInfo & q) const; + TGoalVec missionDestroyObj(const QuestInfo & q) const; + TGoalVec missionIncreasePrimaryStat(const QuestInfo & q) const; + TGoalVec missionLevel(const QuestInfo & q) const; + TGoalVec missionKeymaster(const QuestInfo & q) const; + std::string questToString(const QuestInfo & q) const; + }; +} diff --git a/AI/Nullkiller/Behaviors/DefenceBehavior.cpp b/AI/Nullkiller/Behaviors/DefenceBehavior.cpp index 4464bb2b6..730f086e0 100644 --- a/AI/Nullkiller/Behaviors/DefenceBehavior.cpp +++ b/AI/Nullkiller/Behaviors/DefenceBehavior.cpp @@ -14,7 +14,6 @@ #include "../AIhelper.h" #include "../AIUtility.h" #include "../Goals/BuyArmy.h" -#include "../Goals/VisitTile.h" #include "../Goals/ExecuteHeroChain.h" #include "../Goals/DismissHero.h" #include "../Goals/ExchangeSwapTownHeroes.h" @@ -32,7 +31,7 @@ std::string DefenceBehavior::toString() const return "Defend towns"; } -Goals::TGoalVec DefenceBehavior::getTasks() +Goals::TGoalVec DefenceBehavior::decompose() const { Goals::TGoalVec tasks; @@ -65,7 +64,7 @@ uint64_t townArmyIncome(const CGTownInstance * town) return result; } -void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) +void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const { auto basicPriority = 0.3f + std::sqrt(townArmyIncome(town) / 20000.0f) + town->dailyIncome()[Res::GOLD] / 10000.0f; @@ -163,7 +162,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta if(cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES) { logAi->debug("Hero %s can be recruited to defend %s", hero->name, town->name); - tasks.push_back(Goals::sptr(Goals::RecruitHero().settown(town).setobjid(hero->id.getNum()).setpriority(1))); + tasks.push_back(Goals::sptr(Goals::RecruitHero(town, hero).setpriority(1))); continue; } else diff --git a/AI/Nullkiller/Behaviors/DefenceBehavior.h b/AI/Nullkiller/Behaviors/DefenceBehavior.h index 973cf0be1..30f0b80ea 100644 --- a/AI/Nullkiller/Behaviors/DefenceBehavior.h +++ b/AI/Nullkiller/Behaviors/DefenceBehavior.h @@ -10,20 +10,28 @@ #pragma once #include "lib/VCMI_Lib.h" -#include "Behavior.h" +#include "../Goals/CGoal.h" #include "../AIUtility.h" -class DefenceBehavior : public Behavior +namespace Goals { -public: - DefenceBehavior() + class DefenceBehavior : public CGoal { - } + public: + DefenceBehavior() + { + } - virtual Goals::TGoalVec getTasks() override; - virtual std::string toString() const override; + virtual Goals::TGoalVec decompose() const override; + virtual std::string toString() const override; -private: - void evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town); -}; + virtual bool operator==(const DefenceBehavior & other) const override + { + return true; + } + + private: + void evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const; + }; +} diff --git a/AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp b/AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp index e3e37dbff..f35fdc01a 100644 --- a/AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp +++ b/AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp @@ -23,14 +23,12 @@ extern FuzzyHelper * fh; using namespace Goals; -#define AI_TRACE_LEVEL 2 - std::string GatherArmyBehavior::toString() const { return "Gather army"; } -Goals::TGoalVec GatherArmyBehavior::getTasks() +Goals::TGoalVec GatherArmyBehavior::decompose() const { Goals::TGoalVec tasks; @@ -65,12 +63,12 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her Goals::TGoalVec tasks; const int3 pos = hero->visitablePos(); -#ifdef AI_TRACE_LEVEL >= 1 +#if AI_TRACE_LEVEL >= 1 logAi->trace("Checking ways to gaher army for hero %s, %s", hero->getObjectName(), pos.toString()); #endif if(ai->nullkiller->isHeroLocked(hero)) { -#ifdef AI_TRACE_LEVEL >= 1 +#if AI_TRACE_LEVEL >= 1 logAi->trace("Skipping locked hero %s, %s", hero->getObjectName(), pos.toString()); #endif return tasks; @@ -79,13 +77,13 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her auto paths = ai->ah->getPathsToTile(pos); std::vector> waysToVisitObj; -#ifdef AI_TRACE_LEVEL >= 1 +#if AI_TRACE_LEVEL >= 1 logAi->trace("Found %d paths", paths.size()); #endif for(const AIPath & path : paths) { -#ifdef AI_TRACE_LEVEL >= 2 +#if AI_TRACE_LEVEL >= 2 logAi->trace("Path found %s", path.toString()); #endif @@ -93,7 +91,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her if(path.getFirstBlockedAction()) { -#ifdef AI_TRACE_LEVEL >= 2 +#if AI_TRACE_LEVEL >= 2 // TODO: decomposition? logAi->trace("Ignore path. Action is blocked."); #endif @@ -102,7 +100,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path)) { -#ifdef AI_TRACE_LEVEL >= 2 +#if AI_TRACE_LEVEL >= 2 logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength()); #endif continue; @@ -122,7 +120,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her auto isSafe = isSafeToVisit(hero, path.heroArmy, danger); -#ifdef AI_TRACE_LEVEL >= 2 +#if AI_TRACE_LEVEL >= 2 logAi->trace( "It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld", isSafe ? "safe" : "not safe", @@ -164,25 +162,25 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader) const int3 pos = upgrader->visitablePos(); TResources availableResources = cb->getResourceAmount(); -#ifdef AI_TRACE_LEVEL >= 1 +#if AI_TRACE_LEVEL >= 1 logAi->trace("Checking ways to upgrade army in town %s, %s", upgrader->getObjectName(), pos.toString()); #endif auto paths = ai->ah->getPathsToTile(pos); std::vector> waysToVisitObj; -#ifdef AI_TRACE_LEVEL >= 1 +#if AI_TRACE_LEVEL >= 1 logAi->trace("Found %d paths", paths.size()); #endif for(const AIPath & path : paths) { -#ifdef AI_TRACE_LEVEL >= 2 +#if AI_TRACE_LEVEL >= 2 logAi->trace("Path found %s", path.toString()); #endif if(upgrader->visitingHero != path.targetHero) { -#ifdef AI_TRACE_LEVEL >= 2 +#if AI_TRACE_LEVEL >= 2 logAi->trace("Ignore path. Town has visiting hero."); #endif continue; @@ -190,7 +188,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader) if(path.getFirstBlockedAction()) { -#ifdef AI_TRACE_LEVEL >= 2 +#if AI_TRACE_LEVEL >= 2 // TODO: decomposition? logAi->trace("Ignore path. Action is blocked."); #endif @@ -199,7 +197,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader) if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path)) { -#ifdef AI_TRACE_LEVEL >= 2 +#if AI_TRACE_LEVEL >= 2 logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength()); #endif continue; @@ -215,7 +213,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader) auto isSafe = isSafeToVisit(path.targetHero, path.heroArmy, danger); -#ifdef AI_TRACE_LEVEL >= 2 +#if AI_TRACE_LEVEL >= 2 logAi->trace( "It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld", isSafe ? "safe" : "not safe", diff --git a/AI/Nullkiller/Behaviors/GatherArmyBehavior.h b/AI/Nullkiller/Behaviors/GatherArmyBehavior.h index 7391455d3..faaec0f41 100644 --- a/AI/Nullkiller/Behaviors/GatherArmyBehavior.h +++ b/AI/Nullkiller/Behaviors/GatherArmyBehavior.h @@ -10,27 +10,29 @@ #pragma once #include "lib/VCMI_Lib.h" -#include "Behavior.h" +#include "../Goals/CGoal.h" #include "../AIUtility.h" -class GatherArmyBehavior : public Behavior { -private: - std::vector objectTypes; - std::vector objectSubTypes; - std::vector objectsToCapture; - bool specificObjects; -public: - GatherArmyBehavior() +namespace Goals +{ + class GatherArmyBehavior : public CGoal { - objectTypes = std::vector(); - specificObjects = false; - } + public: + GatherArmyBehavior() + { + } - virtual Goals::TGoalVec getTasks() override; - virtual std::string toString() const override; + virtual TGoalVec decompose() const override; + virtual std::string toString() const override; -private: - Goals::TGoalVec deliverArmyToHero(const CGHeroInstance * hero) const; - Goals::TGoalVec upgradeArmy(const CGTownInstance * upgrader) const; -}; + virtual bool operator==(const GatherArmyBehavior & other) const override + { + return true; + } + + private: + TGoalVec deliverArmyToHero(const CGHeroInstance * hero) const; + TGoalVec upgradeArmy(const CGTownInstance * upgrader) const; + }; +} diff --git a/AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp b/AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp index 86dc973b4..9e414bcc7 100644 --- a/AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp +++ b/AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp @@ -28,7 +28,7 @@ std::string RecruitHeroBehavior::toString() const return "Recruit hero"; } -Goals::TGoalVec RecruitHeroBehavior::getTasks() +Goals::TGoalVec RecruitHeroBehavior::decompose() const { Goals::TGoalVec tasks; auto towns = cb->getTownsInfo(); @@ -40,7 +40,7 @@ Goals::TGoalVec RecruitHeroBehavior::getTasks() if(cb->getHeroesInfo().size() < cb->getTownsInfo().size() + 1 || cb->getResourceAmount(Res::GOLD) > 10000) { - tasks.push_back(Goals::sptr(Goals::RecruitHero().settown(town).setpriority(3))); + tasks.push_back(Goals::sptr(Goals::RecruitHero(town).setpriority(3))); } } } diff --git a/AI/Nullkiller/Behaviors/RecruitHeroBehavior.h b/AI/Nullkiller/Behaviors/RecruitHeroBehavior.h index 782b7bb6a..295a8ec88 100644 --- a/AI/Nullkiller/Behaviors/RecruitHeroBehavior.h +++ b/AI/Nullkiller/Behaviors/RecruitHeroBehavior.h @@ -10,16 +10,24 @@ #pragma once #include "lib/VCMI_Lib.h" -#include "Behavior.h" +#include "../Goals/CGoal.h" #include "../AIUtility.h" -class RecruitHeroBehavior : public Behavior +namespace Goals { -public: - RecruitHeroBehavior() + class RecruitHeroBehavior : public CGoal { - } + public: + RecruitHeroBehavior() + { + } - virtual Goals::TGoalVec getTasks() override; - virtual std::string toString() const override; -}; + virtual TGoalVec decompose() const override; + virtual std::string toString() const override; + + virtual bool operator==(const RecruitHeroBehavior & other) const override + { + return true; + } + }; +} \ No newline at end of file diff --git a/AI/Nullkiller/Behaviors/StartupBehavior.cpp b/AI/Nullkiller/Behaviors/StartupBehavior.cpp index 517504d19..a0b4ca4e0 100644 --- a/AI/Nullkiller/Behaviors/StartupBehavior.cpp +++ b/AI/Nullkiller/Behaviors/StartupBehavior.cpp @@ -92,7 +92,7 @@ bool needToRecruitHero(const CGTownInstance * startupTown) return false; } -Goals::TGoalVec StartupBehavior::getTasks() +Goals::TGoalVec StartupBehavior::decompose() const { Goals::TGoalVec tasks; auto towns = cb->getTownsInfo(); @@ -166,7 +166,7 @@ Goals::TGoalVec StartupBehavior::getTasks() if(tasks.empty() && canRecruitHero && !startupTown->visitingHero) { - tasks.push_back(Goals::sptr(Goals::RecruitHero())); + tasks.push_back(Goals::sptr(Goals::RecruitHero(startupTown))); } if(tasks.empty() && towns.size()) diff --git a/AI/Nullkiller/Behaviors/StartupBehavior.h b/AI/Nullkiller/Behaviors/StartupBehavior.h index 2d34d23af..bd11c3181 100644 --- a/AI/Nullkiller/Behaviors/StartupBehavior.h +++ b/AI/Nullkiller/Behaviors/StartupBehavior.h @@ -10,17 +10,25 @@ #pragma once #include "lib/VCMI_Lib.h" -#include "Behavior.h" +#include "../Goals/CGoal.h" #include "../AIUtility.h" -class StartupBehavior : public Behavior +namespace Goals { -public: - StartupBehavior() + class StartupBehavior : public CGoal { - } + public: + StartupBehavior() + { + } - virtual Goals::TGoalVec getTasks() override; - virtual std::string toString() const override; -}; + virtual TGoalVec decompose() const override; + virtual std::string toString() const override; + + virtual bool operator==(const StartupBehavior & other) const override + { + return true; + } + }; +} diff --git a/AI/Nullkiller/CMakeLists.txt b/AI/Nullkiller/CMakeLists.txt index 96865e2c4..47a741f15 100644 --- a/AI/Nullkiller/CMakeLists.txt +++ b/AI/Nullkiller/CMakeLists.txt @@ -27,24 +27,18 @@ set(VCAI_SRCS Goals/GatherTroops.cpp Goals/BuyArmy.cpp Goals/AdventureSpellCast.cpp - Goals/Win.cpp - Goals/VisitTile.cpp - Goals/VisitObj.cpp - Goals/VisitHero.cpp Goals/CollectRes.cpp Goals/Trade.cpp Goals/RecruitHero.cpp Goals/DigAtTile.cpp Goals/GetArtOfType.cpp Goals/FindObj.cpp - Goals/CompleteQuest.cpp Goals/ExecuteHeroChain.cpp Goals/ExchangeSwapTownHeroes.cpp Engine/Nullkiller.cpp Engine/PriorityEvaluator.cpp Analyzers/DangerHitMapAnalyzer.cpp Analyzers/BuildAnalyzer.cpp - Behaviors/Behavior.cpp Behaviors/CaptureObjectsBehavior.cpp Behaviors/RecruitHeroBehavior.cpp Behaviors/BuyArmyBehavior.cpp @@ -52,6 +46,7 @@ set(VCAI_SRCS Behaviors/StartupBehavior.cpp Behaviors/BuildingBehavior.cpp Behaviors/GatherArmyBehavior.cpp + Behaviors/CompleteQuestBehavior.cpp main.cpp VCAI.cpp ) @@ -88,17 +83,12 @@ set(VCAI_HEADERS Goals/GatherTroops.h Goals/BuyArmy.h Goals/AdventureSpellCast.h - Goals/Win.h - Goals/VisitTile.h - Goals/VisitObj.h - Goals/VisitHero.h Goals/CollectRes.h Goals/Trade.h Goals/RecruitHero.h Goals/DigAtTile.h Goals/GetArtOfType.h Goals/FindObj.h - Goals/CompleteQuest.h Goals/ExecuteHeroChain.h Goals/ExchangeSwapTownHeroes.h Goals/Goals.h @@ -106,7 +96,6 @@ set(VCAI_HEADERS Engine/PriorityEvaluator.h Analyzers/DangerHitMapAnalyzer.h Analyzers/BuildAnalyzer.h - Behaviors/Behavior.h Behaviors/CaptureObjectsBehavior.h Behaviors/RecruitHeroBehavior.h Behaviors/BuyArmyBehavior.h @@ -114,6 +103,7 @@ set(VCAI_HEADERS Behaviors/StartupBehavior.h Behaviors/BuildingBehavior.h Behaviors/GatherArmyBehavior.h + Behaviors/CompleteQuestBehavior.h VCAI.h ) diff --git a/AI/Nullkiller/Engine/Nullkiller.cpp b/AI/Nullkiller/Engine/Nullkiller.cpp index 4aa7dbc14..7c19e954c 100644 --- a/AI/Nullkiller/Engine/Nullkiller.cpp +++ b/AI/Nullkiller/Engine/Nullkiller.cpp @@ -23,6 +23,8 @@ extern boost::thread_specific_ptr cb; extern boost::thread_specific_ptr ai; +using namespace Goals; + Nullkiller::Nullkiller() { priorityEvaluator.reset(new PriorityEvaluator()); @@ -30,38 +32,75 @@ Nullkiller::Nullkiller() buildAnalyzer.reset(new BuildAnalyzer()); } -Goals::TSubgoal Nullkiller::choseBestTask(Goals::TGoalVec & tasks) const +Goals::TTask Nullkiller::choseBestTask(Goals::TTaskVec & tasks) const { - Goals::TSubgoal bestTask = *vstd::maxElementByFun(tasks, [](Goals::TSubgoal goal) -> float{ - return goal->priority; + Goals::TTask bestTask = *vstd::maxElementByFun(tasks, [](Goals::TTask task) -> float{ + return task->priority; }); return bestTask; } -Goals::TSubgoal Nullkiller::choseBestTask(std::shared_ptr behavior) const +Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const { logAi->debug("Checking behavior %s", behavior->toString()); - auto tasks = behavior->getTasks(); + const int MAX_DEPTH = 10; + Goals::TGoalVec goals[MAX_DEPTH + 1]; + Goals::TTaskVec tasks; + + goals[0] = {behavior}; if(tasks.empty()) { logAi->debug("Behavior %s found no tasks", behavior->toString()); - return Goals::sptr(Goals::Invalid()); + return Goals::taskptr(Goals::Invalid()); } logAi->trace("Evaluating priorities, tasks count %d", tasks.size()); - for(auto task : tasks) + int depth = 0; + while(goals[0].size()) { - task->setpriority(priorityEvaluator->evaluate(task)); + TSubgoal current = goals[depth].back(); + TGoalVec subgoals = current->decompose(); + + goals[depth + 1].clear(); + + for(auto subgoal : subgoals) + { + if(subgoal->isElementar) + { + auto task = taskptr(*subgoal); + + if(task->priority <= 0) + task->priority = priorityEvaluator->evaluate(subgoal); + + tasks.push_back(task); + } + else + { + goals[depth + 1].push_back(subgoal); + } + } + + if(goals[depth + 1].size() && depth < MAX_DEPTH) + { + depth++; + } + else + { + while(depth > 0 && goals[depth].empty()) + { + depth--; + } + } } auto task = choseBestTask(tasks); - logAi->debug("Behavior %s returns %s(%s), priority %f", behavior->toString(), task->name(), task->tile.toString(), task->priority); + logAi->debug("Behavior %s returns %s, priority %f", behavior->toString(), task->toString(), task->priority); return task; } @@ -141,54 +180,49 @@ void Nullkiller::makeTurn() { updateAiState(); - Goals::TGoalVec bestTasks = { - choseBestTask(std::make_shared()), - choseBestTask(std::make_shared()), - choseBestTask(std::make_shared()), - choseBestTask(std::make_shared()), - choseBestTask(std::make_shared()), - choseBestTask(std::make_shared()) + Goals::TTaskVec bestTasks = { + choseBestTask(sptr(BuyArmyBehavior())), + choseBestTask(sptr(CaptureObjectsBehavior())), + choseBestTask(sptr(RecruitHeroBehavior())), + choseBestTask(sptr(DefenceBehavior())), + choseBestTask(sptr(BuildingBehavior())), + choseBestTask(sptr(GatherArmyBehavior())) }; if(cb->getDate(Date::DAY) == 1) { - bestTasks.push_back(choseBestTask(std::make_shared())); + bestTasks.push_back(choseBestTask(sptr(StartupBehavior()))); } - Goals::TSubgoal bestTask = choseBestTask(bestTasks); + Goals::TTask bestTask = choseBestTask(bestTasks); - if(bestTask->invalid()) + /*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->name()); + 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->name(), bestTask->priority); + logAi->debug("Trying to realize %s (value %2.3f)", bestTask->toString(), bestTask->priority); try { - if(bestTask->hero) - { - setActive(bestTask->hero.get(), bestTask->tile); - } - bestTask->accept(ai.get()); } catch(goalFulfilledException &) { - logAi->trace("Task %s completed", bestTask->name()); + logAi->trace("Task %s completed", bestTask->toString()); } catch(std::exception & e) { - logAi->debug("Failed to realize subgoal of type %s, I will stop.", bestTask->name()); + logAi->debug("Failed to realize subgoal of type %s, I will stop.", bestTask->toString()); logAi->debug("The error message was: %s", e.what()); return; diff --git a/AI/Nullkiller/Engine/Nullkiller.h b/AI/Nullkiller/Engine/Nullkiller.h index ff9d1bacb..f0f288fbf 100644 --- a/AI/Nullkiller/Engine/Nullkiller.h +++ b/AI/Nullkiller/Engine/Nullkiller.h @@ -13,7 +13,6 @@ #include "../Analyzers/DangerHitMapAnalyzer.h" #include "../Analyzers/BuildAnalyzer.h" #include "../Goals/AbstractGoal.h" -#include "../Behaviors/Behavior.h" const float MAX_GOLD_PEASURE = 0.3f; const float MIN_PRIORITY = 0.01f; @@ -56,6 +55,6 @@ public: private: void resetAiState(); void updateAiState(); - Goals::TSubgoal choseBestTask(std::shared_ptr behavior) const; - Goals::TSubgoal choseBestTask(Goals::TGoalVec & tasks) const; + Goals::TTask choseBestTask(Goals::TSubgoal behavior) const; + Goals::TTask choseBestTask(Goals::TTaskVec & tasks) const; }; diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index 368242320..62c8905a0 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -520,9 +520,6 @@ Goals::EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgo /// importance float PriorityEvaluator::evaluate(Goals::TSubgoal task) { - if(task->priority > 0) - return task->priority; - auto evaluationContext = buildEvaluationContext(task); int rewardType = (evaluationContext.goldReward > 0 ? 1 : 0) @@ -561,7 +558,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task) #ifdef VCMI_TRACE_PATHFINDER logAi->trace("Evaluated %s, loss: %f, turns main: %f, scout: %f, gold: %d, cost: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, cwr: %f, result %f", - task->name(), + task->toString(), evaluationContext.armyLossPersentage, evaluationContext.movementCostByRole[HeroRole::MAIN], evaluationContext.movementCostByRole[HeroRole::SCOUT], diff --git a/AI/Nullkiller/FuzzyEngines.cpp b/AI/Nullkiller/FuzzyEngines.cpp index 2b41c14bf..597629505 100644 --- a/AI/Nullkiller/FuzzyEngines.cpp +++ b/AI/Nullkiller/FuzzyEngines.cpp @@ -89,19 +89,6 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army) return as; } -float HeroMovementGoalEngineBase::calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const -{ - if(goal.evaluationContext.movementCost > 0) - { - return goal.evaluationContext.movementCost; - } - else - { - auto pathInfo = ai->myCb->getPathsInfo(goal.hero.h)->getPathInfo(goal.tile); - return pathInfo->cost; - } -} - TacticalAdvantageEngine::TacticalAdvantageEngine() { try @@ -254,206 +241,3 @@ float TacticalAdvantageEngine::getTacticalAdvantage(const CArmedInstance * we, c return output; } - -//std::shared_ptr chooseSolution (std::vector> & vec) - -HeroMovementGoalEngineBase::HeroMovementGoalEngineBase() -{ - try - { - strengthRatio = new fl::InputVariable("strengthRatio"); //hero must be strong enough to defeat guards - heroStrengthVariable = new fl::InputVariable("heroStrengthVariable"); //we want to use weakest possible hero - turnDistanceVariable = new fl::InputVariable("turnDistanceVariable"); //we want to use hero who is near - missionImportance = new fl::InputVariable("lockedMissionImportance"); //we may want to preempt hero with low-priority mission - value = new fl::OutputVariable("Value"); - value->setMinimum(0); - value->setMaximum(5); - - std::vector helper = { strengthRatio, heroStrengthVariable, turnDistanceVariable, missionImportance }; - for(auto val : helper) - { - engine.addInputVariable(val); - } - engine.addOutputVariable(value); - - strengthRatio->addTerm(new fl::Ramp("LOW", SAFE_ATTACK_CONSTANT, 0)); - strengthRatio->addTerm(new fl::Ramp("HIGH", SAFE_ATTACK_CONSTANT, SAFE_ATTACK_CONSTANT * 3)); - strengthRatio->setRange(0, SAFE_ATTACK_CONSTANT * 3); - - //strength compared to our main hero - heroStrengthVariable->addTerm(new fl::Ramp("LOW", 0.5, 0)); - heroStrengthVariable->addTerm(new fl::Triangle("MEDIUM", 0.2, 0.8)); - heroStrengthVariable->addTerm(new fl::Ramp("HIGH", 0.5, 1)); - heroStrengthVariable->setRange(0.0, 1.0); - - turnDistanceVariable->addTerm(new fl::Ramp("SHORT", 0.5, 0)); - turnDistanceVariable->addTerm(new fl::Triangle("MEDIUM", 0.1, 0.8)); - turnDistanceVariable->addTerm(new fl::Ramp("LONG", 0.5, 10)); - turnDistanceVariable->setRange(0.0, 10.0); - - missionImportance->addTerm(new fl::Ramp("LOW", 2.5, 0)); - missionImportance->addTerm(new fl::Triangle("MEDIUM", 2, 3)); - missionImportance->addTerm(new fl::Ramp("HIGH", 2.5, 5)); - missionImportance->setRange(0.0, 5.0); - - //an issue: in 99% cases this outputs center of mass (2.5) regardless of actual input :/ - //should be same as "mission Importance" to keep consistency - value->addTerm(new fl::Ramp("LOW", 2.5, 0)); - value->addTerm(new fl::Triangle("MEDIUM", 2, 3)); //can't be center of mass :/ - value->addTerm(new fl::Ramp("HIGH", 2.5, 5)); - value->setRange(0.0, 5.0); - - //use unarmed scouts if possible - addRule("if strengthRatio is HIGH and heroStrengthVariable is LOW then Value is HIGH"); - //we may want to use secondary hero(es) rather than main hero - addRule("if strengthRatio is HIGH and heroStrengthVariable is MEDIUM then Value is MEDIUM"); - addRule("if strengthRatio is HIGH and heroStrengthVariable is HIGH then Value is LOW"); - //don't assign targets to heroes who are too weak, but prefer targets of our main hero (in case we need to gather army) - addRule("if strengthRatio is LOW and heroStrengthVariable is LOW then Value is LOW"); - //attempt to arm secondary heroes is not stupid - addRule("if strengthRatio is LOW and heroStrengthVariable is MEDIUM then Value is HIGH"); - addRule("if strengthRatio is LOW and heroStrengthVariable is HIGH then Value is LOW"); - - //do not cancel important goals - addRule("if lockedMissionImportance is HIGH then Value is LOW"); - addRule("if lockedMissionImportance is MEDIUM then Value is MEDIUM"); - addRule("if lockedMissionImportance is LOW then Value is HIGH"); - //pick nearby objects if it's easy, avoid long walks - addRule("if turnDistanceVariable is SHORT then Value is HIGH"); - addRule("if turnDistanceVariable is MEDIUM then Value is MEDIUM"); - addRule("if turnDistanceVariable is LONG then Value is LOW"); - } - catch(fl::Exception & fe) - { - logAi->error("HeroMovementGoalEngineBase: %s", fe.getWhat()); - } -} - -void HeroMovementGoalEngineBase::setSharedFuzzyVariables(Goals::AbstractGoal & goal) -{ - float turns = calculateTurnDistanceInputValue(goal); - float missionImportanceData = 0; - - if(vstd::contains(ai->lockedHeroes, goal.hero)) - { - missionImportanceData = ai->lockedHeroes[goal.hero]->priority; - } - else if(goal.parent) - { - missionImportanceData = goal.parent->priority; - } - - float strengthRatioData = 10.0f; //we are much stronger than enemy - ui64 danger = fh->evaluateDanger(goal.tile, goal.hero.h); - if(danger) - strengthRatioData = (fl::scalar)goal.hero.h->getTotalStrength() / danger; - - try - { - strengthRatio->setValue(strengthRatioData); - heroStrengthVariable->setValue((fl::scalar)goal.hero->getTotalStrength() / ai->primaryHero()->getTotalStrength()); - turnDistanceVariable->setValue(turns); - missionImportance->setValue(missionImportanceData); - } - catch(fl::Exception & fe) - { - logAi->error("HeroMovementGoalEngineBase::setSharedFuzzyVariables: %s", fe.getWhat()); - } -} - -VisitObjEngine::VisitObjEngine() -{ - try - { - objectValue = new fl::InputVariable("objectValue"); //value of that object type known by AI - - engine.addInputVariable(objectValue); - - //objectValue ranges are based on checking RMG priorities of some objects and checking LOW/MID/HIGH proportions for various values in QtFuzzyLite - objectValue->addTerm(new fl::Ramp("LOW", 3500.0, 0.0)); - objectValue->addTerm(new fl::Triangle("MEDIUM", 0.0, 8500.0)); - std::vector multiRamp = { fl::Discrete::Pair(5000.0, 0.0), fl::Discrete::Pair(10000.0, 0.75), fl::Discrete::Pair(20000.0, 1.0) }; - objectValue->addTerm(new fl::Discrete("HIGH", multiRamp)); - objectValue->setRange(0.0, 20000.0); //relic artifact value is border value by design, even better things are scaled down. - - addRule("if objectValue is HIGH then Value is HIGH"); - addRule("if objectValue is MEDIUM then Value is MEDIUM"); - addRule("if objectValue is LOW then Value is LOW"); - } - catch(fl::Exception & fe) - { - logAi->error("FindWanderTarget: %s", fe.getWhat()); - } - configure(); -} - -float VisitObjEngine::evaluate(Goals::VisitObj & goal) -{ - if(!goal.hero) - return 0; - - auto obj = ai->myCb->getObj(ObjectInstanceID(goal.objid)); - if(!obj) - { - logAi->error("Goals::VisitObj objid " + std::to_string(goal.objid) + " no longer visible, probably goal used for something it's not intended"); - return -100; // FIXME: Added check when goal was used for hero instead of VisitHero, but crashes are bad anyway - } - - boost::optional objValueKnownByAI = MapObjectsEvaluator::getInstance().getObjectValue(obj); - int objValue = 0; - - if(objValueKnownByAI != boost::none) //consider adding value manipulation based on object instances on map - { - objValue = std::min(std::max(objValueKnownByAI.get(), 0), 20000); - } - else - { - MapObjectsEvaluator::getInstance().addObjectData(obj->ID, obj->subID, 0); - logGlobal->error("AI met object type it doesn't know - ID: " + std::to_string(obj->ID) + ", subID: " + std::to_string(obj->subID) + " - adding to database with value " + std::to_string(objValue)); - } - - setSharedFuzzyVariables(goal); - - float output = -1.0f; - try - { - objectValue->setValue(objValue); - engine.process(); - output = value->getValue(); - } - catch(fl::Exception & fe) - { - logAi->error("evaluate getWanderTargetObjectValue: %s", fe.getWhat()); - } - assert(output >= 0.0f); - return output; -} - -VisitTileEngine::VisitTileEngine() //so far no VisitTile-specific variables that are not shared with HeroMovementGoalEngineBase -{ - configure(); -} - -float VisitTileEngine::evaluate(Goals::VisitTile & goal) -{ - //we assume that hero is already set and we want to choose most suitable one for the mission - if(!goal.hero) - return 0; - - //assert(cb->isInTheMap(g.tile)); - - setSharedFuzzyVariables(goal); - - try - { - engine.process(); - - goal.priority = value->getValue(); - } - catch(fl::Exception & fe) - { - logAi->error("evaluate VisitTile: %s", fe.getWhat()); - } - assert(goal.priority >= 0); - return goal.priority; -} diff --git a/AI/Nullkiller/FuzzyEngines.h b/AI/Nullkiller/FuzzyEngines.h index 479ff0bbe..ac73653ad 100644 --- a/AI/Nullkiller/FuzzyEngines.h +++ b/AI/Nullkiller/FuzzyEngines.h @@ -36,37 +36,3 @@ private: fl::InputVariable * castleWalls; fl::OutputVariable * threat; }; - -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 -{ -public: - HeroMovementGoalEngineBase(); - -protected: - void setSharedFuzzyVariables(Goals::AbstractGoal & goal); - - fl::InputVariable * strengthRatio; - fl::InputVariable * heroStrengthVariable; - fl::InputVariable * turnDistanceVariable; - fl::InputVariable * missionImportance; - fl::OutputVariable * value; - -private: - float calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const; -}; - -class VisitTileEngine : public HeroMovementGoalEngineBase -{ -public: - VisitTileEngine(); - float evaluate(Goals::VisitTile & goal); -}; - -class VisitObjEngine : public HeroMovementGoalEngineBase -{ -public: - VisitObjEngine(); - float evaluate(Goals::VisitObj & goal); -protected: - fl::InputVariable * objectValue; -}; diff --git a/AI/Nullkiller/Goals/AbstractGoal.cpp b/AI/Nullkiller/Goals/AbstractGoal.cpp index df7d22141..8f12e7be6 100644 --- a/AI/Nullkiller/Goals/AbstractGoal.cpp +++ b/AI/Nullkiller/Goals/AbstractGoal.cpp @@ -29,7 +29,19 @@ TSubgoal Goals::sptr(const AbstractGoal & tmp) return ptr; } -std::string AbstractGoal::name() const //TODO: virtualize +TTask Goals::taskptr(const AbstractGoal & tmp) +{ + TTask ptr; + + if(!tmp.isElementar) + throw cannotFulfillGoalException(tmp.toString() + " is not elementar"); + + ptr.reset(dynamic_cast(tmp.clone())); + + return ptr; +} + +std::string AbstractGoal::toString() const //TODO: virtualize { std::string desc; switch(goalType) @@ -124,11 +136,6 @@ bool AbstractGoal::invalid() const return goalType == EGoals::INVALID; } -void AbstractGoal::accept(VCAI * ai) -{ - ai->tryRealize(*this); -} - EvaluationContext::EvaluationContext() : movementCost(0.0), manaCost(0), diff --git a/AI/Nullkiller/Goals/AbstractGoal.h b/AI/Nullkiller/Goals/AbstractGoal.h index 75387c39f..de46a0372 100644 --- a/AI/Nullkiller/Goals/AbstractGoal.h +++ b/AI/Nullkiller/Goals/AbstractGoal.h @@ -22,21 +22,16 @@ class FuzzyHelper; namespace Goals { class AbstractGoal; - class Explore; + class ITask; class RecruitHero; - class VisitTile; - class VisitObj; - class VisitHero; class BuildThis; class DigAtTile; class CollectRes; class BuyArmy; class BuildBoat; - class GatherArmy; class ClearWayTo; class Invalid; class Trade; - class CompleteQuest; class AdventureSpellCast; enum EGoals @@ -78,6 +73,8 @@ namespace Goals //TODO: serialize? }; + typedef std::shared_ptr TTask; + typedef std::vector TTaskVec; typedef std::vector TGoalVec; //method chaining + clone pattern @@ -91,6 +88,7 @@ namespace Goals enum { LOW_PR = -1 }; DLL_EXPORT TSubgoal sptr(const AbstractGoal & tmp); + DLL_EXPORT TTask taskptr(const AbstractGoal & tmp); struct DLL_EXPORT EvaluationContext { @@ -117,23 +115,21 @@ namespace Goals { public: bool isElementar; VSETTER(bool, isElementar) - bool isAbstract; VSETTER(bool, isAbstract) - float priority; VSETTER(float, priority) - int value; VSETTER(int, value) - int resID; VSETTER(int, resID) - int objid; VSETTER(int, objid) - int aid; VSETTER(int, aid) - int3 tile; VSETTER(int3, tile) - HeroPtr hero; VSETTER(HeroPtr, hero) - const CGTownInstance *town; VSETTER(CGTownInstance *, town) - int bid; VSETTER(int, bid) - TSubgoal parent; VSETTER(TSubgoal, parent) - EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext) + bool isAbstract; VSETTER(bool, isAbstract) + int value; VSETTER(int, value) + int resID; VSETTER(int, resID) + int objid; VSETTER(int, objid) + int aid; VSETTER(int, aid) + int3 tile; VSETTER(int3, tile) + HeroPtr hero; VSETTER(HeroPtr, hero) + const CGTownInstance *town; VSETTER(CGTownInstance *, town) + int bid; VSETTER(int, bid) + TSubgoal parent; VSETTER(TSubgoal, parent) + EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext) - AbstractGoal(EGoals goal = EGoals::INVALID) + AbstractGoal(EGoals goal = EGoals::INVALID) : goalType(goal), evaluationContext() { - priority = 0; isElementar = false; isAbstract = false; value = 0; @@ -150,25 +146,18 @@ namespace Goals { return const_cast(this); } - virtual TGoalVec getAllPossibleSubgoals() + + virtual TGoalVec decompose() const { return TGoalVec(); } - virtual TSubgoal whatToDoToAchieve() - { - return sptr(AbstractGoal()); - } EGoals goalType; - virtual std::string name() const; + virtual std::string toString() const; bool invalid() const; - - ///Visitor pattern - //TODO: make accept work for std::shared_ptr... somehow - virtual void accept(VCAI * ai); //unhandled goal will report standard error - + virtual bool operator==(const AbstractGoal & g) const; bool operator!=(const AbstractGoal & g) const @@ -192,4 +181,15 @@ namespace Goals h & bid; } }; + + class DLL_EXPORT ITask + { + public: + float priority; + + ///Visitor pattern + //TODO: make accept work for std::shared_ptr... somehow + virtual void accept(VCAI * ai) = 0; //unhandled goal will report standard error + virtual std::string toString() const = 0; + }; } diff --git a/AI/Nullkiller/Goals/AdventureSpellCast.cpp b/AI/Nullkiller/Goals/AdventureSpellCast.cpp index e5e03c931..628f5e865 100644 --- a/AI/Nullkiller/Goals/AdventureSpellCast.cpp +++ b/AI/Nullkiller/Goals/AdventureSpellCast.cpp @@ -26,7 +26,7 @@ bool AdventureSpellCast::operator==(const AdventureSpellCast & other) const return hero.h == other.hero.h; } -TSubgoal AdventureSpellCast::whatToDoToAchieve() +void AdventureSpellCast::accept(VCAI * ai) { if(!hero.validAndSet()) throw cannotFulfillGoalException("Invalid hero!"); @@ -47,11 +47,6 @@ TSubgoal AdventureSpellCast::whatToDoToAchieve() if(spellID == SpellID::TOWN_PORTAL && town && town->visitingHero) throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->name); - return iAmElementar(); -} - -void AdventureSpellCast::accept(VCAI * ai) -{ if(town && spellID == SpellID::TOWN_PORTAL) { ai->selectedObject = town->id; @@ -73,7 +68,7 @@ void AdventureSpellCast::accept(VCAI * ai) throw goalFulfilledException(sptr(*this)); } -std::string AdventureSpellCast::name() const +std::string AdventureSpellCast::toString() const { return "AdventureSpellCast " + spellID.toSpell()->name; } diff --git a/AI/Nullkiller/Goals/AdventureSpellCast.h b/AI/Nullkiller/Goals/AdventureSpellCast.h index 720bdb86d..377a9ee20 100644 --- a/AI/Nullkiller/Goals/AdventureSpellCast.h +++ b/AI/Nullkiller/Goals/AdventureSpellCast.h @@ -13,31 +13,25 @@ namespace Goals { - class DLL_EXPORT AdventureSpellCast : public CGoal + class DLL_EXPORT AdventureSpellCast : public ElementarGoal { private: SpellID spellID; public: AdventureSpellCast(HeroPtr hero, SpellID spellID) - : CGoal(Goals::ADVENTURE_SPELL_CAST), spellID(spellID) + : ElementarGoal(Goals::ADVENTURE_SPELL_CAST), spellID(spellID) { sethero(hero); } - TGoalVec getAllPossibleSubgoals() override - { - return TGoalVec(); - } - const CSpell * getSpell() const { return spellID.toSpell(); } - TSubgoal whatToDoToAchieve() override; void accept(VCAI * ai) override; - std::string name() const override; + std::string toString() const override; virtual bool operator==(const AdventureSpellCast & other) const override; }; } diff --git a/AI/Nullkiller/Goals/BuildBoat.cpp b/AI/Nullkiller/Goals/BuildBoat.cpp index 6ea051075..daccdbb76 100644 --- a/AI/Nullkiller/Goals/BuildBoat.cpp +++ b/AI/Nullkiller/Goals/BuildBoat.cpp @@ -14,6 +14,7 @@ #include "../AIhelper.h" #include "../../../lib/mapping/CMap.h" //for victory conditions #include "../../../lib/CPathfinder.h" +#include "../Behaviors/CaptureObjectsBehavior.h" extern boost::thread_specific_ptr cb; extern boost::thread_specific_ptr ai; @@ -26,23 +27,23 @@ bool BuildBoat::operator==(const BuildBoat & other) const return shipyard->o->id == other.shipyard->o->id; } -//TSubgoal BuildBoat::whatToDoToAchieve() -//{ -// if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) -// { -// return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o)); -// } -// -// if(shipyard->shipyardStatus() != IShipyard::GOOD) -// { -// throw cannotFulfillGoalException("Shipyard is busy."); -// } -// -// TResources boatCost; -// shipyard->getBoatCost(boatCost); -// -// return ai->ah->whatToDo(boatCost, this->iAmElementar()); -//} +TSubgoal BuildBoat::decomposeSingle() const +{ + if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) + { + return sptr(CaptureObjectsBehavior(shipyard->o)); + } + + if(shipyard->shipyardStatus() != IShipyard::GOOD) + { + throw cannotFulfillGoalException("Shipyard is busy."); + } + + TResources boatCost; + shipyard->getBoatCost(boatCost); + + return iAmElementar(); +} void BuildBoat::accept(VCAI * ai) { @@ -75,7 +76,7 @@ void BuildBoat::accept(VCAI * ai) throw goalFulfilledException(sptr(*this)); } -std::string BuildBoat::name() const +std::string BuildBoat::toString() const { return "BuildBoat"; } diff --git a/AI/Nullkiller/Goals/BuildBoat.h b/AI/Nullkiller/Goals/BuildBoat.h index fca2a5b84..f43740b4b 100644 --- a/AI/Nullkiller/Goals/BuildBoat.h +++ b/AI/Nullkiller/Goals/BuildBoat.h @@ -13,23 +13,20 @@ namespace Goals { - class DLL_EXPORT BuildBoat : public CGoal + class DLL_EXPORT BuildBoat : public ElementarGoal { private: const IShipyard * shipyard; + TSubgoal decomposeSingle() const override; public: BuildBoat(const IShipyard * shipyard) - : CGoal(Goals::BUILD_BOAT), shipyard(shipyard) + : ElementarGoal(Goals::BUILD_BOAT), shipyard(shipyard) { - priority = 0; - } - TGoalVec getAllPossibleSubgoals() override - { - return TGoalVec(); } + void accept(VCAI * ai) override; - std::string name() const override; + std::string toString() const override; virtual bool operator==(const BuildBoat & other) const override; }; } diff --git a/AI/Nullkiller/Goals/BuildThis.cpp b/AI/Nullkiller/Goals/BuildThis.cpp index c1e75a9b9..0bab2fdb1 100644 --- a/AI/Nullkiller/Goals/BuildThis.cpp +++ b/AI/Nullkiller/Goals/BuildThis.cpp @@ -29,7 +29,7 @@ bool BuildThis::operator==(const BuildThis & other) const return town == other.town && bid == other.bid; } -std::string BuildThis::name() const +std::string BuildThis::toString() const { return "Build " + buildingInfo.name + "(" + std::to_string(bid) + ") in " + town->name; } \ No newline at end of file diff --git a/AI/Nullkiller/Goals/BuildThis.h b/AI/Nullkiller/Goals/BuildThis.h index 51e0492c6..b5f89a944 100644 --- a/AI/Nullkiller/Goals/BuildThis.h +++ b/AI/Nullkiller/Goals/BuildThis.h @@ -18,36 +18,36 @@ class FuzzyHelper; namespace Goals { - class DLL_EXPORT BuildThis : public CGoal + class DLL_EXPORT BuildThis : public ElementarGoal { public: BuildingInfo buildingInfo; TownDevelopmentInfo townInfo; BuildThis() //should be private, but unit test uses it - : CGoal(Goals::BUILD_STRUCTURE) + : ElementarGoal(Goals::BUILD_STRUCTURE) { } BuildThis(const BuildingInfo & buildingInfo, const TownDevelopmentInfo & townInfo) //should be private, but unit test uses it - : CGoal(Goals::BUILD_STRUCTURE), buildingInfo(buildingInfo), townInfo(townInfo) + : ElementarGoal(Goals::BUILD_STRUCTURE), buildingInfo(buildingInfo), townInfo(townInfo) { bid = buildingInfo.id; town = townInfo.town; } BuildThis(BuildingID Bid, const CGTownInstance * tid) - : CGoal(Goals::BUILD_STRUCTURE) + : ElementarGoal(Goals::BUILD_STRUCTURE) { bid = Bid; town = tid; priority = 1; } BuildThis(BuildingID Bid) - : CGoal(Goals::BUILD_STRUCTURE) + : ElementarGoal(Goals::BUILD_STRUCTURE) { bid = Bid; priority = 1; } virtual bool operator==(const BuildThis & other) const override; - virtual std::string name() const override; + virtual std::string toString() const override; }; } diff --git a/AI/Nullkiller/Goals/BuyArmy.cpp b/AI/Nullkiller/Goals/BuyArmy.cpp index fb644e294..168c85e49 100644 --- a/AI/Nullkiller/Goals/BuyArmy.cpp +++ b/AI/Nullkiller/Goals/BuyArmy.cpp @@ -25,7 +25,7 @@ bool BuyArmy::operator==(const BuyArmy & other) const return town == other.town && objid == other.objid; } -TSubgoal BuyArmy::whatToDoToAchieve() +std::string BuyArmy::toString() const { - return iAmElementar(); + return "Buy army at " + town->name; } \ No newline at end of file diff --git a/AI/Nullkiller/Goals/BuyArmy.h b/AI/Nullkiller/Goals/BuyArmy.h index 1c7fc356c..9fffbae32 100644 --- a/AI/Nullkiller/Goals/BuyArmy.h +++ b/AI/Nullkiller/Goals/BuyArmy.h @@ -17,23 +17,24 @@ class FuzzyHelper; namespace Goals { - class DLL_EXPORT BuyArmy : public CGoal + class DLL_EXPORT BuyArmy : public ElementarGoal { private: BuyArmy() - : CGoal(Goals::BUY_ARMY) + : ElementarGoal(Goals::BUY_ARMY) { } public: BuyArmy(const CGTownInstance * Town, int val) - : CGoal(Goals::BUY_ARMY) + : ElementarGoal(Goals::BUY_ARMY) { town = Town; //where to buy this army value = val; //expressed in AI unit strength priority = 3;//TODO: evaluate? } - TSubgoal whatToDoToAchieve() override; virtual bool operator==(const BuyArmy & other) const override; + + virtual std::string toString() const override; }; } diff --git a/AI/Nullkiller/Goals/CGoal.h b/AI/Nullkiller/Goals/CGoal.h index acc291d13..bd5f17d72 100644 --- a/AI/Nullkiller/Goals/CGoal.h +++ b/AI/Nullkiller/Goals/CGoal.h @@ -23,9 +23,8 @@ namespace Goals public: CGoal(EGoals goal = INVALID) : AbstractGoal(goal) { - priority = 0; isElementar = false; - isAbstract = false; + isAbstract = true; value = 0; aid = -1; objid = -1; @@ -36,7 +35,6 @@ namespace Goals OSETTER(bool, isElementar) OSETTER(bool, isAbstract) - OSETTER(float, priority) OSETTER(int, value) OSETTER(int, resID) OSETTER(int, objid) @@ -46,11 +44,6 @@ namespace Goals OSETTER(CGTownInstance *, town) OSETTER(int, bid) - void accept(VCAI * ai) override - { - ai->tryRealize(static_cast(*this)); //casting enforces template instantiation - } - CGoal * clone() const override { return new T(static_cast(*this)); //casting enforces template instantiation @@ -80,5 +73,65 @@ namespace Goals } virtual bool operator==(const T & other) const = 0; + + virtual TGoalVec decompose() const override + { + return {decomposeSingle()}; + } + + protected: + virtual TSubgoal decomposeSingle() const + { + return sptr(Invalid()); + } + }; + + template class DLL_EXPORT ElementarGoal : public CGoal, public ITask + { + public: + ElementarGoal(EGoals goal = INVALID) : CGoal(goal) + { + priority = 0; + isElementar = true; + isAbstract = false; + } + + ///Visitor pattern + //TODO: make accept work for std::shared_ptr... somehow + virtual void accept(VCAI * ai) override //unhandled goal will report standard error + { + ai->tryRealize(*this); + } + + T & setpriority(float p) + { + priority = p; + + return *((T *)this); + } + }; + + class DLL_EXPORT Invalid : public ElementarGoal + { + public: + Invalid() + : ElementarGoal(Goals::INVALID) + { + priority = -1; + } + TGoalVec decompose() const override + { + return TGoalVec(); + } + + virtual bool operator==(const Invalid & other) const override + { + return true; + } + + virtual std::string toString() const override + { + return "Invalid"; + } }; } diff --git a/AI/Nullkiller/Goals/CollectRes.cpp b/AI/Nullkiller/Goals/CollectRes.cpp index c134783ba..7c0553e38 100644 --- a/AI/Nullkiller/Goals/CollectRes.cpp +++ b/AI/Nullkiller/Goals/CollectRes.cpp @@ -29,10 +29,10 @@ bool CollectRes::operator==(const CollectRes & other) const return resID == other.resID; } -TGoalVec CollectRes::getAllPossibleSubgoals() -{ - TGoalVec ret; - +//TGoalVec CollectRes::getAllPossibleSubgoals() +//{ +// TGoalVec ret; +// //auto givesResource = [this](const CGObjectInstance * obj) -> bool //{ // //TODO: move this logic to object side @@ -107,18 +107,18 @@ TGoalVec CollectRes::getAllPossibleSubgoals() // vstd::concatenate(ret, waysToGo); // } //} - return ret; -} +// return ret; +//} -TSubgoal CollectRes::whatToDoToAchieve() -{ - auto goals = getAllPossibleSubgoals(); - auto trade = whatToDoToTrade(); - if (!trade->invalid()) - goals.push_back(trade); - - return sptr(Invalid()); //we can always do that -} +//TSubgoal CollectRes::whatToDoToAchieve() +//{ +// auto goals = getAllPossibleSubgoals(); +// auto trade = whatToDoToTrade(); +// if (!trade->invalid()) +// goals.push_back(trade); +// +// return sptr(Invalid()); //we can always do that +//} TSubgoal CollectRes::whatToDoToTrade() { diff --git a/AI/Nullkiller/Goals/CollectRes.h b/AI/Nullkiller/Goals/CollectRes.h index 0708d9557..9bc942b83 100644 --- a/AI/Nullkiller/Goals/CollectRes.h +++ b/AI/Nullkiller/Goals/CollectRes.h @@ -29,10 +29,9 @@ namespace Goals { resID = rid; value = val; - priority = 2; } - TGoalVec getAllPossibleSubgoals() override; - TSubgoal whatToDoToAchieve() override; + /*TGoalVec getAllPossibleSubgoals() override; + TSubgoal whatToDoToAchieve() override;*/ TSubgoal whatToDoToTrade(); virtual bool operator==(const CollectRes & other) const override; }; diff --git a/AI/Nullkiller/Goals/CompleteQuest.cpp b/AI/Nullkiller/Goals/CompleteQuest.cpp deleted file mode 100644 index c0ffa71e1..000000000 --- a/AI/Nullkiller/Goals/CompleteQuest.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* -* CompleteQuest.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 "Goals.h" -#include "../VCAI.h" -#include "../FuzzyHelper.h" -#include "../AIhelper.h" -#include "../../../lib/mapping/CMap.h" //for victory conditions -#include "../../../lib/CPathfinder.h" - -extern boost::thread_specific_ptr cb; -extern boost::thread_specific_ptr ai; -extern FuzzyHelper * fh; - -using namespace Goals; - -bool CompleteQuest::operator==(const CompleteQuest & other) const -{ - return q.quest->qid == other.q.quest->qid; -} - -TGoalVec CompleteQuest::getAllPossibleSubgoals() -{ - TGoalVec solutions; - - if(q.quest->missionType && q.quest->progress != CQuest::COMPLETE) - { - logAi->debug("Trying to realize quest: %s", questToString()); - - switch(q.quest->missionType) - { - case CQuest::MISSION_ART: - return missionArt(); - - case CQuest::MISSION_HERO: - return missionHero(); - - case CQuest::MISSION_ARMY: - return missionArmy(); - - case CQuest::MISSION_RESOURCES: - return missionResources(); - - case CQuest::MISSION_KILL_HERO: - case CQuest::MISSION_KILL_CREATURE: - return missionDestroyObj(); - - case CQuest::MISSION_PRIMARY_STAT: - return missionIncreasePrimaryStat(); - - case CQuest::MISSION_LEVEL: - return missionLevel(); - - case CQuest::MISSION_PLAYER: - if(ai->playerID.getNum() != q.quest->m13489val) - logAi->debug("Can't be player of color %d", q.quest->m13489val); - - break; - - case CQuest::MISSION_KEYMASTER: - return missionKeymaster(); - - } //end of switch - } - - return TGoalVec(); -} - -TSubgoal CompleteQuest::whatToDoToAchieve() -{ - if(q.quest->missionType == CQuest::MISSION_NONE) - { - throw cannotFulfillGoalException("Can not complete inactive quest"); - } - - TGoalVec solutions = getAllPossibleSubgoals(); - - throw cannotFulfillGoalException("Can not complete quest " + questToString()); -/* - TSubgoal result = fh->chooseSolution(solutions); - - logAi->trace( - "Returning %s, tile: %s, objid: %d, hero: %s", - result->name(), - result->tile.toString(), - result->objid, - result->hero.validAndSet() ? result->hero->name : "not specified"); - - return result;*/ -} - -std::string CompleteQuest::name() const -{ - return "CompleteQuest"; -} - -std::string CompleteQuest::questToString() const -{ - if(q.quest->missionType == CQuest::MISSION_NONE) - return "inactive quest"; - - MetaString ms; - q.quest->getRolloverText(ms, false); - - return ms.toString(); -} - -TGoalVec CompleteQuest::tryCompleteQuest() const -{ - TGoalVec solutions; - - auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities? - - for(auto hero : heroes) - { - if(q.quest->checkQuest(hero)) - { - //vstd::concatenate(solutions, ai->ah->howToVisitObj(hero, ObjectIdRef(q.obj->id))); - } - } - - return solutions; -} - -TGoalVec CompleteQuest::missionArt() const -{ - TGoalVec solutions = tryCompleteQuest(); - - if(!solutions.empty()) - return solutions; - - for(auto art : q.quest->m5arts) - { - solutions.push_back(sptr(GetArtOfType(art))); //TODO: transport? - } - - return solutions; -} - -TGoalVec CompleteQuest::missionHero() const -{ - TGoalVec solutions = tryCompleteQuest(); - - if(solutions.empty()) - { - //rule of a thumb - quest heroes usually are locked in prisons - solutions.push_back(sptr(FindObj(Obj::PRISON))); - } - - return solutions; -} - -TGoalVec CompleteQuest::missionArmy() const -{ - TGoalVec solutions = tryCompleteQuest(); - - if(!solutions.empty()) - return solutions; - - for(auto creature : q.quest->m6creatures) - { - solutions.push_back(sptr(GatherTroops(creature.type->idNumber, creature.count))); - } - - return solutions; -} - -TGoalVec CompleteQuest::missionIncreasePrimaryStat() const -{ - TGoalVec solutions = tryCompleteQuest(); - - if(solutions.empty()) - { - for(int i = 0; i < q.quest->m2stats.size(); ++i) - { - // TODO: library, school and other boost objects - logAi->debug("Don't know how to increase primary stat %d", i); - } - } - - return solutions; -} - -TGoalVec CompleteQuest::missionLevel() const -{ - TGoalVec solutions = tryCompleteQuest(); - - if(solutions.empty()) - { - logAi->debug("Don't know how to reach hero level %d", q.quest->m13489val); - } - - return solutions; -} - -TGoalVec CompleteQuest::missionKeymaster() const -{ - TGoalVec solutions = tryCompleteQuest(); - - if(solutions.empty()) - { - solutions.push_back(sptr(Goals::FindObj(Obj::KEYMASTER, q.obj->subID))); - } - - return solutions; -} - -TGoalVec CompleteQuest::missionResources() const -{ - TGoalVec solutions; - - auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities? - - if(heroes.size()) - { - if(q.quest->checkQuest(heroes.front())) //it doesn't matter which hero it is - { - return solutions;// ai->ah->howToVisitObj(q.obj); - } - else - { - for(int i = 0; i < q.quest->m7resources.size(); ++i) - { - if(q.quest->m7resources[i]) - solutions.push_back(sptr(CollectRes(i, q.quest->m7resources[i]))); - } - } - } - else - { - solutions.push_back(sptr(Goals::RecruitHero())); //FIXME: checkQuest requires any hero belonging to player :( - } - - return solutions; -} - -TGoalVec CompleteQuest::missionDestroyObj() const -{ - TGoalVec solutions; - - auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val); - - if(!obj) - return solutions;// ai->ah->howToVisitObj(q.obj); - - if(obj->ID == Obj::HERO) - { - auto relations = cb->getPlayerRelations(ai->playerID, obj->tempOwner); - - if(relations == PlayerRelations::SAME_PLAYER) - { - auto heroToProtect = cb->getHero(obj->id); - - //solutions.push_back(sptr(GatherArmy().sethero(heroToProtect))); - } - else if(relations == PlayerRelations::ENEMIES) - { - //solutions = ai->ah->howToVisitObj(obj); - } - } - - return solutions; -} \ No newline at end of file diff --git a/AI/Nullkiller/Goals/CompleteQuest.h b/AI/Nullkiller/Goals/CompleteQuest.h deleted file mode 100644 index 41b7e5fce..000000000 --- a/AI/Nullkiller/Goals/CompleteQuest.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -* CompleteQuest.h, 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 -* -*/ -#pragma once - -#include "CGoal.h" -#include "../../../lib/VCMI_Lib.h" - -namespace Goals -{ - class DLL_EXPORT CompleteQuest : public CGoal - { - private: - const QuestInfo q; - - public: - CompleteQuest(const QuestInfo quest) - : CGoal(Goals::COMPLETE_QUEST), q(quest) - { - } - - TGoalVec getAllPossibleSubgoals() override; - TSubgoal whatToDoToAchieve() override; - std::string name() const override; - virtual bool operator==(const CompleteQuest & other) const override; - - private: - TGoalVec tryCompleteQuest() const; - TGoalVec missionArt() const; - TGoalVec missionHero() const; - TGoalVec missionArmy() const; - TGoalVec missionResources() const; - TGoalVec missionDestroyObj() const; - TGoalVec missionIncreasePrimaryStat() const; - TGoalVec missionLevel() const; - TGoalVec missionKeymaster() const; - std::string questToString() const; - }; -} diff --git a/AI/Nullkiller/Goals/DigAtTile.cpp b/AI/Nullkiller/Goals/DigAtTile.cpp index 230d62a5e..ba5cf33df 100644 --- a/AI/Nullkiller/Goals/DigAtTile.cpp +++ b/AI/Nullkiller/Goals/DigAtTile.cpp @@ -24,16 +24,16 @@ bool DigAtTile::operator==(const DigAtTile & other) const { return other.hero.h == hero.h && other.tile == tile; } - -TSubgoal DigAtTile::whatToDoToAchieve() -{ - const CGObjectInstance * firstObj = vstd::frontOrNull(cb->getVisitableObjs(tile)); - if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest - { - const CGHeroInstance * h = dynamic_cast(firstObj); - sethero(h).setisElementar(true); - return sptr(*this); - } - - return sptr(VisitTile(tile)); -} +// +//TSubgoal DigAtTile::decomposeSingle() const +//{ +// const CGObjectInstance * firstObj = vstd::frontOrNull(cb->getVisitableObjs(tile)); +// if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest +// { +// const CGHeroInstance * h = dynamic_cast(firstObj); +// sethero(h).setisElementar(true); +// return sptr(*this); +// } +// +// return sptr(VisitTile(tile)); +//} diff --git a/AI/Nullkiller/Goals/DigAtTile.h b/AI/Nullkiller/Goals/DigAtTile.h index 963306539..79f4b1bf1 100644 --- a/AI/Nullkiller/Goals/DigAtTile.h +++ b/AI/Nullkiller/Goals/DigAtTile.h @@ -29,13 +29,10 @@ namespace Goals : CGoal(Goals::DIG_AT_TILE) { tile = Tile; - priority = 20; } - TGoalVec getAllPossibleSubgoals() override - { - return TGoalVec(); - } - TSubgoal whatToDoToAchieve() override; virtual bool operator==(const DigAtTile & other) const override; + + private: + //TSubgoal decomposeSingle() const override; }; } diff --git a/AI/Nullkiller/Goals/DismissHero.cpp b/AI/Nullkiller/Goals/DismissHero.cpp index f50ff8c76..1b73b8e0e 100644 --- a/AI/Nullkiller/Goals/DismissHero.cpp +++ b/AI/Nullkiller/Goals/DismissHero.cpp @@ -26,14 +26,6 @@ bool DismissHero::operator==(const DismissHero & other) const return hero.h == other.hero.h; } -TSubgoal DismissHero::whatToDoToAchieve() -{ - if(!hero.validAndSet()) - throw cannotFulfillGoalException("Invalid hero!"); - - return iAmElementar(); -} - void DismissHero::accept(VCAI * ai) { if(!hero.validAndSet()) @@ -44,7 +36,7 @@ void DismissHero::accept(VCAI * ai) throw goalFulfilledException(sptr(*this)); } -std::string DismissHero::name() const +std::string DismissHero::toString() const { return "DismissHero " + hero.name; } diff --git a/AI/Nullkiller/Goals/DismissHero.h b/AI/Nullkiller/Goals/DismissHero.h index 611c4e246..2b1de0806 100644 --- a/AI/Nullkiller/Goals/DismissHero.h +++ b/AI/Nullkiller/Goals/DismissHero.h @@ -13,23 +13,17 @@ namespace Goals { - class DLL_EXPORT DismissHero : public CGoal + class DLL_EXPORT DismissHero : public ElementarGoal { public: DismissHero(HeroPtr hero) - : CGoal(Goals::DISMISS_HERO) + : ElementarGoal(Goals::DISMISS_HERO) { sethero(hero); } - TGoalVec getAllPossibleSubgoals() override - { - return TGoalVec(); - } - - TSubgoal whatToDoToAchieve() override; void accept(VCAI * ai) override; - std::string name() const override; + std::string toString() const override; virtual bool operator==(const DismissHero & other) const override; }; } diff --git a/AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp b/AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp index d2c1929a5..72c56141f 100644 --- a/AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp +++ b/AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp @@ -27,11 +27,11 @@ ExchangeSwapTownHeroes::ExchangeSwapTownHeroes( const CGTownInstance * town, const CGHeroInstance * garrisonHero, HeroLockedReason lockingReason) - :CGoal(Goals::EXCHANGE_SWAP_TOWN_HEROES), town(town), garrisonHero(garrisonHero), lockingReason(lockingReason) + :ElementarGoal(Goals::EXCHANGE_SWAP_TOWN_HEROES), town(town), garrisonHero(garrisonHero), lockingReason(lockingReason) { } -std::string ExchangeSwapTownHeroes::name() const +std::string ExchangeSwapTownHeroes::toString() const { return "Exchange and swap heroes of " + town->name; } @@ -41,11 +41,6 @@ bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) co return town == other.town; } -TSubgoal ExchangeSwapTownHeroes::whatToDoToAchieve() -{ - return iAmElementar(); -} - void ExchangeSwapTownHeroes::accept(VCAI * ai) { if(!garrisonHero) diff --git a/AI/Nullkiller/Goals/ExchangeSwapTownHeroes.h b/AI/Nullkiller/Goals/ExchangeSwapTownHeroes.h index 19374ec55..9c632b281 100644 --- a/AI/Nullkiller/Goals/ExchangeSwapTownHeroes.h +++ b/AI/Nullkiller/Goals/ExchangeSwapTownHeroes.h @@ -14,7 +14,7 @@ namespace Goals { - class DLL_EXPORT ExchangeSwapTownHeroes : public CGoal + class DLL_EXPORT ExchangeSwapTownHeroes : public ElementarGoal { private: const CGTownInstance * town; @@ -27,14 +27,8 @@ namespace Goals const CGHeroInstance * garrisonHero = nullptr, HeroLockedReason lockingReason = HeroLockedReason::NOT_LOCKED); - TGoalVec getAllPossibleSubgoals() override - { - return TGoalVec(); - } - - TSubgoal whatToDoToAchieve() override; void accept(VCAI * ai) override; - std::string name() const override; + std::string toString() const override; virtual bool operator==(const ExchangeSwapTownHeroes & other) const override; }; } diff --git a/AI/Nullkiller/Goals/ExecuteHeroChain.cpp b/AI/Nullkiller/Goals/ExecuteHeroChain.cpp index fae95a77d..6cc229d75 100644 --- a/AI/Nullkiller/Goals/ExecuteHeroChain.cpp +++ b/AI/Nullkiller/Goals/ExecuteHeroChain.cpp @@ -24,23 +24,12 @@ extern FuzzyHelper * fh; using namespace Goals; ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj) - :CGoal(Goals::EXECUTE_HERO_CHAIN), chainPath(path) + :ElementarGoal(Goals::EXECUTE_HERO_CHAIN), chainPath(path) { - evaluationContext.danger = path.getTotalDanger(); - evaluationContext.movementCost = path.movementCost(); - evaluationContext.armyLoss = path.getTotalArmyLoss(); - evaluationContext.heroStrength = path.getHeroStrength(); hero = path.targetHero; tile = path.targetTile(); - for(auto & node : path.nodes) - { - auto role = ai->ah->getHeroRole(node.targetHero); - - evaluationContext.movementCostByRole[role] += node.cost; - } - if(obj) { objid = obj->id.getNum(); @@ -57,15 +46,12 @@ bool ExecuteHeroChain::operator==(const ExecuteHeroChain & other) const return false; } -TSubgoal ExecuteHeroChain::whatToDoToAchieve() -{ - return iAmElementar(); -} - void ExecuteHeroChain::accept(VCAI * ai) { logAi->debug("Executing hero chain towards %s. Path %s", targetName, chainPath.toString()); + ai->nullkiller->setActive(chainPath.targetHero, tile); + std::set blockedIndexes; for(int i = chainPath.nodes.size() - 1; i >= 0; i--) @@ -89,7 +75,6 @@ void ExecuteHeroChain::accept(VCAI * ai) { if(hero->movement) { - ai->nullkiller->setActive(hero, node.coord); if(node.specialAction) @@ -98,6 +83,8 @@ void ExecuteHeroChain::accept(VCAI * ai) { auto specialGoal = node.specialAction->whatToDo(hero); + if(!specialGoal->isElementar) + specialGoal->accept(ai); } else @@ -135,7 +122,10 @@ void ExecuteHeroChain::accept(VCAI * ai) { try { - Goals::VisitTile(node.coord).sethero(hero).accept(ai); + if(moveHeroToTile(hero, node.coord)) + { + continue; + } } catch(cannotFulfillGoalException) { @@ -195,7 +185,19 @@ void ExecuteHeroChain::accept(VCAI * ai) } } -std::string ExecuteHeroChain::name() const +std::string ExecuteHeroChain::toString() const { return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->name; +} + +bool ExecuteHeroChain::moveHeroToTile(const CGHeroInstance * hero, const int3 & tile) +{ + if(g.tile == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2) + { + logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile.toString()); + + return true; + } + + return ai->moveHeroToTile(tile, hero); } \ No newline at end of file diff --git a/AI/Nullkiller/Goals/ExecuteHeroChain.h b/AI/Nullkiller/Goals/ExecuteHeroChain.h index 94bbc6289..67347d092 100644 --- a/AI/Nullkiller/Goals/ExecuteHeroChain.h +++ b/AI/Nullkiller/Goals/ExecuteHeroChain.h @@ -13,7 +13,7 @@ namespace Goals { - class DLL_EXPORT ExecuteHeroChain : public CGoal + class DLL_EXPORT ExecuteHeroChain : public ElementarGoal { private: AIPath chainPath; @@ -22,15 +22,13 @@ namespace Goals public: ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj = nullptr); - TGoalVec getAllPossibleSubgoals() override - { - return TGoalVec(); - } - - TSubgoal whatToDoToAchieve() override; + void accept(VCAI * ai) override; - std::string name() const override; + std::string toString() const override; virtual bool operator==(const ExecuteHeroChain & other) const override; const AIPath & getPath() const { return chainPath; } + + private: + bool moveHeroToTile(const CGHeroInstance * hero, const int3 & tile); }; } diff --git a/AI/Nullkiller/Goals/FindObj.cpp b/AI/Nullkiller/Goals/FindObj.cpp index 7780ded2e..41cc6f0f4 100644 --- a/AI/Nullkiller/Goals/FindObj.cpp +++ b/AI/Nullkiller/Goals/FindObj.cpp @@ -23,32 +23,32 @@ bool FindObj::operator==(const FindObj & other) const { return other.hero.h == hero.h && other.objid == objid; } - -TSubgoal FindObj::whatToDoToAchieve() -{ - const CGObjectInstance * o = nullptr; - if(resID > -1) //specified - { - for(const CGObjectInstance * obj : ai->visitableObjs) - { - if(obj->ID == objid && obj->subID == resID) - { - o = obj; - break; //TODO: consider multiple objects and choose best - } - } - } - else - { - for(const CGObjectInstance * obj : ai->visitableObjs) - { - if(obj->ID == objid) - { - o = obj; - break; //TODO: consider multiple objects and choose best - } - } - } - if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is - return sptr(VisitObj(o->id.getNum())); -} \ No newline at end of file +// +//TSubgoal FindObj::whatToDoToAchieve() +//{ +// const CGObjectInstance * o = nullptr; +// if(resID > -1) //specified +// { +// for(const CGObjectInstance * obj : ai->visitableObjs) +// { +// if(obj->ID == objid && obj->subID == resID) +// { +// o = obj; +// break; //TODO: consider multiple objects and choose best +// } +// } +// } +// else +// { +// for(const CGObjectInstance * obj : ai->visitableObjs) +// { +// if(obj->ID == objid) +// { +// o = obj; +// break; //TODO: consider multiple objects and choose best +// } +// } +// } +// if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is +// return sptr(VisitObj(o->id.getNum())); +//} \ No newline at end of file diff --git a/AI/Nullkiller/Goals/FindObj.h b/AI/Nullkiller/Goals/FindObj.h index 919f8e5e5..3b6c50a65 100644 --- a/AI/Nullkiller/Goals/FindObj.h +++ b/AI/Nullkiller/Goals/FindObj.h @@ -27,20 +27,16 @@ namespace Goals { objid = ID; resID = -1; //subid unspecified - priority = 1; } FindObj(int ID, int subID) : CGoal(Goals::FIND_OBJ) { objid = ID; resID = subID; - priority = 1; } - TGoalVec getAllPossibleSubgoals() override - { - return TGoalVec(); - } - TSubgoal whatToDoToAchieve() override; virtual bool operator==(const FindObj & other) const override; + + private: + //TSubgoal decomposeSingle() const override; }; } \ No newline at end of file diff --git a/AI/Nullkiller/Goals/GatherTroops.cpp b/AI/Nullkiller/Goals/GatherTroops.cpp index b27350fc6..c34dcfc6d 100644 --- a/AI/Nullkiller/Goals/GatherTroops.cpp +++ b/AI/Nullkiller/Goals/GatherTroops.cpp @@ -43,30 +43,30 @@ int GatherTroops::getCreaturesCount(const CArmedInstance * army) return count; } +// +//TSubgoal GatherTroops::whatToDoToAchieve() +//{ +// logAi->trace("Entering GatherTroops::whatToDoToAchieve"); +// +// auto heroes = cb->getHeroesInfo(true); +// +// for(auto hero : heroes) +// { +// if(getCreaturesCount(hero) >= this->value) +// { +// logAi->trace("Completing GATHER_TROOPS by hero %s", hero->name); +// +// throw goalFulfilledException(sptr(*this)); +// } +// } +// +// return sptr(Invalid()); +//} -TSubgoal GatherTroops::whatToDoToAchieve() -{ - logAi->trace("Entering GatherTroops::whatToDoToAchieve"); - - auto heroes = cb->getHeroesInfo(true); - - for(auto hero : heroes) - { - if(getCreaturesCount(hero) >= this->value) - { - logAi->trace("Completing GATHER_TROOPS by hero %s", hero->name); - - throw goalFulfilledException(sptr(*this)); - } - } - - return sptr(Invalid()); -} - - -TGoalVec GatherTroops::getAllPossibleSubgoals() -{ - TGoalVec solutions; +// +//TGoalVec GatherTroops::getAllPossibleSubgoals() +//{ +// TGoalVec solutions; //for(const CGTownInstance * t : cb->getTownsInfo()) //{ @@ -136,6 +136,6 @@ TGoalVec GatherTroops::getAllPossibleSubgoals() // return goal->hero && !goal->hero->getSlotFor(creID).validSlot() && !goal->hero->getFreeSlot().validSlot(); //}); - return solutions; - //TODO: exchange troops between heroes -} +// return solutions; +// //TODO: exchange troops between heroes +//} diff --git a/AI/Nullkiller/Goals/GatherTroops.h b/AI/Nullkiller/Goals/GatherTroops.h index 960512289..7d95a3fb0 100644 --- a/AI/Nullkiller/Goals/GatherTroops.h +++ b/AI/Nullkiller/Goals/GatherTroops.h @@ -23,17 +23,13 @@ namespace Goals GatherTroops() : CGoal(Goals::GATHER_TROOPS) { - priority = 2; } GatherTroops(int type, int val) : CGoal(Goals::GATHER_TROOPS) { objid = type; value = val; - priority = 2; } - TGoalVec getAllPossibleSubgoals() override; - TSubgoal whatToDoToAchieve() override; virtual bool operator==(const GatherTroops & other) const override; private: diff --git a/AI/Nullkiller/Goals/GetArtOfType.cpp b/AI/Nullkiller/Goals/GetArtOfType.cpp index 5195796ec..ed4b61077 100644 --- a/AI/Nullkiller/Goals/GetArtOfType.cpp +++ b/AI/Nullkiller/Goals/GetArtOfType.cpp @@ -24,8 +24,8 @@ bool GetArtOfType::operator==(const GetArtOfType & other) const { return other.hero.h == hero.h && other.objid == objid; } - -TSubgoal GetArtOfType::whatToDoToAchieve() -{ - return sptr(FindObj(Obj::ARTIFACT, aid)); -} \ No newline at end of file +// +//TSubgoal GetArtOfType::whatToDoToAchieve() +//{ +// return sptr(FindObj(Obj::ARTIFACT, aid)); +//} \ No newline at end of file diff --git a/AI/Nullkiller/Goals/GetArtOfType.h b/AI/Nullkiller/Goals/GetArtOfType.h index ba31c2a56..c20490c30 100644 --- a/AI/Nullkiller/Goals/GetArtOfType.h +++ b/AI/Nullkiller/Goals/GetArtOfType.h @@ -28,13 +28,7 @@ namespace Goals : CGoal(Goals::GET_ART_TYPE) { aid = type; - priority = 2; } - TGoalVec getAllPossibleSubgoals() override - { - return TGoalVec(); - } - TSubgoal whatToDoToAchieve() override; virtual bool operator==(const GetArtOfType & other) const override; }; } diff --git a/AI/Nullkiller/Goals/Goals.h b/AI/Nullkiller/Goals/Goals.h index 16a31d44d..c4e694e0d 100644 --- a/AI/Nullkiller/Goals/Goals.h +++ b/AI/Nullkiller/Goals/Goals.h @@ -13,10 +13,6 @@ #include "Invalid.h" #include "BuildBoat.h" #include "BuildThis.h" -#include "Win.h" -#include "VisitObj.h" -#include "VisitTile.h" -#include "VisitHero.h" #include "BuyArmy.h" #include "GatherTroops.h" #include "Trade.h" @@ -25,5 +21,4 @@ #include "GetArtOfType.h" #include "DigAtTile.h" #include "FindObj.h" -#include "CompleteQuest.h" #include "AdventureSpellCast.h" \ No newline at end of file diff --git a/AI/Nullkiller/Goals/Invalid.h b/AI/Nullkiller/Goals/Invalid.h index cd77ac929..a67d80ed5 100644 --- a/AI/Nullkiller/Goals/Invalid.h +++ b/AI/Nullkiller/Goals/Invalid.h @@ -16,26 +16,4 @@ class VCAI; namespace Goals { - class DLL_EXPORT Invalid : public CGoal - { - public: - Invalid() - : CGoal(Goals::INVALID) - { - priority = -1e10; - } - TGoalVec getAllPossibleSubgoals() override - { - return TGoalVec(); - } - TSubgoal whatToDoToAchieve() override - { - return iAmElementar(); - } - - virtual bool operator==(const Invalid & other) const override - { - return true; - } - }; } diff --git a/AI/Nullkiller/Goals/RecruitHero.cpp b/AI/Nullkiller/Goals/RecruitHero.cpp index 58d0911dc..3fe5b21c8 100644 --- a/AI/Nullkiller/Goals/RecruitHero.cpp +++ b/AI/Nullkiller/Goals/RecruitHero.cpp @@ -23,3 +23,8 @@ extern boost::thread_specific_ptr ai; extern FuzzyHelper * fh; using namespace Goals; + +std::string RecruitHero::toString() const +{ + return "Recruit hero at " + town->name; +} \ No newline at end of file diff --git a/AI/Nullkiller/Goals/RecruitHero.h b/AI/Nullkiller/Goals/RecruitHero.h index 3563f6aa1..5c1fad992 100644 --- a/AI/Nullkiller/Goals/RecruitHero.h +++ b/AI/Nullkiller/Goals/RecruitHero.h @@ -17,18 +17,27 @@ class FuzzyHelper; namespace Goals { - class DLL_EXPORT RecruitHero : public CGoal + class DLL_EXPORT RecruitHero : public ElementarGoal { public: - RecruitHero() - : CGoal(Goals::RECRUIT_HERO) + RecruitHero(const CGTownInstance * townWithTavern, const CGHeroInstance * heroToBuy) + : RecruitHero(townWithTavern) + { + objid = heroToBuy->id.getNum(); + } + + RecruitHero(const CGTownInstance * townWithTavern) + : ElementarGoal(Goals::RECRUIT_HERO) { priority = 1; + town = townWithTavern; } virtual bool operator==(const RecruitHero & other) const override { return true; } + + virtual std::string toString() const override; }; } diff --git a/AI/Nullkiller/Goals/Trade.cpp b/AI/Nullkiller/Goals/Trade.cpp index c4fa8f05f..e97e7c363 100644 --- a/AI/Nullkiller/Goals/Trade.cpp +++ b/AI/Nullkiller/Goals/Trade.cpp @@ -15,9 +15,4 @@ using namespace Goals; bool Trade::operator==(const Trade & other) const { return resID == other.resID; -} - -TSubgoal Trade::whatToDoToAchieve() -{ - return iAmElementar(); -} +} \ No newline at end of file diff --git a/AI/Nullkiller/Goals/Trade.h b/AI/Nullkiller/Goals/Trade.h index c8da46fc2..d9b41ed1b 100644 --- a/AI/Nullkiller/Goals/Trade.h +++ b/AI/Nullkiller/Goals/Trade.h @@ -30,9 +30,7 @@ namespace Goals resID = rid; value = val; objid = Objid; - priority = 3; //trading is instant, but picking resources is free } - TSubgoal whatToDoToAchieve() override; virtual bool operator==(const Trade & other) const override; }; } diff --git a/AI/Nullkiller/Goals/VisitHero.h b/AI/Nullkiller/Goals/VisitHero.h index 584cd6b7b..d172c88dd 100644 --- a/AI/Nullkiller/Goals/VisitHero.h +++ b/AI/Nullkiller/Goals/VisitHero.h @@ -9,6 +9,8 @@ */ #pragma once +#error not used + #include "CGoal.h" struct HeroPtr; @@ -28,7 +30,6 @@ namespace Goals : CGoal(Goals::VISIT_HERO) { objid = hid; - priority = 4; } virtual bool operator==(const VisitHero & other) const override; }; diff --git a/AI/Nullkiller/Goals/VisitObj.cpp b/AI/Nullkiller/Goals/VisitObj.cpp index fc99ca41d..af2db3674 100644 --- a/AI/Nullkiller/Goals/VisitObj.cpp +++ b/AI/Nullkiller/Goals/VisitObj.cpp @@ -38,6 +38,4 @@ VisitObj::VisitObj(int Objid) tile = obj->visitablePos(); else logAi->error("VisitObj constructed with invalid object instance %d", Objid); - - priority = 3; } \ No newline at end of file diff --git a/AI/Nullkiller/Goals/VisitObj.h b/AI/Nullkiller/Goals/VisitObj.h index 4baeb8e44..43b34283e 100644 --- a/AI/Nullkiller/Goals/VisitObj.h +++ b/AI/Nullkiller/Goals/VisitObj.h @@ -9,6 +9,8 @@ */ #pragma once +#error not used + #include "CGoal.h" struct HeroPtr; diff --git a/AI/Nullkiller/Goals/VisitTile.h b/AI/Nullkiller/Goals/VisitTile.h index fc64248fa..88ea45b1f 100644 --- a/AI/Nullkiller/Goals/VisitTile.h +++ b/AI/Nullkiller/Goals/VisitTile.h @@ -9,6 +9,8 @@ */ #pragma once +#error not used + #include "CGoal.h" struct HeroPtr; @@ -27,7 +29,6 @@ namespace Goals : CGoal(Goals::VISIT_TILE) { tile = Tile; - priority = 5; } virtual bool operator==(const VisitTile & other) const override; }; diff --git a/AI/Nullkiller/VCAI.cpp b/AI/Nullkiller/VCAI.cpp index 02d36c525..0dea4eade 100644 --- a/AI/Nullkiller/VCAI.cpp +++ b/AI/Nullkiller/VCAI.cpp @@ -299,15 +299,6 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q requestActionASAP([=]() { - float goalpriority1 = 0, goalpriority2 = 0; - - auto firstGoal = getGoal(firstHero); - if(firstGoal->goalType == Goals::GATHER_ARMY) - goalpriority1 = firstGoal->priority; - auto secondGoal = getGoal(secondHero); - if(secondGoal->goalType == Goals::GATHER_ARMY) - goalpriority2 = secondGoal->priority; - auto transferFrom2to1 = [this](const CGHeroInstance * h1, const CGHeroInstance * h2) -> void { this->pickBestCreatures(h1, h2); @@ -320,28 +311,13 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q { logAi->debug("Heroes owned by different players. Do not exchange army or artifacts."); } - else if(nullkiller) + else { if(nullkiller->isActive(firstHero)) transferFrom2to1(secondHero, firstHero); else transferFrom2to1(firstHero, secondHero); } - else if(goalpriority1 > goalpriority2) - { - transferFrom2to1(firstHero, secondHero); - } - else if(goalpriority1 < goalpriority2) - { - transferFrom2to1(secondHero, firstHero); - } - else //regular criteria - { - if(firstHero->getFightingStrength() > secondHero->getFightingStrength() && ah->canGetArmy(firstHero, secondHero)) - transferFrom2to1(firstHero, secondHero); - else if(ah->canGetArmy(secondHero, firstHero)) - transferFrom2to1(secondHero, firstHero); - } answerQuery(query, 0); }); @@ -1403,7 +1379,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) if(path.nodes.empty()) { logAi->error("Hero %s cannot reach %s.", h->name, dst.toString()); - throw goalFulfilledException(sptr(Goals::VisitTile(dst).sethero(h))); + return true; } int i = path.nodes.size() - 1; @@ -1568,11 +1544,6 @@ void VCAI::buildStructure(const CGTownInstance * t, BuildingID building) cb->buildBuilding(t, building); //just do this; } -void VCAI::tryRealize(Goals::Explore & g) -{ - throw cannotFulfillGoalException("EXPLORE is not an elementar goal!"); -} - void VCAI::tryRealize(Goals::RecruitHero & g) { const CGTownInstance * t = g.town; @@ -1591,56 +1562,6 @@ void VCAI::tryRealize(Goals::RecruitHero & g) } } -void VCAI::tryRealize(Goals::VisitTile & g) -{ - if(!g.hero->movement) - throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!"); - if(g.tile == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2) - { - logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile.toString()); - throw goalFulfilledException(sptr(g)); - } - if(ai->moveHeroToTile(g.tile, g.hero.get())) - { - throw goalFulfilledException(sptr(g)); - } -} - -void VCAI::tryRealize(Goals::VisitObj & g) -{ - auto position = g.tile; - if(!g.hero->movement) - throw cannotFulfillGoalException("Cannot visit object: hero is out of MPs!"); - if(position == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2) - { - logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile.toString()); - throw goalFulfilledException(sptr(g)); - } - if(ai->moveHeroToTile(position, g.hero.get())) - { - throw goalFulfilledException(sptr(g)); - } -} - -void VCAI::tryRealize(Goals::VisitHero & g) -{ - if(!g.hero->movement) - throw cannotFulfillGoalException("Cannot visit target hero: hero is out of MPs!"); - - const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(g.objid)); - if(obj) - { - if(ai->moveHeroToTile(obj->visitablePos(), g.hero.get())) - { - throw goalFulfilledException(sptr(g)); - } - } - else - { - throw cannotFulfillGoalException("Cannot visit hero: object not found!"); - } -} - void VCAI::tryRealize(Goals::BuildThis & g) { auto b = BuildingID(g.bid); @@ -1773,7 +1694,7 @@ void VCAI::tryRealize(Goals::Invalid & g) void VCAI::tryRealize(Goals::AbstractGoal & g) { - logAi->debug("Attempting realizing goal with code %s", g.name()); + logAi->debug("Attempting realizing goal with code %s", g.toString()); throw cannotFulfillGoalException("Unknown type of goal !"); } @@ -1786,42 +1707,6 @@ const CGTownInstance * VCAI::findTownWithTavern() const return nullptr; } -Goals::TSubgoal VCAI::getGoal(HeroPtr h) const -{ - auto it = lockedHeroes.find(h); - if(it != lockedHeroes.end()) - return it->second; - else - return sptr(Goals::Invalid()); -} - - -std::vector VCAI::getUnblockedHeroes() const -{ - std::vector ret; - for(auto h : cb->getHeroesInfo()) - { - //&& !vstd::contains(lockedHeroes, h) - //at this point we assume heroes exhausted their locked goals - if(canAct(h)) - ret.push_back(h); - } - return ret; -} - -bool VCAI::canAct(HeroPtr h) const -{ - auto mission = lockedHeroes.find(h); - if(mission != lockedHeroes.end()) - { - //FIXME: I'm afraid there can be other conditions when heroes can act but not move :? - if(mission->second->goalType == Goals::DIG_AT_TILE && !mission->second->isElementar) - return false; - } - - return h->movement; -} - HeroPtr VCAI::primaryHero() const { auto hs = cb->getHeroesInfo(); @@ -1876,7 +1761,7 @@ void VCAI::recruitHero(const CGTownInstance * t, bool throwing) if(t->visitingHero) moveHeroToTile(t->visitablePos(), t->visitingHero.get()); - throw goalFulfilledException(sptr(Goals::RecruitHero().settown(t))); + throw goalFulfilledException(sptr(Goals::RecruitHero(t))); } else if(throwing) { diff --git a/AI/Nullkiller/VCAI.h b/AI/Nullkiller/VCAI.h index aba276163..4663972ba 100644 --- a/AI/Nullkiller/VCAI.h +++ b/AI/Nullkiller/VCAI.h @@ -117,11 +117,7 @@ public: virtual ~VCAI(); //TODO: use only smart pointers? - void tryRealize(Goals::Explore & g); void tryRealize(Goals::RecruitHero & g); - void tryRealize(Goals::VisitTile & g); - void tryRealize(Goals::VisitObj & g); - void tryRealize(Goals::VisitHero & g); void tryRealize(Goals::BuildThis & g); void tryRealize(Goals::DigAtTile & g); void tryRealize(Goals::Trade & g); @@ -241,9 +237,6 @@ public: const CGTownInstance * findTownWithTavern() const; bool canRecruitAnyHero(const CGTownInstance * t = NULL) const; - Goals::TSubgoal getGoal(HeroPtr h) const; - bool canAct(HeroPtr h) const; - std::vector getUnblockedHeroes() const; std::vector getMyHeroes() const; HeroPtr primaryHero() const; @@ -374,7 +367,7 @@ public: explicit goalFulfilledException(Goals::TSubgoal Goal) : goal(Goal) { - msg = goal->name(); + msg = goal->toString(); } virtual ~goalFulfilledException() throw ()