1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

Nullkiller: Try to join behavior and goal and see what come out of it.

This commit is contained in:
Andrii Danylchenko
2021-05-16 14:38:26 +03:00
committed by Andrii Danylchenko
parent af0dcf97c4
commit 223a52b3d1
64 changed files with 843 additions and 1191 deletions

View File

@@ -10,10 +10,10 @@
#pragma once #pragma once
#include "../VCAI.h" #include "../VCAI.h"
#error REMOVE THIS FILE
class Behavior class Behavior : public Goals::AbstractGoal
{ {
public: public:
virtual Goals::TGoalVec getTasks() = 0;
virtual std::string toString() const = 0; virtual std::string toString() const = 0;
}; };

View File

@@ -29,7 +29,7 @@ std::string BuildingBehavior::toString() const
return "Build"; return "Build";
} }
Goals::TGoalVec BuildingBehavior::getTasks() Goals::TGoalVec BuildingBehavior::decompose() const
{ {
Goals::TGoalVec tasks; Goals::TGoalVec tasks;

View File

@@ -10,17 +10,24 @@
#pragma once #pragma once
#include "lib/VCMI_Lib.h" #include "lib/VCMI_Lib.h"
#include "Behavior.h"
#include "../AIUtility.h" #include "../AIUtility.h"
#include "../Goals/CGoal.h"
class BuildingBehavior : public Behavior namespace Goals
{ {
public: class BuildingBehavior : public CGoal<BuildingBehavior>
{
public:
BuildingBehavior() BuildingBehavior()
{ {
} }
virtual Goals::TGoalVec getTasks() override; virtual Goals::TGoalVec decompose() const override;
virtual std::string toString() const override; virtual std::string toString() const override;
}; virtual bool operator==(const BuildingBehavior & other) const override
{
return true;
}
};
}

View File

@@ -29,7 +29,7 @@ std::string BuyArmyBehavior::toString() const
return "Buy army"; return "Buy army";
} }
Goals::TGoalVec BuyArmyBehavior::getTasks() Goals::TGoalVec BuyArmyBehavior::decompose() const
{ {
Goals::TGoalVec tasks; Goals::TGoalVec tasks;

View File

@@ -10,17 +10,23 @@
#pragma once #pragma once
#include "lib/VCMI_Lib.h" #include "lib/VCMI_Lib.h"
#include "Behavior.h"
#include "../AIUtility.h" #include "../AIUtility.h"
#include "../Goals/CGoal.h"
class BuyArmyBehavior : public Behavior namespace Goals
{ {
public: class BuyArmyBehavior : public CGoal<BuyArmyBehavior>
{
public:
BuyArmyBehavior() BuyArmyBehavior()
{ {
} }
virtual Goals::TGoalVec getTasks() override; virtual Goals::TGoalVec decompose() const override;
virtual std::string toString() const override; virtual std::string toString() const override;
}; virtual bool operator==(const BuyArmyBehavior & other) const override
{
return true;
}
};
}

View File

@@ -28,7 +28,7 @@ std::string CaptureObjectsBehavior::toString() const
return "Capture objects"; return "Capture objects";
} }
Goals::TGoalVec CaptureObjectsBehavior::getTasks() Goals::TGoalVec CaptureObjectsBehavior::decompose() const
{ {
Goals::TGoalVec tasks; Goals::TGoalVec tasks;
@@ -42,7 +42,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
for(auto objToVisit : objs) 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()); logAi->trace("Checking object %s, %s", objToVisit->getObjectName(), objToVisit->visitablePos().toString());
#endif #endif
@@ -55,19 +55,19 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj; std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
std::shared_ptr<ExecuteHeroChain> closestWay; std::shared_ptr<ExecuteHeroChain> closestWay;
#ifdef AI_TRACE_LEVEL >= 1 #if AI_TRACE_LEVEL >= 1
logAi->trace("Found %d paths", paths.size()); logAi->trace("Found %d paths", paths.size());
#endif #endif
for(auto & path : paths) for(auto & path : paths)
{ {
#ifdef AI_TRACE_LEVEL >= 2 #if AI_TRACE_LEVEL >= 2
logAi->trace("Path found %s", path.toString()); logAi->trace("Path found %s", path.toString());
#endif #endif
if(path.getFirstBlockedAction()) if(path.getFirstBlockedAction())
{ {
#ifdef AI_TRACE_LEVEL >= 2 #if AI_TRACE_LEVEL >= 2
// TODO: decomposition? // TODO: decomposition?
logAi->trace("Ignore path. Action is blocked."); logAi->trace("Ignore path. Action is blocked.");
#endif #endif
@@ -76,7 +76,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path)) 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()); logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
#endif #endif
continue; continue;
@@ -98,7 +98,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger); auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
#ifdef AI_TRACE_LEVEL >= 2 #if AI_TRACE_LEVEL >= 2
logAi->trace( logAi->trace(
"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld", "It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld",
isSafe ? "safe" : "not safe", isSafe ? "safe" : "not safe",

View File

@@ -10,16 +10,19 @@
#pragma once #pragma once
#include "lib/VCMI_Lib.h" #include "lib/VCMI_Lib.h"
#include "Behavior.h"
#include "../AIUtility.h" #include "../AIUtility.h"
#include "../Goals/CGoal.h"
class CaptureObjectsBehavior : public Behavior { namespace Goals
private: {
class CaptureObjectsBehavior : public CGoal<CaptureObjectsBehavior>
{
private:
std::vector<int> objectTypes; std::vector<int> objectTypes;
std::vector<int> objectSubTypes; std::vector<int> objectSubTypes;
std::vector<const CGObjectInstance *> objectsToCapture; std::vector<const CGObjectInstance *> objectsToCapture;
bool specificObjects; bool specificObjects;
public: public:
CaptureObjectsBehavior() CaptureObjectsBehavior()
{ {
objectTypes = std::vector<int>(); objectTypes = std::vector<int>();
@@ -39,22 +42,30 @@ public:
specificObjects = true; specificObjects = true;
} }
virtual Goals::TGoalVec getTasks() override; virtual Goals::TGoalVec decompose() const override;
virtual std::string toString() const override; virtual std::string toString() const override;
CaptureObjectsBehavior & ofType(int type) { CaptureObjectsBehavior & ofType(int type)
{
objectTypes.push_back(type); objectTypes.push_back(type);
return *this; return *this;
} }
CaptureObjectsBehavior & ofType(int type, int subType) { CaptureObjectsBehavior & ofType(int type, int subType)
{
objectTypes.push_back(type); objectTypes.push_back(type);
objectSubTypes.push_back(subType); objectSubTypes.push_back(subType);
return *this; return *this;
} }
private: virtual bool operator==(const CaptureObjectsBehavior & other) const override
bool shouldVisitObject(ObjectIdRef obj) const; {
}; return false;
}
private:
bool shouldVisitObject(ObjectIdRef obj) const;
};
}

View File

@@ -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<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> 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();
}

View File

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

View File

@@ -14,7 +14,6 @@
#include "../AIhelper.h" #include "../AIhelper.h"
#include "../AIUtility.h" #include "../AIUtility.h"
#include "../Goals/BuyArmy.h" #include "../Goals/BuyArmy.h"
#include "../Goals/VisitTile.h"
#include "../Goals/ExecuteHeroChain.h" #include "../Goals/ExecuteHeroChain.h"
#include "../Goals/DismissHero.h" #include "../Goals/DismissHero.h"
#include "../Goals/ExchangeSwapTownHeroes.h" #include "../Goals/ExchangeSwapTownHeroes.h"
@@ -32,7 +31,7 @@ std::string DefenceBehavior::toString() const
return "Defend towns"; return "Defend towns";
} }
Goals::TGoalVec DefenceBehavior::getTasks() Goals::TGoalVec DefenceBehavior::decompose() const
{ {
Goals::TGoalVec tasks; Goals::TGoalVec tasks;
@@ -65,7 +64,7 @@ uint64_t townArmyIncome(const CGTownInstance * town)
return result; 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) auto basicPriority = 0.3f + std::sqrt(townArmyIncome(town) / 20000.0f)
+ town->dailyIncome()[Res::GOLD] / 10000.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) if(cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES)
{ {
logAi->debug("Hero %s can be recruited to defend %s", hero->name, town->name); 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; continue;
} }
else else

View File

@@ -10,20 +10,28 @@
#pragma once #pragma once
#include "lib/VCMI_Lib.h" #include "lib/VCMI_Lib.h"
#include "Behavior.h" #include "../Goals/CGoal.h"
#include "../AIUtility.h" #include "../AIUtility.h"
class DefenceBehavior : public Behavior namespace Goals
{ {
public: class DefenceBehavior : public CGoal<DefenceBehavior>
{
public:
DefenceBehavior() DefenceBehavior()
{ {
} }
virtual Goals::TGoalVec getTasks() override; virtual Goals::TGoalVec decompose() const override;
virtual std::string toString() const override; virtual std::string toString() const override;
private: virtual bool operator==(const DefenceBehavior & other) const override
void evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town); {
}; return true;
}
private:
void evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const;
};
}

View File

@@ -23,14 +23,12 @@ extern FuzzyHelper * fh;
using namespace Goals; using namespace Goals;
#define AI_TRACE_LEVEL 2
std::string GatherArmyBehavior::toString() const std::string GatherArmyBehavior::toString() const
{ {
return "Gather army"; return "Gather army";
} }
Goals::TGoalVec GatherArmyBehavior::getTasks() Goals::TGoalVec GatherArmyBehavior::decompose() const
{ {
Goals::TGoalVec tasks; Goals::TGoalVec tasks;
@@ -65,12 +63,12 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
Goals::TGoalVec tasks; Goals::TGoalVec tasks;
const int3 pos = hero->visitablePos(); 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()); logAi->trace("Checking ways to gaher army for hero %s, %s", hero->getObjectName(), pos.toString());
#endif #endif
if(ai->nullkiller->isHeroLocked(hero)) 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()); logAi->trace("Skipping locked hero %s, %s", hero->getObjectName(), pos.toString());
#endif #endif
return tasks; return tasks;
@@ -79,13 +77,13 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
auto paths = ai->ah->getPathsToTile(pos); auto paths = ai->ah->getPathsToTile(pos);
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj; std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
#ifdef AI_TRACE_LEVEL >= 1 #if AI_TRACE_LEVEL >= 1
logAi->trace("Found %d paths", paths.size()); logAi->trace("Found %d paths", paths.size());
#endif #endif
for(const AIPath & path : paths) for(const AIPath & path : paths)
{ {
#ifdef AI_TRACE_LEVEL >= 2 #if AI_TRACE_LEVEL >= 2
logAi->trace("Path found %s", path.toString()); logAi->trace("Path found %s", path.toString());
#endif #endif
@@ -93,7 +91,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
if(path.getFirstBlockedAction()) if(path.getFirstBlockedAction())
{ {
#ifdef AI_TRACE_LEVEL >= 2 #if AI_TRACE_LEVEL >= 2
// TODO: decomposition? // TODO: decomposition?
logAi->trace("Ignore path. Action is blocked."); logAi->trace("Ignore path. Action is blocked.");
#endif #endif
@@ -102,7 +100,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path)) 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()); logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
#endif #endif
continue; continue;
@@ -122,7 +120,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger); auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
#ifdef AI_TRACE_LEVEL >= 2 #if AI_TRACE_LEVEL >= 2
logAi->trace( logAi->trace(
"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld", "It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld",
isSafe ? "safe" : "not safe", isSafe ? "safe" : "not safe",
@@ -164,25 +162,25 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
const int3 pos = upgrader->visitablePos(); const int3 pos = upgrader->visitablePos();
TResources availableResources = cb->getResourceAmount(); 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()); logAi->trace("Checking ways to upgrade army in town %s, %s", upgrader->getObjectName(), pos.toString());
#endif #endif
auto paths = ai->ah->getPathsToTile(pos); auto paths = ai->ah->getPathsToTile(pos);
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj; std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
#ifdef AI_TRACE_LEVEL >= 1 #if AI_TRACE_LEVEL >= 1
logAi->trace("Found %d paths", paths.size()); logAi->trace("Found %d paths", paths.size());
#endif #endif
for(const AIPath & path : paths) for(const AIPath & path : paths)
{ {
#ifdef AI_TRACE_LEVEL >= 2 #if AI_TRACE_LEVEL >= 2
logAi->trace("Path found %s", path.toString()); logAi->trace("Path found %s", path.toString());
#endif #endif
if(upgrader->visitingHero != path.targetHero) if(upgrader->visitingHero != path.targetHero)
{ {
#ifdef AI_TRACE_LEVEL >= 2 #if AI_TRACE_LEVEL >= 2
logAi->trace("Ignore path. Town has visiting hero."); logAi->trace("Ignore path. Town has visiting hero.");
#endif #endif
continue; continue;
@@ -190,7 +188,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
if(path.getFirstBlockedAction()) if(path.getFirstBlockedAction())
{ {
#ifdef AI_TRACE_LEVEL >= 2 #if AI_TRACE_LEVEL >= 2
// TODO: decomposition? // TODO: decomposition?
logAi->trace("Ignore path. Action is blocked."); logAi->trace("Ignore path. Action is blocked.");
#endif #endif
@@ -199,7 +197,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path)) 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()); logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
#endif #endif
continue; continue;
@@ -215,7 +213,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
auto isSafe = isSafeToVisit(path.targetHero, path.heroArmy, danger); auto isSafe = isSafeToVisit(path.targetHero, path.heroArmy, danger);
#ifdef AI_TRACE_LEVEL >= 2 #if AI_TRACE_LEVEL >= 2
logAi->trace( logAi->trace(
"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld", "It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld",
isSafe ? "safe" : "not safe", isSafe ? "safe" : "not safe",

View File

@@ -10,27 +10,29 @@
#pragma once #pragma once
#include "lib/VCMI_Lib.h" #include "lib/VCMI_Lib.h"
#include "Behavior.h" #include "../Goals/CGoal.h"
#include "../AIUtility.h" #include "../AIUtility.h"
class GatherArmyBehavior : public Behavior { namespace Goals
private: {
std::vector<int> objectTypes; class GatherArmyBehavior : public CGoal<GatherArmyBehavior>
std::vector<int> objectSubTypes; {
std::vector<const CGObjectInstance *> objectsToCapture; public:
bool specificObjects;
public:
GatherArmyBehavior() GatherArmyBehavior()
{ {
objectTypes = std::vector<int>();
specificObjects = false;
} }
virtual Goals::TGoalVec getTasks() override; virtual TGoalVec decompose() const override;
virtual std::string toString() const override; virtual std::string toString() const override;
private: virtual bool operator==(const GatherArmyBehavior & other) const override
Goals::TGoalVec deliverArmyToHero(const CGHeroInstance * hero) const; {
Goals::TGoalVec upgradeArmy(const CGTownInstance * upgrader) const; return true;
}; }
private:
TGoalVec deliverArmyToHero(const CGHeroInstance * hero) const;
TGoalVec upgradeArmy(const CGTownInstance * upgrader) const;
};
}

View File

@@ -28,7 +28,7 @@ std::string RecruitHeroBehavior::toString() const
return "Recruit hero"; return "Recruit hero";
} }
Goals::TGoalVec RecruitHeroBehavior::getTasks() Goals::TGoalVec RecruitHeroBehavior::decompose() const
{ {
Goals::TGoalVec tasks; Goals::TGoalVec tasks;
auto towns = cb->getTownsInfo(); auto towns = cb->getTownsInfo();
@@ -40,7 +40,7 @@ Goals::TGoalVec RecruitHeroBehavior::getTasks()
if(cb->getHeroesInfo().size() < cb->getTownsInfo().size() + 1 if(cb->getHeroesInfo().size() < cb->getTownsInfo().size() + 1
|| cb->getResourceAmount(Res::GOLD) > 10000) || 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)));
} }
} }
} }

View File

@@ -10,16 +10,24 @@
#pragma once #pragma once
#include "lib/VCMI_Lib.h" #include "lib/VCMI_Lib.h"
#include "Behavior.h" #include "../Goals/CGoal.h"
#include "../AIUtility.h" #include "../AIUtility.h"
class RecruitHeroBehavior : public Behavior namespace Goals
{ {
public: class RecruitHeroBehavior : public CGoal<RecruitHeroBehavior>
{
public:
RecruitHeroBehavior() RecruitHeroBehavior()
{ {
} }
virtual Goals::TGoalVec getTasks() override; virtual TGoalVec decompose() const override;
virtual std::string toString() const override; virtual std::string toString() const override;
};
virtual bool operator==(const RecruitHeroBehavior & other) const override
{
return true;
}
};
}

View File

@@ -92,7 +92,7 @@ bool needToRecruitHero(const CGTownInstance * startupTown)
return false; return false;
} }
Goals::TGoalVec StartupBehavior::getTasks() Goals::TGoalVec StartupBehavior::decompose() const
{ {
Goals::TGoalVec tasks; Goals::TGoalVec tasks;
auto towns = cb->getTownsInfo(); auto towns = cb->getTownsInfo();
@@ -166,7 +166,7 @@ Goals::TGoalVec StartupBehavior::getTasks()
if(tasks.empty() && canRecruitHero && !startupTown->visitingHero) 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()) if(tasks.empty() && towns.size())

View File

@@ -10,17 +10,25 @@
#pragma once #pragma once
#include "lib/VCMI_Lib.h" #include "lib/VCMI_Lib.h"
#include "Behavior.h" #include "../Goals/CGoal.h"
#include "../AIUtility.h" #include "../AIUtility.h"
class StartupBehavior : public Behavior namespace Goals
{ {
public: class StartupBehavior : public CGoal<StartupBehavior>
{
public:
StartupBehavior() StartupBehavior()
{ {
} }
virtual Goals::TGoalVec getTasks() override; virtual TGoalVec decompose() const override;
virtual std::string toString() const override; virtual std::string toString() const override;
};
virtual bool operator==(const StartupBehavior & other) const override
{
return true;
}
};
}

View File

@@ -27,24 +27,18 @@ set(VCAI_SRCS
Goals/GatherTroops.cpp Goals/GatherTroops.cpp
Goals/BuyArmy.cpp Goals/BuyArmy.cpp
Goals/AdventureSpellCast.cpp Goals/AdventureSpellCast.cpp
Goals/Win.cpp
Goals/VisitTile.cpp
Goals/VisitObj.cpp
Goals/VisitHero.cpp
Goals/CollectRes.cpp Goals/CollectRes.cpp
Goals/Trade.cpp Goals/Trade.cpp
Goals/RecruitHero.cpp Goals/RecruitHero.cpp
Goals/DigAtTile.cpp Goals/DigAtTile.cpp
Goals/GetArtOfType.cpp Goals/GetArtOfType.cpp
Goals/FindObj.cpp Goals/FindObj.cpp
Goals/CompleteQuest.cpp
Goals/ExecuteHeroChain.cpp Goals/ExecuteHeroChain.cpp
Goals/ExchangeSwapTownHeroes.cpp Goals/ExchangeSwapTownHeroes.cpp
Engine/Nullkiller.cpp Engine/Nullkiller.cpp
Engine/PriorityEvaluator.cpp Engine/PriorityEvaluator.cpp
Analyzers/DangerHitMapAnalyzer.cpp Analyzers/DangerHitMapAnalyzer.cpp
Analyzers/BuildAnalyzer.cpp Analyzers/BuildAnalyzer.cpp
Behaviors/Behavior.cpp
Behaviors/CaptureObjectsBehavior.cpp Behaviors/CaptureObjectsBehavior.cpp
Behaviors/RecruitHeroBehavior.cpp Behaviors/RecruitHeroBehavior.cpp
Behaviors/BuyArmyBehavior.cpp Behaviors/BuyArmyBehavior.cpp
@@ -52,6 +46,7 @@ set(VCAI_SRCS
Behaviors/StartupBehavior.cpp Behaviors/StartupBehavior.cpp
Behaviors/BuildingBehavior.cpp Behaviors/BuildingBehavior.cpp
Behaviors/GatherArmyBehavior.cpp Behaviors/GatherArmyBehavior.cpp
Behaviors/CompleteQuestBehavior.cpp
main.cpp main.cpp
VCAI.cpp VCAI.cpp
) )
@@ -88,17 +83,12 @@ set(VCAI_HEADERS
Goals/GatherTroops.h Goals/GatherTroops.h
Goals/BuyArmy.h Goals/BuyArmy.h
Goals/AdventureSpellCast.h Goals/AdventureSpellCast.h
Goals/Win.h
Goals/VisitTile.h
Goals/VisitObj.h
Goals/VisitHero.h
Goals/CollectRes.h Goals/CollectRes.h
Goals/Trade.h Goals/Trade.h
Goals/RecruitHero.h Goals/RecruitHero.h
Goals/DigAtTile.h Goals/DigAtTile.h
Goals/GetArtOfType.h Goals/GetArtOfType.h
Goals/FindObj.h Goals/FindObj.h
Goals/CompleteQuest.h
Goals/ExecuteHeroChain.h Goals/ExecuteHeroChain.h
Goals/ExchangeSwapTownHeroes.h Goals/ExchangeSwapTownHeroes.h
Goals/Goals.h Goals/Goals.h
@@ -106,7 +96,6 @@ set(VCAI_HEADERS
Engine/PriorityEvaluator.h Engine/PriorityEvaluator.h
Analyzers/DangerHitMapAnalyzer.h Analyzers/DangerHitMapAnalyzer.h
Analyzers/BuildAnalyzer.h Analyzers/BuildAnalyzer.h
Behaviors/Behavior.h
Behaviors/CaptureObjectsBehavior.h Behaviors/CaptureObjectsBehavior.h
Behaviors/RecruitHeroBehavior.h Behaviors/RecruitHeroBehavior.h
Behaviors/BuyArmyBehavior.h Behaviors/BuyArmyBehavior.h
@@ -114,6 +103,7 @@ set(VCAI_HEADERS
Behaviors/StartupBehavior.h Behaviors/StartupBehavior.h
Behaviors/BuildingBehavior.h Behaviors/BuildingBehavior.h
Behaviors/GatherArmyBehavior.h Behaviors/GatherArmyBehavior.h
Behaviors/CompleteQuestBehavior.h
VCAI.h VCAI.h
) )

View File

@@ -23,6 +23,8 @@
extern boost::thread_specific_ptr<CCallback> cb; extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai; extern boost::thread_specific_ptr<VCAI> ai;
using namespace Goals;
Nullkiller::Nullkiller() Nullkiller::Nullkiller()
{ {
priorityEvaluator.reset(new PriorityEvaluator()); priorityEvaluator.reset(new PriorityEvaluator());
@@ -30,38 +32,75 @@ Nullkiller::Nullkiller()
buildAnalyzer.reset(new BuildAnalyzer()); 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{ Goals::TTask bestTask = *vstd::maxElementByFun(tasks, [](Goals::TTask task) -> float{
return goal->priority; return task->priority;
}); });
return bestTask; return bestTask;
} }
Goals::TSubgoal Nullkiller::choseBestTask(std::shared_ptr<Behavior> behavior) const Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const
{ {
logAi->debug("Checking behavior %s", behavior->toString()); 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()) if(tasks.empty())
{ {
logAi->debug("Behavior %s found no tasks", behavior->toString()); 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()); 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); 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; return task;
} }
@@ -141,54 +180,49 @@ void Nullkiller::makeTurn()
{ {
updateAiState(); updateAiState();
Goals::TGoalVec bestTasks = { Goals::TTaskVec bestTasks = {
choseBestTask(std::make_shared<BuyArmyBehavior>()), choseBestTask(sptr(BuyArmyBehavior())),
choseBestTask(std::make_shared<CaptureObjectsBehavior>()), choseBestTask(sptr(CaptureObjectsBehavior())),
choseBestTask(std::make_shared<RecruitHeroBehavior>()), choseBestTask(sptr(RecruitHeroBehavior())),
choseBestTask(std::make_shared<DefenceBehavior>()), choseBestTask(sptr(DefenceBehavior())),
choseBestTask(std::make_shared<BuildingBehavior>()), choseBestTask(sptr(BuildingBehavior())),
choseBestTask(std::make_shared<GatherArmyBehavior>()) choseBestTask(sptr(GatherArmyBehavior()))
}; };
if(cb->getDate(Date::DAY) == 1) if(cb->getDate(Date::DAY) == 1)
{ {
bestTasks.push_back(choseBestTask(std::make_shared<StartupBehavior>())); 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."); logAi->trace("No goals found. Ending turn.");
return; return;
} }*/
if(bestTask->priority < MIN_PRIORITY) 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; 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 try
{ {
if(bestTask->hero)
{
setActive(bestTask->hero.get(), bestTask->tile);
}
bestTask->accept(ai.get()); bestTask->accept(ai.get());
} }
catch(goalFulfilledException &) catch(goalFulfilledException &)
{ {
logAi->trace("Task %s completed", bestTask->name()); logAi->trace("Task %s completed", bestTask->toString());
} }
catch(std::exception & e) 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()); logAi->debug("The error message was: %s", e.what());
return; return;

View File

@@ -13,7 +13,6 @@
#include "../Analyzers/DangerHitMapAnalyzer.h" #include "../Analyzers/DangerHitMapAnalyzer.h"
#include "../Analyzers/BuildAnalyzer.h" #include "../Analyzers/BuildAnalyzer.h"
#include "../Goals/AbstractGoal.h" #include "../Goals/AbstractGoal.h"
#include "../Behaviors/Behavior.h"
const float MAX_GOLD_PEASURE = 0.3f; const float MAX_GOLD_PEASURE = 0.3f;
const float MIN_PRIORITY = 0.01f; const float MIN_PRIORITY = 0.01f;
@@ -56,6 +55,6 @@ public:
private: private:
void resetAiState(); void resetAiState();
void updateAiState(); void updateAiState();
Goals::TSubgoal choseBestTask(std::shared_ptr<Behavior> behavior) const; Goals::TTask choseBestTask(Goals::TSubgoal behavior) const;
Goals::TSubgoal choseBestTask(Goals::TGoalVec & tasks) const; Goals::TTask choseBestTask(Goals::TTaskVec & tasks) const;
}; };

View File

@@ -520,9 +520,6 @@ Goals::EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgo
/// importance /// importance
float PriorityEvaluator::evaluate(Goals::TSubgoal task) float PriorityEvaluator::evaluate(Goals::TSubgoal task)
{ {
if(task->priority > 0)
return task->priority;
auto evaluationContext = buildEvaluationContext(task); auto evaluationContext = buildEvaluationContext(task);
int rewardType = (evaluationContext.goldReward > 0 ? 1 : 0) int rewardType = (evaluationContext.goldReward > 0 ? 1 : 0)
@@ -561,7 +558,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
#ifdef VCMI_TRACE_PATHFINDER #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", 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.armyLossPersentage,
evaluationContext.movementCostByRole[HeroRole::MAIN], evaluationContext.movementCostByRole[HeroRole::MAIN],
evaluationContext.movementCostByRole[HeroRole::SCOUT], evaluationContext.movementCostByRole[HeroRole::SCOUT],

View File

@@ -89,19 +89,6 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army)
return as; 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() TacticalAdvantageEngine::TacticalAdvantageEngine()
{ {
try try
@@ -254,206 +241,3 @@ float TacticalAdvantageEngine::getTacticalAdvantage(const CArmedInstance * we, c
return output; return output;
} }
//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & 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<fl::InputVariable *> 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<fl::Discrete::Pair> 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<int> 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;
}

View File

@@ -36,37 +36,3 @@ private:
fl::InputVariable * castleWalls; fl::InputVariable * castleWalls;
fl::OutputVariable * threat; 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;
};

View File

@@ -29,7 +29,19 @@ TSubgoal Goals::sptr(const AbstractGoal & tmp)
return ptr; 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<ITask *>(tmp.clone()));
return ptr;
}
std::string AbstractGoal::toString() const //TODO: virtualize
{ {
std::string desc; std::string desc;
switch(goalType) switch(goalType)
@@ -124,11 +136,6 @@ bool AbstractGoal::invalid() const
return goalType == EGoals::INVALID; return goalType == EGoals::INVALID;
} }
void AbstractGoal::accept(VCAI * ai)
{
ai->tryRealize(*this);
}
EvaluationContext::EvaluationContext() EvaluationContext::EvaluationContext()
: movementCost(0.0), : movementCost(0.0),
manaCost(0), manaCost(0),

View File

@@ -22,21 +22,16 @@ class FuzzyHelper;
namespace Goals namespace Goals
{ {
class AbstractGoal; class AbstractGoal;
class Explore; class ITask;
class RecruitHero; class RecruitHero;
class VisitTile;
class VisitObj;
class VisitHero;
class BuildThis; class BuildThis;
class DigAtTile; class DigAtTile;
class CollectRes; class CollectRes;
class BuyArmy; class BuyArmy;
class BuildBoat; class BuildBoat;
class GatherArmy;
class ClearWayTo; class ClearWayTo;
class Invalid; class Invalid;
class Trade; class Trade;
class CompleteQuest;
class AdventureSpellCast; class AdventureSpellCast;
enum EGoals enum EGoals
@@ -78,6 +73,8 @@ namespace Goals
//TODO: serialize? //TODO: serialize?
}; };
typedef std::shared_ptr<ITask> TTask;
typedef std::vector<TTask> TTaskVec;
typedef std::vector<TSubgoal> TGoalVec; typedef std::vector<TSubgoal> TGoalVec;
//method chaining + clone pattern //method chaining + clone pattern
@@ -91,6 +88,7 @@ namespace Goals
enum { LOW_PR = -1 }; enum { LOW_PR = -1 };
DLL_EXPORT TSubgoal sptr(const AbstractGoal & tmp); DLL_EXPORT TSubgoal sptr(const AbstractGoal & tmp);
DLL_EXPORT TTask taskptr(const AbstractGoal & tmp);
struct DLL_EXPORT EvaluationContext struct DLL_EXPORT EvaluationContext
{ {
@@ -118,7 +116,6 @@ namespace Goals
public: public:
bool isElementar; VSETTER(bool, isElementar) bool isElementar; VSETTER(bool, isElementar)
bool isAbstract; VSETTER(bool, isAbstract) bool isAbstract; VSETTER(bool, isAbstract)
float priority; VSETTER(float, priority)
int value; VSETTER(int, value) int value; VSETTER(int, value)
int resID; VSETTER(int, resID) int resID; VSETTER(int, resID)
int objid; VSETTER(int, objid) int objid; VSETTER(int, objid)
@@ -133,7 +130,6 @@ namespace Goals
AbstractGoal(EGoals goal = EGoals::INVALID) AbstractGoal(EGoals goal = EGoals::INVALID)
: goalType(goal), evaluationContext() : goalType(goal), evaluationContext()
{ {
priority = 0;
isElementar = false; isElementar = false;
isAbstract = false; isAbstract = false;
value = 0; value = 0;
@@ -150,25 +146,18 @@ namespace Goals
{ {
return const_cast<AbstractGoal *>(this); return const_cast<AbstractGoal *>(this);
} }
virtual TGoalVec getAllPossibleSubgoals()
virtual TGoalVec decompose() const
{ {
return TGoalVec(); return TGoalVec();
} }
virtual TSubgoal whatToDoToAchieve()
{
return sptr(AbstractGoal());
}
EGoals goalType; EGoals goalType;
virtual std::string name() const; virtual std::string toString() const;
bool invalid() 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; virtual bool operator==(const AbstractGoal & g) const;
bool operator!=(const AbstractGoal & g) const bool operator!=(const AbstractGoal & g) const
@@ -192,4 +181,15 @@ namespace Goals
h & bid; 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;
};
} }

View File

@@ -26,7 +26,7 @@ bool AdventureSpellCast::operator==(const AdventureSpellCast & other) const
return hero.h == other.hero.h; return hero.h == other.hero.h;
} }
TSubgoal AdventureSpellCast::whatToDoToAchieve() void AdventureSpellCast::accept(VCAI * ai)
{ {
if(!hero.validAndSet()) if(!hero.validAndSet())
throw cannotFulfillGoalException("Invalid hero!"); throw cannotFulfillGoalException("Invalid hero!");
@@ -47,11 +47,6 @@ TSubgoal AdventureSpellCast::whatToDoToAchieve()
if(spellID == SpellID::TOWN_PORTAL && town && town->visitingHero) if(spellID == SpellID::TOWN_PORTAL && town && town->visitingHero)
throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->name); throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->name);
return iAmElementar();
}
void AdventureSpellCast::accept(VCAI * ai)
{
if(town && spellID == SpellID::TOWN_PORTAL) if(town && spellID == SpellID::TOWN_PORTAL)
{ {
ai->selectedObject = town->id; ai->selectedObject = town->id;
@@ -73,7 +68,7 @@ void AdventureSpellCast::accept(VCAI * ai)
throw goalFulfilledException(sptr(*this)); throw goalFulfilledException(sptr(*this));
} }
std::string AdventureSpellCast::name() const std::string AdventureSpellCast::toString() const
{ {
return "AdventureSpellCast " + spellID.toSpell()->name; return "AdventureSpellCast " + spellID.toSpell()->name;
} }

View File

@@ -13,31 +13,25 @@
namespace Goals namespace Goals
{ {
class DLL_EXPORT AdventureSpellCast : public CGoal<AdventureSpellCast> class DLL_EXPORT AdventureSpellCast : public ElementarGoal<AdventureSpellCast>
{ {
private: private:
SpellID spellID; SpellID spellID;
public: public:
AdventureSpellCast(HeroPtr hero, SpellID spellID) AdventureSpellCast(HeroPtr hero, SpellID spellID)
: CGoal(Goals::ADVENTURE_SPELL_CAST), spellID(spellID) : ElementarGoal(Goals::ADVENTURE_SPELL_CAST), spellID(spellID)
{ {
sethero(hero); sethero(hero);
} }
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
const CSpell * getSpell() const const CSpell * getSpell() const
{ {
return spellID.toSpell(); return spellID.toSpell();
} }
TSubgoal whatToDoToAchieve() override;
void accept(VCAI * ai) override; void accept(VCAI * ai) override;
std::string name() const override; std::string toString() const override;
virtual bool operator==(const AdventureSpellCast & other) const override; virtual bool operator==(const AdventureSpellCast & other) const override;
}; };
} }

View File

@@ -14,6 +14,7 @@
#include "../AIhelper.h" #include "../AIhelper.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions #include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h" #include "../../../lib/CPathfinder.h"
#include "../Behaviors/CaptureObjectsBehavior.h"
extern boost::thread_specific_ptr<CCallback> cb; extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai; extern boost::thread_specific_ptr<VCAI> ai;
@@ -26,23 +27,23 @@ bool BuildBoat::operator==(const BuildBoat & other) const
return shipyard->o->id == other.shipyard->o->id; return shipyard->o->id == other.shipyard->o->id;
} }
//TSubgoal BuildBoat::whatToDoToAchieve() TSubgoal BuildBoat::decomposeSingle() const
//{ {
// if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
// { {
// return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o)); return sptr(CaptureObjectsBehavior(shipyard->o));
// } }
//
// if(shipyard->shipyardStatus() != IShipyard::GOOD) if(shipyard->shipyardStatus() != IShipyard::GOOD)
// { {
// throw cannotFulfillGoalException("Shipyard is busy."); throw cannotFulfillGoalException("Shipyard is busy.");
// } }
//
// TResources boatCost; TResources boatCost;
// shipyard->getBoatCost(boatCost); shipyard->getBoatCost(boatCost);
//
// return ai->ah->whatToDo(boatCost, this->iAmElementar()); return iAmElementar();
//} }
void BuildBoat::accept(VCAI * ai) void BuildBoat::accept(VCAI * ai)
{ {
@@ -75,7 +76,7 @@ void BuildBoat::accept(VCAI * ai)
throw goalFulfilledException(sptr(*this)); throw goalFulfilledException(sptr(*this));
} }
std::string BuildBoat::name() const std::string BuildBoat::toString() const
{ {
return "BuildBoat"; return "BuildBoat";
} }

View File

@@ -13,23 +13,20 @@
namespace Goals namespace Goals
{ {
class DLL_EXPORT BuildBoat : public CGoal<BuildBoat> class DLL_EXPORT BuildBoat : public ElementarGoal<BuildBoat>
{ {
private: private:
const IShipyard * shipyard; const IShipyard * shipyard;
TSubgoal decomposeSingle() const override;
public: public:
BuildBoat(const IShipyard * shipyard) 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; void accept(VCAI * ai) override;
std::string name() const override; std::string toString() const override;
virtual bool operator==(const BuildBoat & other) const override; virtual bool operator==(const BuildBoat & other) const override;
}; };
} }

View File

@@ -29,7 +29,7 @@ bool BuildThis::operator==(const BuildThis & other) const
return town == other.town && bid == other.bid; 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; return "Build " + buildingInfo.name + "(" + std::to_string(bid) + ") in " + town->name;
} }

View File

@@ -18,36 +18,36 @@ class FuzzyHelper;
namespace Goals namespace Goals
{ {
class DLL_EXPORT BuildThis : public CGoal<BuildThis> class DLL_EXPORT BuildThis : public ElementarGoal<BuildThis>
{ {
public: public:
BuildingInfo buildingInfo; BuildingInfo buildingInfo;
TownDevelopmentInfo townInfo; TownDevelopmentInfo townInfo;
BuildThis() //should be private, but unit test uses it 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 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; bid = buildingInfo.id;
town = townInfo.town; town = townInfo.town;
} }
BuildThis(BuildingID Bid, const CGTownInstance * tid) BuildThis(BuildingID Bid, const CGTownInstance * tid)
: CGoal(Goals::BUILD_STRUCTURE) : ElementarGoal(Goals::BUILD_STRUCTURE)
{ {
bid = Bid; bid = Bid;
town = tid; town = tid;
priority = 1; priority = 1;
} }
BuildThis(BuildingID Bid) BuildThis(BuildingID Bid)
: CGoal(Goals::BUILD_STRUCTURE) : ElementarGoal(Goals::BUILD_STRUCTURE)
{ {
bid = Bid; bid = Bid;
priority = 1; priority = 1;
} }
virtual bool operator==(const BuildThis & other) const override; virtual bool operator==(const BuildThis & other) const override;
virtual std::string name() const override; virtual std::string toString() const override;
}; };
} }

View File

@@ -25,7 +25,7 @@ bool BuyArmy::operator==(const BuyArmy & other) const
return town == other.town && objid == other.objid; return town == other.town && objid == other.objid;
} }
TSubgoal BuyArmy::whatToDoToAchieve() std::string BuyArmy::toString() const
{ {
return iAmElementar(); return "Buy army at " + town->name;
} }

View File

@@ -17,23 +17,24 @@ class FuzzyHelper;
namespace Goals namespace Goals
{ {
class DLL_EXPORT BuyArmy : public CGoal<BuyArmy> class DLL_EXPORT BuyArmy : public ElementarGoal<BuyArmy>
{ {
private: private:
BuyArmy() BuyArmy()
: CGoal(Goals::BUY_ARMY) : ElementarGoal(Goals::BUY_ARMY)
{ {
} }
public: public:
BuyArmy(const CGTownInstance * Town, int val) BuyArmy(const CGTownInstance * Town, int val)
: CGoal(Goals::BUY_ARMY) : ElementarGoal(Goals::BUY_ARMY)
{ {
town = Town; //where to buy this army town = Town; //where to buy this army
value = val; //expressed in AI unit strength value = val; //expressed in AI unit strength
priority = 3;//TODO: evaluate? priority = 3;//TODO: evaluate?
} }
TSubgoal whatToDoToAchieve() override;
virtual bool operator==(const BuyArmy & other) const override; virtual bool operator==(const BuyArmy & other) const override;
virtual std::string toString() const override;
}; };
} }

View File

@@ -23,9 +23,8 @@ namespace Goals
public: public:
CGoal<T>(EGoals goal = INVALID) : AbstractGoal(goal) CGoal<T>(EGoals goal = INVALID) : AbstractGoal(goal)
{ {
priority = 0;
isElementar = false; isElementar = false;
isAbstract = false; isAbstract = true;
value = 0; value = 0;
aid = -1; aid = -1;
objid = -1; objid = -1;
@@ -36,7 +35,6 @@ namespace Goals
OSETTER(bool, isElementar) OSETTER(bool, isElementar)
OSETTER(bool, isAbstract) OSETTER(bool, isAbstract)
OSETTER(float, priority)
OSETTER(int, value) OSETTER(int, value)
OSETTER(int, resID) OSETTER(int, resID)
OSETTER(int, objid) OSETTER(int, objid)
@@ -46,11 +44,6 @@ namespace Goals
OSETTER(CGTownInstance *, town) OSETTER(CGTownInstance *, town)
OSETTER(int, bid) OSETTER(int, bid)
void accept(VCAI * ai) override
{
ai->tryRealize(static_cast<T &>(*this)); //casting enforces template instantiation
}
CGoal<T> * clone() const override CGoal<T> * clone() const override
{ {
return new T(static_cast<T const &>(*this)); //casting enforces template instantiation return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
@@ -80,5 +73,65 @@ namespace Goals
} }
virtual bool operator==(const T & other) const = 0; virtual bool operator==(const T & other) const = 0;
virtual TGoalVec decompose() const override
{
return {decomposeSingle()};
}
protected:
virtual TSubgoal decomposeSingle() const
{
return sptr(Invalid());
}
};
template<typename T> class DLL_EXPORT ElementarGoal : public CGoal<T>, public ITask
{
public:
ElementarGoal<T>(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<Invalid>
{
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";
}
}; };
} }

View File

@@ -29,10 +29,10 @@ bool CollectRes::operator==(const CollectRes & other) const
return resID == other.resID; return resID == other.resID;
} }
TGoalVec CollectRes::getAllPossibleSubgoals() //TGoalVec CollectRes::getAllPossibleSubgoals()
{ //{
TGoalVec ret; // TGoalVec ret;
//
//auto givesResource = [this](const CGObjectInstance * obj) -> bool //auto givesResource = [this](const CGObjectInstance * obj) -> bool
//{ //{
// //TODO: move this logic to object side // //TODO: move this logic to object side
@@ -107,18 +107,18 @@ TGoalVec CollectRes::getAllPossibleSubgoals()
// vstd::concatenate(ret, waysToGo); // vstd::concatenate(ret, waysToGo);
// } // }
//} //}
return ret; // return ret;
} //}
TSubgoal CollectRes::whatToDoToAchieve() //TSubgoal CollectRes::whatToDoToAchieve()
{ //{
auto goals = getAllPossibleSubgoals(); // auto goals = getAllPossibleSubgoals();
auto trade = whatToDoToTrade(); // auto trade = whatToDoToTrade();
if (!trade->invalid()) // if (!trade->invalid())
goals.push_back(trade); // goals.push_back(trade);
//
return sptr(Invalid()); //we can always do that // return sptr(Invalid()); //we can always do that
} //}
TSubgoal CollectRes::whatToDoToTrade() TSubgoal CollectRes::whatToDoToTrade()
{ {

View File

@@ -29,10 +29,9 @@ namespace Goals
{ {
resID = rid; resID = rid;
value = val; value = val;
priority = 2;
} }
TGoalVec getAllPossibleSubgoals() override; /*TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;*/
TSubgoal whatToDoToTrade(); TSubgoal whatToDoToTrade();
virtual bool operator==(const CollectRes & other) const override; virtual bool operator==(const CollectRes & other) const override;
}; };

View File

@@ -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<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> 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;
}

View File

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

View File

@@ -24,16 +24,16 @@ bool DigAtTile::operator==(const DigAtTile & other) const
{ {
return other.hero.h == hero.h && other.tile == tile; return other.hero.h == hero.h && other.tile == tile;
} }
//
TSubgoal DigAtTile::whatToDoToAchieve() //TSubgoal DigAtTile::decomposeSingle() const
{ //{
const CGObjectInstance * firstObj = vstd::frontOrNull(cb->getVisitableObjs(tile)); // const CGObjectInstance * firstObj = vstd::frontOrNull(cb->getVisitableObjs(tile));
if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest // if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest
{ // {
const CGHeroInstance * h = dynamic_cast<const CGHeroInstance *>(firstObj); // const CGHeroInstance * h = dynamic_cast<const CGHeroInstance *>(firstObj);
sethero(h).setisElementar(true); // sethero(h).setisElementar(true);
return sptr(*this); // return sptr(*this);
} // }
//
return sptr(VisitTile(tile)); // return sptr(VisitTile(tile));
} //}

View File

@@ -29,13 +29,10 @@ namespace Goals
: CGoal(Goals::DIG_AT_TILE) : CGoal(Goals::DIG_AT_TILE)
{ {
tile = Tile; tile = Tile;
priority = 20;
} }
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
virtual bool operator==(const DigAtTile & other) const override; virtual bool operator==(const DigAtTile & other) const override;
private:
//TSubgoal decomposeSingle() const override;
}; };
} }

View File

@@ -26,14 +26,6 @@ bool DismissHero::operator==(const DismissHero & other) const
return hero.h == other.hero.h; return hero.h == other.hero.h;
} }
TSubgoal DismissHero::whatToDoToAchieve()
{
if(!hero.validAndSet())
throw cannotFulfillGoalException("Invalid hero!");
return iAmElementar();
}
void DismissHero::accept(VCAI * ai) void DismissHero::accept(VCAI * ai)
{ {
if(!hero.validAndSet()) if(!hero.validAndSet())
@@ -44,7 +36,7 @@ void DismissHero::accept(VCAI * ai)
throw goalFulfilledException(sptr(*this)); throw goalFulfilledException(sptr(*this));
} }
std::string DismissHero::name() const std::string DismissHero::toString() const
{ {
return "DismissHero " + hero.name; return "DismissHero " + hero.name;
} }

View File

@@ -13,23 +13,17 @@
namespace Goals namespace Goals
{ {
class DLL_EXPORT DismissHero : public CGoal<DismissHero> class DLL_EXPORT DismissHero : public ElementarGoal<DismissHero>
{ {
public: public:
DismissHero(HeroPtr hero) DismissHero(HeroPtr hero)
: CGoal(Goals::DISMISS_HERO) : ElementarGoal(Goals::DISMISS_HERO)
{ {
sethero(hero); sethero(hero);
} }
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
void accept(VCAI * ai) override; void accept(VCAI * ai) override;
std::string name() const override; std::string toString() const override;
virtual bool operator==(const DismissHero & other) const override; virtual bool operator==(const DismissHero & other) const override;
}; };
} }

View File

@@ -27,11 +27,11 @@ ExchangeSwapTownHeroes::ExchangeSwapTownHeroes(
const CGTownInstance * town, const CGTownInstance * town,
const CGHeroInstance * garrisonHero, const CGHeroInstance * garrisonHero,
HeroLockedReason lockingReason) 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; return "Exchange and swap heroes of " + town->name;
} }
@@ -41,11 +41,6 @@ bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) co
return town == other.town; return town == other.town;
} }
TSubgoal ExchangeSwapTownHeroes::whatToDoToAchieve()
{
return iAmElementar();
}
void ExchangeSwapTownHeroes::accept(VCAI * ai) void ExchangeSwapTownHeroes::accept(VCAI * ai)
{ {
if(!garrisonHero) if(!garrisonHero)

View File

@@ -14,7 +14,7 @@
namespace Goals namespace Goals
{ {
class DLL_EXPORT ExchangeSwapTownHeroes : public CGoal<ExchangeSwapTownHeroes> class DLL_EXPORT ExchangeSwapTownHeroes : public ElementarGoal<ExchangeSwapTownHeroes>
{ {
private: private:
const CGTownInstance * town; const CGTownInstance * town;
@@ -27,14 +27,8 @@ namespace Goals
const CGHeroInstance * garrisonHero = nullptr, const CGHeroInstance * garrisonHero = nullptr,
HeroLockedReason lockingReason = HeroLockedReason::NOT_LOCKED); HeroLockedReason lockingReason = HeroLockedReason::NOT_LOCKED);
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
void accept(VCAI * ai) override; void accept(VCAI * ai) override;
std::string name() const override; std::string toString() const override;
virtual bool operator==(const ExchangeSwapTownHeroes & other) const override; virtual bool operator==(const ExchangeSwapTownHeroes & other) const override;
}; };
} }

View File

@@ -24,23 +24,12 @@ extern FuzzyHelper * fh;
using namespace Goals; using namespace Goals;
ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj) 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; hero = path.targetHero;
tile = path.targetTile(); tile = path.targetTile();
for(auto & node : path.nodes)
{
auto role = ai->ah->getHeroRole(node.targetHero);
evaluationContext.movementCostByRole[role] += node.cost;
}
if(obj) if(obj)
{ {
objid = obj->id.getNum(); objid = obj->id.getNum();
@@ -57,15 +46,12 @@ bool ExecuteHeroChain::operator==(const ExecuteHeroChain & other) const
return false; return false;
} }
TSubgoal ExecuteHeroChain::whatToDoToAchieve()
{
return iAmElementar();
}
void ExecuteHeroChain::accept(VCAI * ai) void ExecuteHeroChain::accept(VCAI * ai)
{ {
logAi->debug("Executing hero chain towards %s. Path %s", targetName, chainPath.toString()); logAi->debug("Executing hero chain towards %s. Path %s", targetName, chainPath.toString());
ai->nullkiller->setActive(chainPath.targetHero, tile);
std::set<int> blockedIndexes; std::set<int> blockedIndexes;
for(int i = chainPath.nodes.size() - 1; i >= 0; i--) for(int i = chainPath.nodes.size() - 1; i >= 0; i--)
@@ -89,7 +75,6 @@ void ExecuteHeroChain::accept(VCAI * ai)
{ {
if(hero->movement) if(hero->movement)
{ {
ai->nullkiller->setActive(hero, node.coord); ai->nullkiller->setActive(hero, node.coord);
if(node.specialAction) if(node.specialAction)
@@ -98,6 +83,8 @@ void ExecuteHeroChain::accept(VCAI * ai)
{ {
auto specialGoal = node.specialAction->whatToDo(hero); auto specialGoal = node.specialAction->whatToDo(hero);
if(!specialGoal->isElementar)
specialGoal->accept(ai); specialGoal->accept(ai);
} }
else else
@@ -135,7 +122,10 @@ void ExecuteHeroChain::accept(VCAI * ai)
{ {
try try
{ {
Goals::VisitTile(node.coord).sethero(hero).accept(ai); if(moveHeroToTile(hero, node.coord))
{
continue;
}
} }
catch(cannotFulfillGoalException) 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; 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);
}

View File

@@ -13,7 +13,7 @@
namespace Goals namespace Goals
{ {
class DLL_EXPORT ExecuteHeroChain : public CGoal<ExecuteHeroChain> class DLL_EXPORT ExecuteHeroChain : public ElementarGoal<ExecuteHeroChain>
{ {
private: private:
AIPath chainPath; AIPath chainPath;
@@ -22,15 +22,13 @@ namespace Goals
public: public:
ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj = nullptr); ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj = nullptr);
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
void accept(VCAI * ai) override; void accept(VCAI * ai) override;
std::string name() const override; std::string toString() const override;
virtual bool operator==(const ExecuteHeroChain & other) const override; virtual bool operator==(const ExecuteHeroChain & other) const override;
const AIPath & getPath() const { return chainPath; } const AIPath & getPath() const { return chainPath; }
private:
bool moveHeroToTile(const CGHeroInstance * hero, const int3 & tile);
}; };
} }

View File

@@ -23,32 +23,32 @@ bool FindObj::operator==(const FindObj & other) const
{ {
return other.hero.h == hero.h && other.objid == objid; return other.hero.h == hero.h && other.objid == objid;
} }
//
TSubgoal FindObj::whatToDoToAchieve() //TSubgoal FindObj::whatToDoToAchieve()
{ //{
const CGObjectInstance * o = nullptr; // const CGObjectInstance * o = nullptr;
if(resID > -1) //specified // if(resID > -1) //specified
{ // {
for(const CGObjectInstance * obj : ai->visitableObjs) // for(const CGObjectInstance * obj : ai->visitableObjs)
{ // {
if(obj->ID == objid && obj->subID == resID) // if(obj->ID == objid && obj->subID == resID)
{ // {
o = obj; // o = obj;
break; //TODO: consider multiple objects and choose best // break; //TODO: consider multiple objects and choose best
} // }
} // }
} // }
else // else
{ // {
for(const CGObjectInstance * obj : ai->visitableObjs) // for(const CGObjectInstance * obj : ai->visitableObjs)
{ // {
if(obj->ID == objid) // if(obj->ID == objid)
{ // {
o = obj; // o = obj;
break; //TODO: consider multiple objects and choose best // 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 // 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())); // return sptr(VisitObj(o->id.getNum()));
} //}

View File

@@ -27,20 +27,16 @@ namespace Goals
{ {
objid = ID; objid = ID;
resID = -1; //subid unspecified resID = -1; //subid unspecified
priority = 1;
} }
FindObj(int ID, int subID) FindObj(int ID, int subID)
: CGoal(Goals::FIND_OBJ) : CGoal(Goals::FIND_OBJ)
{ {
objid = ID; objid = ID;
resID = subID; resID = subID;
priority = 1;
} }
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
virtual bool operator==(const FindObj & other) const override; virtual bool operator==(const FindObj & other) const override;
private:
//TSubgoal decomposeSingle() const override;
}; };
} }

View File

@@ -43,30 +43,30 @@ int GatherTroops::getCreaturesCount(const CArmedInstance * army)
return count; 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() //
{ //TGoalVec GatherTroops::getAllPossibleSubgoals()
logAi->trace("Entering GatherTroops::whatToDoToAchieve"); //{
// TGoalVec solutions;
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;
//for(const CGTownInstance * t : cb->getTownsInfo()) //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 goal->hero && !goal->hero->getSlotFor(creID).validSlot() && !goal->hero->getFreeSlot().validSlot();
//}); //});
return solutions; // return solutions;
//TODO: exchange troops between heroes // //TODO: exchange troops between heroes
} //}

View File

@@ -23,17 +23,13 @@ namespace Goals
GatherTroops() GatherTroops()
: CGoal(Goals::GATHER_TROOPS) : CGoal(Goals::GATHER_TROOPS)
{ {
priority = 2;
} }
GatherTroops(int type, int val) GatherTroops(int type, int val)
: CGoal(Goals::GATHER_TROOPS) : CGoal(Goals::GATHER_TROOPS)
{ {
objid = type; objid = type;
value = val; value = val;
priority = 2;
} }
TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override;
virtual bool operator==(const GatherTroops & other) const override; virtual bool operator==(const GatherTroops & other) const override;
private: private:

View File

@@ -24,8 +24,8 @@ bool GetArtOfType::operator==(const GetArtOfType & other) const
{ {
return other.hero.h == hero.h && other.objid == objid; return other.hero.h == hero.h && other.objid == objid;
} }
//
TSubgoal GetArtOfType::whatToDoToAchieve() //TSubgoal GetArtOfType::whatToDoToAchieve()
{ //{
return sptr(FindObj(Obj::ARTIFACT, aid)); // return sptr(FindObj(Obj::ARTIFACT, aid));
} //}

View File

@@ -28,13 +28,7 @@ namespace Goals
: CGoal(Goals::GET_ART_TYPE) : CGoal(Goals::GET_ART_TYPE)
{ {
aid = type; aid = type;
priority = 2;
} }
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
virtual bool operator==(const GetArtOfType & other) const override; virtual bool operator==(const GetArtOfType & other) const override;
}; };
} }

View File

@@ -13,10 +13,6 @@
#include "Invalid.h" #include "Invalid.h"
#include "BuildBoat.h" #include "BuildBoat.h"
#include "BuildThis.h" #include "BuildThis.h"
#include "Win.h"
#include "VisitObj.h"
#include "VisitTile.h"
#include "VisitHero.h"
#include "BuyArmy.h" #include "BuyArmy.h"
#include "GatherTroops.h" #include "GatherTroops.h"
#include "Trade.h" #include "Trade.h"
@@ -25,5 +21,4 @@
#include "GetArtOfType.h" #include "GetArtOfType.h"
#include "DigAtTile.h" #include "DigAtTile.h"
#include "FindObj.h" #include "FindObj.h"
#include "CompleteQuest.h"
#include "AdventureSpellCast.h" #include "AdventureSpellCast.h"

View File

@@ -16,26 +16,4 @@ class VCAI;
namespace Goals namespace Goals
{ {
class DLL_EXPORT Invalid : public CGoal<Invalid>
{
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;
}
};
} }

View File

@@ -23,3 +23,8 @@ extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh; extern FuzzyHelper * fh;
using namespace Goals; using namespace Goals;
std::string RecruitHero::toString() const
{
return "Recruit hero at " + town->name;
}

View File

@@ -17,18 +17,27 @@ class FuzzyHelper;
namespace Goals namespace Goals
{ {
class DLL_EXPORT RecruitHero : public CGoal<RecruitHero> class DLL_EXPORT RecruitHero : public ElementarGoal<RecruitHero>
{ {
public: public:
RecruitHero() RecruitHero(const CGTownInstance * townWithTavern, const CGHeroInstance * heroToBuy)
: CGoal(Goals::RECRUIT_HERO) : RecruitHero(townWithTavern)
{
objid = heroToBuy->id.getNum();
}
RecruitHero(const CGTownInstance * townWithTavern)
: ElementarGoal(Goals::RECRUIT_HERO)
{ {
priority = 1; priority = 1;
town = townWithTavern;
} }
virtual bool operator==(const RecruitHero & other) const override virtual bool operator==(const RecruitHero & other) const override
{ {
return true; return true;
} }
virtual std::string toString() const override;
}; };
} }

View File

@@ -16,8 +16,3 @@ bool Trade::operator==(const Trade & other) const
{ {
return resID == other.resID; return resID == other.resID;
} }
TSubgoal Trade::whatToDoToAchieve()
{
return iAmElementar();
}

View File

@@ -30,9 +30,7 @@ namespace Goals
resID = rid; resID = rid;
value = val; value = val;
objid = Objid; objid = Objid;
priority = 3; //trading is instant, but picking resources is free
} }
TSubgoal whatToDoToAchieve() override;
virtual bool operator==(const Trade & other) const override; virtual bool operator==(const Trade & other) const override;
}; };
} }

View File

@@ -9,6 +9,8 @@
*/ */
#pragma once #pragma once
#error not used
#include "CGoal.h" #include "CGoal.h"
struct HeroPtr; struct HeroPtr;
@@ -28,7 +30,6 @@ namespace Goals
: CGoal(Goals::VISIT_HERO) : CGoal(Goals::VISIT_HERO)
{ {
objid = hid; objid = hid;
priority = 4;
} }
virtual bool operator==(const VisitHero & other) const override; virtual bool operator==(const VisitHero & other) const override;
}; };

View File

@@ -38,6 +38,4 @@ VisitObj::VisitObj(int Objid)
tile = obj->visitablePos(); tile = obj->visitablePos();
else else
logAi->error("VisitObj constructed with invalid object instance %d", Objid); logAi->error("VisitObj constructed with invalid object instance %d", Objid);
priority = 3;
} }

View File

@@ -9,6 +9,8 @@
*/ */
#pragma once #pragma once
#error not used
#include "CGoal.h" #include "CGoal.h"
struct HeroPtr; struct HeroPtr;

View File

@@ -9,6 +9,8 @@
*/ */
#pragma once #pragma once
#error not used
#include "CGoal.h" #include "CGoal.h"
struct HeroPtr; struct HeroPtr;
@@ -27,7 +29,6 @@ namespace Goals
: CGoal(Goals::VISIT_TILE) : CGoal(Goals::VISIT_TILE)
{ {
tile = Tile; tile = Tile;
priority = 5;
} }
virtual bool operator==(const VisitTile & other) const override; virtual bool operator==(const VisitTile & other) const override;
}; };

View File

@@ -299,15 +299,6 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
requestActionASAP([=]() 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 auto transferFrom2to1 = [this](const CGHeroInstance * h1, const CGHeroInstance * h2) -> void
{ {
this->pickBestCreatures(h1, h2); 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."); logAi->debug("Heroes owned by different players. Do not exchange army or artifacts.");
} }
else if(nullkiller) else
{ {
if(nullkiller->isActive(firstHero)) if(nullkiller->isActive(firstHero))
transferFrom2to1(secondHero, firstHero); transferFrom2to1(secondHero, firstHero);
else else
transferFrom2to1(firstHero, secondHero); 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); answerQuery(query, 0);
}); });
@@ -1403,7 +1379,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
if(path.nodes.empty()) if(path.nodes.empty())
{ {
logAi->error("Hero %s cannot reach %s.", h->name, dst.toString()); 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; 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; 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) void VCAI::tryRealize(Goals::RecruitHero & g)
{ {
const CGTownInstance * t = g.town; 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) void VCAI::tryRealize(Goals::BuildThis & g)
{ {
auto b = BuildingID(g.bid); auto b = BuildingID(g.bid);
@@ -1773,7 +1694,7 @@ void VCAI::tryRealize(Goals::Invalid & g)
void VCAI::tryRealize(Goals::AbstractGoal & 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 !"); throw cannotFulfillGoalException("Unknown type of goal !");
} }
@@ -1786,42 +1707,6 @@ const CGTownInstance * VCAI::findTownWithTavern() const
return nullptr; 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<HeroPtr> VCAI::getUnblockedHeroes() const
{
std::vector<HeroPtr> 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 HeroPtr VCAI::primaryHero() const
{ {
auto hs = cb->getHeroesInfo(); auto hs = cb->getHeroesInfo();
@@ -1876,7 +1761,7 @@ void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
if(t->visitingHero) if(t->visitingHero)
moveHeroToTile(t->visitablePos(), t->visitingHero.get()); moveHeroToTile(t->visitablePos(), t->visitingHero.get());
throw goalFulfilledException(sptr(Goals::RecruitHero().settown(t))); throw goalFulfilledException(sptr(Goals::RecruitHero(t)));
} }
else if(throwing) else if(throwing)
{ {

View File

@@ -117,11 +117,7 @@ public:
virtual ~VCAI(); virtual ~VCAI();
//TODO: use only smart pointers? //TODO: use only smart pointers?
void tryRealize(Goals::Explore & g);
void tryRealize(Goals::RecruitHero & 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::BuildThis & g);
void tryRealize(Goals::DigAtTile & g); void tryRealize(Goals::DigAtTile & g);
void tryRealize(Goals::Trade & g); void tryRealize(Goals::Trade & g);
@@ -241,9 +237,6 @@ public:
const CGTownInstance * findTownWithTavern() const; const CGTownInstance * findTownWithTavern() const;
bool canRecruitAnyHero(const CGTownInstance * t = NULL) const; bool canRecruitAnyHero(const CGTownInstance * t = NULL) const;
Goals::TSubgoal getGoal(HeroPtr h) const;
bool canAct(HeroPtr h) const;
std::vector<HeroPtr> getUnblockedHeroes() const;
std::vector<HeroPtr> getMyHeroes() const; std::vector<HeroPtr> getMyHeroes() const;
HeroPtr primaryHero() const; HeroPtr primaryHero() const;
@@ -374,7 +367,7 @@ public:
explicit goalFulfilledException(Goals::TSubgoal Goal) explicit goalFulfilledException(Goals::TSubgoal Goal)
: goal(Goal) : goal(Goal)
{ {
msg = goal->name(); msg = goal->toString();
} }
virtual ~goalFulfilledException() throw () virtual ~goalFulfilledException() throw ()