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
#include "../VCAI.h"
#error REMOVE THIS FILE
class Behavior
class Behavior : public Goals::AbstractGoal
{
public:
virtual Goals::TGoalVec getTasks() = 0;
virtual std::string toString() const = 0;
};

View File

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

View File

@@ -10,17 +10,24 @@
#pragma once
#include "lib/VCMI_Lib.h"
#include "Behavior.h"
#include "../AIUtility.h"
#include "../Goals/CGoal.h"
class BuildingBehavior : public Behavior
namespace Goals
{
class BuildingBehavior : public CGoal<BuildingBehavior>
{
public:
BuildingBehavior()
{
}
virtual Goals::TGoalVec getTasks() override;
virtual Goals::TGoalVec decompose() 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";
}
Goals::TGoalVec BuyArmyBehavior::getTasks()
Goals::TGoalVec BuyArmyBehavior::decompose() const
{
Goals::TGoalVec tasks;

View File

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

View File

@@ -10,10 +10,13 @@
#pragma once
#include "lib/VCMI_Lib.h"
#include "Behavior.h"
#include "../AIUtility.h"
#include "../Goals/CGoal.h"
class CaptureObjectsBehavior : public Behavior {
namespace Goals
{
class CaptureObjectsBehavior : public CGoal<CaptureObjectsBehavior>
{
private:
std::vector<int> objectTypes;
std::vector<int> objectSubTypes;
@@ -39,22 +42,30 @@ public:
specificObjects = true;
}
virtual Goals::TGoalVec getTasks() override;
virtual Goals::TGoalVec decompose() const override;
virtual std::string toString() const override;
CaptureObjectsBehavior & ofType(int type) {
CaptureObjectsBehavior & ofType(int type)
{
objectTypes.push_back(type);
return *this;
}
CaptureObjectsBehavior & ofType(int type, int subType) {
CaptureObjectsBehavior & ofType(int type, int subType)
{
objectTypes.push_back(type);
objectSubTypes.push_back(subType);
return *this;
}
virtual bool operator==(const CaptureObjectsBehavior & other) const override
{
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 "../AIUtility.h"
#include "../Goals/BuyArmy.h"
#include "../Goals/VisitTile.h"
#include "../Goals/ExecuteHeroChain.h"
#include "../Goals/DismissHero.h"
#include "../Goals/ExchangeSwapTownHeroes.h"
@@ -32,7 +31,7 @@ std::string DefenceBehavior::toString() const
return "Defend towns";
}
Goals::TGoalVec DefenceBehavior::getTasks()
Goals::TGoalVec DefenceBehavior::decompose() const
{
Goals::TGoalVec tasks;
@@ -65,7 +64,7 @@ uint64_t townArmyIncome(const CGTownInstance * town)
return result;
}
void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town)
void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const
{
auto basicPriority = 0.3f + std::sqrt(townArmyIncome(town) / 20000.0f)
+ town->dailyIncome()[Res::GOLD] / 10000.0f;
@@ -163,7 +162,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
if(cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES)
{
logAi->debug("Hero %s can be recruited to defend %s", hero->name, town->name);
tasks.push_back(Goals::sptr(Goals::RecruitHero().settown(town).setobjid(hero->id.getNum()).setpriority(1)));
tasks.push_back(Goals::sptr(Goals::RecruitHero(town, hero).setpriority(1)));
continue;
}
else

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,16 +10,24 @@
#pragma once
#include "lib/VCMI_Lib.h"
#include "Behavior.h"
#include "../Goals/CGoal.h"
#include "../AIUtility.h"
class RecruitHeroBehavior : public Behavior
namespace Goals
{
class RecruitHeroBehavior : public CGoal<RecruitHeroBehavior>
{
public:
RecruitHeroBehavior()
{
}
virtual Goals::TGoalVec getTasks() override;
virtual TGoalVec decompose() 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;
}
Goals::TGoalVec StartupBehavior::getTasks()
Goals::TGoalVec StartupBehavior::decompose() const
{
Goals::TGoalVec tasks;
auto towns = cb->getTownsInfo();
@@ -166,7 +166,7 @@ Goals::TGoalVec StartupBehavior::getTasks()
if(tasks.empty() && canRecruitHero && !startupTown->visitingHero)
{
tasks.push_back(Goals::sptr(Goals::RecruitHero()));
tasks.push_back(Goals::sptr(Goals::RecruitHero(startupTown)));
}
if(tasks.empty() && towns.size())

View File

@@ -10,17 +10,25 @@
#pragma once
#include "lib/VCMI_Lib.h"
#include "Behavior.h"
#include "../Goals/CGoal.h"
#include "../AIUtility.h"
class StartupBehavior : public Behavior
namespace Goals
{
class StartupBehavior : public CGoal<StartupBehavior>
{
public:
StartupBehavior()
{
}
virtual Goals::TGoalVec getTasks() override;
virtual TGoalVec decompose() 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/BuyArmy.cpp
Goals/AdventureSpellCast.cpp
Goals/Win.cpp
Goals/VisitTile.cpp
Goals/VisitObj.cpp
Goals/VisitHero.cpp
Goals/CollectRes.cpp
Goals/Trade.cpp
Goals/RecruitHero.cpp
Goals/DigAtTile.cpp
Goals/GetArtOfType.cpp
Goals/FindObj.cpp
Goals/CompleteQuest.cpp
Goals/ExecuteHeroChain.cpp
Goals/ExchangeSwapTownHeroes.cpp
Engine/Nullkiller.cpp
Engine/PriorityEvaluator.cpp
Analyzers/DangerHitMapAnalyzer.cpp
Analyzers/BuildAnalyzer.cpp
Behaviors/Behavior.cpp
Behaviors/CaptureObjectsBehavior.cpp
Behaviors/RecruitHeroBehavior.cpp
Behaviors/BuyArmyBehavior.cpp
@@ -52,6 +46,7 @@ set(VCAI_SRCS
Behaviors/StartupBehavior.cpp
Behaviors/BuildingBehavior.cpp
Behaviors/GatherArmyBehavior.cpp
Behaviors/CompleteQuestBehavior.cpp
main.cpp
VCAI.cpp
)
@@ -88,17 +83,12 @@ set(VCAI_HEADERS
Goals/GatherTroops.h
Goals/BuyArmy.h
Goals/AdventureSpellCast.h
Goals/Win.h
Goals/VisitTile.h
Goals/VisitObj.h
Goals/VisitHero.h
Goals/CollectRes.h
Goals/Trade.h
Goals/RecruitHero.h
Goals/DigAtTile.h
Goals/GetArtOfType.h
Goals/FindObj.h
Goals/CompleteQuest.h
Goals/ExecuteHeroChain.h
Goals/ExchangeSwapTownHeroes.h
Goals/Goals.h
@@ -106,7 +96,6 @@ set(VCAI_HEADERS
Engine/PriorityEvaluator.h
Analyzers/DangerHitMapAnalyzer.h
Analyzers/BuildAnalyzer.h
Behaviors/Behavior.h
Behaviors/CaptureObjectsBehavior.h
Behaviors/RecruitHeroBehavior.h
Behaviors/BuyArmyBehavior.h
@@ -114,6 +103,7 @@ set(VCAI_HEADERS
Behaviors/StartupBehavior.h
Behaviors/BuildingBehavior.h
Behaviors/GatherArmyBehavior.h
Behaviors/CompleteQuestBehavior.h
VCAI.h
)

View File

@@ -23,6 +23,8 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
using namespace Goals;
Nullkiller::Nullkiller()
{
priorityEvaluator.reset(new PriorityEvaluator());
@@ -30,38 +32,75 @@ Nullkiller::Nullkiller()
buildAnalyzer.reset(new BuildAnalyzer());
}
Goals::TSubgoal Nullkiller::choseBestTask(Goals::TGoalVec & tasks) const
Goals::TTask Nullkiller::choseBestTask(Goals::TTaskVec & tasks) const
{
Goals::TSubgoal bestTask = *vstd::maxElementByFun(tasks, [](Goals::TSubgoal goal) -> float{
return goal->priority;
Goals::TTask bestTask = *vstd::maxElementByFun(tasks, [](Goals::TTask task) -> float{
return task->priority;
});
return bestTask;
}
Goals::TSubgoal Nullkiller::choseBestTask(std::shared_ptr<Behavior> behavior) const
Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const
{
logAi->debug("Checking behavior %s", behavior->toString());
auto tasks = behavior->getTasks();
const int MAX_DEPTH = 10;
Goals::TGoalVec goals[MAX_DEPTH + 1];
Goals::TTaskVec tasks;
goals[0] = {behavior};
if(tasks.empty())
{
logAi->debug("Behavior %s found no tasks", behavior->toString());
return Goals::sptr(Goals::Invalid());
return Goals::taskptr(Goals::Invalid());
}
logAi->trace("Evaluating priorities, tasks count %d", tasks.size());
for(auto task : tasks)
int depth = 0;
while(goals[0].size())
{
task->setpriority(priorityEvaluator->evaluate(task));
TSubgoal current = goals[depth].back();
TGoalVec subgoals = current->decompose();
goals[depth + 1].clear();
for(auto subgoal : subgoals)
{
if(subgoal->isElementar)
{
auto task = taskptr(*subgoal);
if(task->priority <= 0)
task->priority = priorityEvaluator->evaluate(subgoal);
tasks.push_back(task);
}
else
{
goals[depth + 1].push_back(subgoal);
}
}
if(goals[depth + 1].size() && depth < MAX_DEPTH)
{
depth++;
}
else
{
while(depth > 0 && goals[depth].empty())
{
depth--;
}
}
}
auto task = choseBestTask(tasks);
logAi->debug("Behavior %s returns %s(%s), priority %f", behavior->toString(), task->name(), task->tile.toString(), task->priority);
logAi->debug("Behavior %s returns %s, priority %f", behavior->toString(), task->toString(), task->priority);
return task;
}
@@ -141,54 +180,49 @@ void Nullkiller::makeTurn()
{
updateAiState();
Goals::TGoalVec bestTasks = {
choseBestTask(std::make_shared<BuyArmyBehavior>()),
choseBestTask(std::make_shared<CaptureObjectsBehavior>()),
choseBestTask(std::make_shared<RecruitHeroBehavior>()),
choseBestTask(std::make_shared<DefenceBehavior>()),
choseBestTask(std::make_shared<BuildingBehavior>()),
choseBestTask(std::make_shared<GatherArmyBehavior>())
Goals::TTaskVec bestTasks = {
choseBestTask(sptr(BuyArmyBehavior())),
choseBestTask(sptr(CaptureObjectsBehavior())),
choseBestTask(sptr(RecruitHeroBehavior())),
choseBestTask(sptr(DefenceBehavior())),
choseBestTask(sptr(BuildingBehavior())),
choseBestTask(sptr(GatherArmyBehavior()))
};
if(cb->getDate(Date::DAY) == 1)
{
bestTasks.push_back(choseBestTask(std::make_shared<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.");
return;
}
}*/
if(bestTask->priority < MIN_PRIORITY)
{
logAi->trace("Goal %s has too low priority. It is not worth doing it. Ending turn.", bestTask->name());
logAi->trace("Goal %s has too low priority. It is not worth doing it. Ending turn.", bestTask->toString());
return;
}
logAi->debug("Trying to realize %s (value %2.3f)", bestTask->name(), bestTask->priority);
logAi->debug("Trying to realize %s (value %2.3f)", bestTask->toString(), bestTask->priority);
try
{
if(bestTask->hero)
{
setActive(bestTask->hero.get(), bestTask->tile);
}
bestTask->accept(ai.get());
}
catch(goalFulfilledException &)
{
logAi->trace("Task %s completed", bestTask->name());
logAi->trace("Task %s completed", bestTask->toString());
}
catch(std::exception & e)
{
logAi->debug("Failed to realize subgoal of type %s, I will stop.", bestTask->name());
logAi->debug("Failed to realize subgoal of type %s, I will stop.", bestTask->toString());
logAi->debug("The error message was: %s", e.what());
return;

View File

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

View File

@@ -520,9 +520,6 @@ Goals::EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgo
/// importance
float PriorityEvaluator::evaluate(Goals::TSubgoal task)
{
if(task->priority > 0)
return task->priority;
auto evaluationContext = buildEvaluationContext(task);
int rewardType = (evaluationContext.goldReward > 0 ? 1 : 0)
@@ -561,7 +558,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace("Evaluated %s, loss: %f, turns main: %f, scout: %f, gold: %d, cost: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, cwr: %f, result %f",
task->name(),
task->toString(),
evaluationContext.armyLossPersentage,
evaluationContext.movementCostByRole[HeroRole::MAIN],
evaluationContext.movementCostByRole[HeroRole::SCOUT],

View File

@@ -89,19 +89,6 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army)
return as;
}
float HeroMovementGoalEngineBase::calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const
{
if(goal.evaluationContext.movementCost > 0)
{
return goal.evaluationContext.movementCost;
}
else
{
auto pathInfo = ai->myCb->getPathsInfo(goal.hero.h)->getPathInfo(goal.tile);
return pathInfo->cost;
}
}
TacticalAdvantageEngine::TacticalAdvantageEngine()
{
try
@@ -254,206 +241,3 @@ float TacticalAdvantageEngine::getTacticalAdvantage(const CArmedInstance * we, c
return output;
}
//std::shared_ptr<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::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;
}
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;
switch(goalType)
@@ -124,11 +136,6 @@ bool AbstractGoal::invalid() const
return goalType == EGoals::INVALID;
}
void AbstractGoal::accept(VCAI * ai)
{
ai->tryRealize(*this);
}
EvaluationContext::EvaluationContext()
: movementCost(0.0),
manaCost(0),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,7 +29,7 @@ bool BuildThis::operator==(const BuildThis & other) const
return town == other.town && bid == other.bid;
}
std::string BuildThis::name() const
std::string BuildThis::toString() const
{
return "Build " + buildingInfo.name + "(" + std::to_string(bid) + ") in " + town->name;
}

View File

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

View File

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

View File

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

View File

@@ -23,9 +23,8 @@ namespace Goals
public:
CGoal<T>(EGoals goal = INVALID) : AbstractGoal(goal)
{
priority = 0;
isElementar = false;
isAbstract = false;
isAbstract = true;
value = 0;
aid = -1;
objid = -1;
@@ -36,7 +35,6 @@ namespace Goals
OSETTER(bool, isElementar)
OSETTER(bool, isAbstract)
OSETTER(float, priority)
OSETTER(int, value)
OSETTER(int, resID)
OSETTER(int, objid)
@@ -46,11 +44,6 @@ namespace Goals
OSETTER(CGTownInstance *, town)
OSETTER(int, bid)
void accept(VCAI * ai) override
{
ai->tryRealize(static_cast<T &>(*this)); //casting enforces template instantiation
}
CGoal<T> * clone() const override
{
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 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;
}
TGoalVec CollectRes::getAllPossibleSubgoals()
{
TGoalVec ret;
//TGoalVec CollectRes::getAllPossibleSubgoals()
//{
// TGoalVec ret;
//
//auto givesResource = [this](const CGObjectInstance * obj) -> bool
//{
// //TODO: move this logic to object side
@@ -107,18 +107,18 @@ TGoalVec CollectRes::getAllPossibleSubgoals()
// vstd::concatenate(ret, waysToGo);
// }
//}
return ret;
}
// return ret;
//}
TSubgoal CollectRes::whatToDoToAchieve()
{
auto goals = getAllPossibleSubgoals();
auto trade = whatToDoToTrade();
if (!trade->invalid())
goals.push_back(trade);
return sptr(Invalid()); //we can always do that
}
//TSubgoal CollectRes::whatToDoToAchieve()
//{
// auto goals = getAllPossibleSubgoals();
// auto trade = whatToDoToTrade();
// if (!trade->invalid())
// goals.push_back(trade);
//
// return sptr(Invalid()); //we can always do that
//}
TSubgoal CollectRes::whatToDoToTrade()
{

View File

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

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;
}
TSubgoal DigAtTile::whatToDoToAchieve()
{
const CGObjectInstance * firstObj = vstd::frontOrNull(cb->getVisitableObjs(tile));
if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest
{
const CGHeroInstance * h = dynamic_cast<const CGHeroInstance *>(firstObj);
sethero(h).setisElementar(true);
return sptr(*this);
}
return sptr(VisitTile(tile));
}
//
//TSubgoal DigAtTile::decomposeSingle() const
//{
// const CGObjectInstance * firstObj = vstd::frontOrNull(cb->getVisitableObjs(tile));
// if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest
// {
// const CGHeroInstance * h = dynamic_cast<const CGHeroInstance *>(firstObj);
// sethero(h).setisElementar(true);
// return sptr(*this);
// }
//
// return sptr(VisitTile(tile));
//}

View File

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

View File

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

View File

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

View File

@@ -27,11 +27,11 @@ ExchangeSwapTownHeroes::ExchangeSwapTownHeroes(
const CGTownInstance * town,
const CGHeroInstance * garrisonHero,
HeroLockedReason lockingReason)
:CGoal(Goals::EXCHANGE_SWAP_TOWN_HEROES), town(town), garrisonHero(garrisonHero), lockingReason(lockingReason)
:ElementarGoal(Goals::EXCHANGE_SWAP_TOWN_HEROES), town(town), garrisonHero(garrisonHero), lockingReason(lockingReason)
{
}
std::string ExchangeSwapTownHeroes::name() const
std::string ExchangeSwapTownHeroes::toString() const
{
return "Exchange and swap heroes of " + town->name;
}
@@ -41,11 +41,6 @@ bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) co
return town == other.town;
}
TSubgoal ExchangeSwapTownHeroes::whatToDoToAchieve()
{
return iAmElementar();
}
void ExchangeSwapTownHeroes::accept(VCAI * ai)
{
if(!garrisonHero)

View File

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

View File

@@ -24,23 +24,12 @@ extern FuzzyHelper * fh;
using namespace Goals;
ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj)
:CGoal(Goals::EXECUTE_HERO_CHAIN), chainPath(path)
:ElementarGoal(Goals::EXECUTE_HERO_CHAIN), chainPath(path)
{
evaluationContext.danger = path.getTotalDanger();
evaluationContext.movementCost = path.movementCost();
evaluationContext.armyLoss = path.getTotalArmyLoss();
evaluationContext.heroStrength = path.getHeroStrength();
hero = path.targetHero;
tile = path.targetTile();
for(auto & node : path.nodes)
{
auto role = ai->ah->getHeroRole(node.targetHero);
evaluationContext.movementCostByRole[role] += node.cost;
}
if(obj)
{
objid = obj->id.getNum();
@@ -57,15 +46,12 @@ bool ExecuteHeroChain::operator==(const ExecuteHeroChain & other) const
return false;
}
TSubgoal ExecuteHeroChain::whatToDoToAchieve()
{
return iAmElementar();
}
void ExecuteHeroChain::accept(VCAI * ai)
{
logAi->debug("Executing hero chain towards %s. Path %s", targetName, chainPath.toString());
ai->nullkiller->setActive(chainPath.targetHero, tile);
std::set<int> blockedIndexes;
for(int i = chainPath.nodes.size() - 1; i >= 0; i--)
@@ -89,7 +75,6 @@ void ExecuteHeroChain::accept(VCAI * ai)
{
if(hero->movement)
{
ai->nullkiller->setActive(hero, node.coord);
if(node.specialAction)
@@ -98,6 +83,8 @@ void ExecuteHeroChain::accept(VCAI * ai)
{
auto specialGoal = node.specialAction->whatToDo(hero);
if(!specialGoal->isElementar)
specialGoal->accept(ai);
}
else
@@ -135,7 +122,10 @@ void ExecuteHeroChain::accept(VCAI * ai)
{
try
{
Goals::VisitTile(node.coord).sethero(hero).accept(ai);
if(moveHeroToTile(hero, node.coord))
{
continue;
}
}
catch(cannotFulfillGoalException)
{
@@ -195,7 +185,19 @@ void ExecuteHeroChain::accept(VCAI * ai)
}
}
std::string ExecuteHeroChain::name() const
std::string ExecuteHeroChain::toString() const
{
return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->name;
}
bool ExecuteHeroChain::moveHeroToTile(const CGHeroInstance * hero, const int3 & tile)
{
if(g.tile == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2)
{
logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile.toString());
return true;
}
return ai->moveHeroToTile(tile, hero);
}

View File

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

View File

@@ -23,32 +23,32 @@ bool FindObj::operator==(const FindObj & other) const
{
return other.hero.h == hero.h && other.objid == objid;
}
TSubgoal FindObj::whatToDoToAchieve()
{
const CGObjectInstance * o = nullptr;
if(resID > -1) //specified
{
for(const CGObjectInstance * obj : ai->visitableObjs)
{
if(obj->ID == objid && obj->subID == resID)
{
o = obj;
break; //TODO: consider multiple objects and choose best
}
}
}
else
{
for(const CGObjectInstance * obj : ai->visitableObjs)
{
if(obj->ID == objid)
{
o = obj;
break; //TODO: consider multiple objects and choose best
}
}
}
if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is
return sptr(VisitObj(o->id.getNum()));
}
//
//TSubgoal FindObj::whatToDoToAchieve()
//{
// const CGObjectInstance * o = nullptr;
// if(resID > -1) //specified
// {
// for(const CGObjectInstance * obj : ai->visitableObjs)
// {
// if(obj->ID == objid && obj->subID == resID)
// {
// o = obj;
// break; //TODO: consider multiple objects and choose best
// }
// }
// }
// else
// {
// for(const CGObjectInstance * obj : ai->visitableObjs)
// {
// if(obj->ID == objid)
// {
// o = obj;
// break; //TODO: consider multiple objects and choose best
// }
// }
// }
// if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is
// return sptr(VisitObj(o->id.getNum()));
//}

View File

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

View File

@@ -43,30 +43,30 @@ int GatherTroops::getCreaturesCount(const CArmedInstance * army)
return count;
}
//
//TSubgoal GatherTroops::whatToDoToAchieve()
//{
// logAi->trace("Entering GatherTroops::whatToDoToAchieve");
//
// auto heroes = cb->getHeroesInfo(true);
//
// for(auto hero : heroes)
// {
// if(getCreaturesCount(hero) >= this->value)
// {
// logAi->trace("Completing GATHER_TROOPS by hero %s", hero->name);
//
// throw goalFulfilledException(sptr(*this));
// }
// }
//
// return sptr(Invalid());
//}
TSubgoal GatherTroops::whatToDoToAchieve()
{
logAi->trace("Entering GatherTroops::whatToDoToAchieve");
auto heroes = cb->getHeroesInfo(true);
for(auto hero : heroes)
{
if(getCreaturesCount(hero) >= this->value)
{
logAi->trace("Completing GATHER_TROOPS by hero %s", hero->name);
throw goalFulfilledException(sptr(*this));
}
}
return sptr(Invalid());
}
TGoalVec GatherTroops::getAllPossibleSubgoals()
{
TGoalVec solutions;
//
//TGoalVec GatherTroops::getAllPossibleSubgoals()
//{
// TGoalVec solutions;
//for(const CGTownInstance * t : cb->getTownsInfo())
//{
@@ -136,6 +136,6 @@ TGoalVec GatherTroops::getAllPossibleSubgoals()
// return goal->hero && !goal->hero->getSlotFor(creID).validSlot() && !goal->hero->getFreeSlot().validSlot();
//});
return solutions;
//TODO: exchange troops between heroes
}
// return solutions;
// //TODO: exchange troops between heroes
//}

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,26 +16,4 @@ class VCAI;
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;
using namespace Goals;
std::string RecruitHero::toString() const
{
return "Recruit hero at " + town->name;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -299,15 +299,6 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
requestActionASAP([=]()
{
float goalpriority1 = 0, goalpriority2 = 0;
auto firstGoal = getGoal(firstHero);
if(firstGoal->goalType == Goals::GATHER_ARMY)
goalpriority1 = firstGoal->priority;
auto secondGoal = getGoal(secondHero);
if(secondGoal->goalType == Goals::GATHER_ARMY)
goalpriority2 = secondGoal->priority;
auto transferFrom2to1 = [this](const CGHeroInstance * h1, const CGHeroInstance * h2) -> void
{
this->pickBestCreatures(h1, h2);
@@ -320,28 +311,13 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
{
logAi->debug("Heroes owned by different players. Do not exchange army or artifacts.");
}
else if(nullkiller)
else
{
if(nullkiller->isActive(firstHero))
transferFrom2to1(secondHero, firstHero);
else
transferFrom2to1(firstHero, secondHero);
}
else if(goalpriority1 > goalpriority2)
{
transferFrom2to1(firstHero, secondHero);
}
else if(goalpriority1 < goalpriority2)
{
transferFrom2to1(secondHero, firstHero);
}
else //regular criteria
{
if(firstHero->getFightingStrength() > secondHero->getFightingStrength() && ah->canGetArmy(firstHero, secondHero))
transferFrom2to1(firstHero, secondHero);
else if(ah->canGetArmy(secondHero, firstHero))
transferFrom2to1(secondHero, firstHero);
}
answerQuery(query, 0);
});
@@ -1403,7 +1379,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
if(path.nodes.empty())
{
logAi->error("Hero %s cannot reach %s.", h->name, dst.toString());
throw goalFulfilledException(sptr(Goals::VisitTile(dst).sethero(h)));
return true;
}
int i = path.nodes.size() - 1;
@@ -1568,11 +1544,6 @@ void VCAI::buildStructure(const CGTownInstance * t, BuildingID building)
cb->buildBuilding(t, building); //just do this;
}
void VCAI::tryRealize(Goals::Explore & g)
{
throw cannotFulfillGoalException("EXPLORE is not an elementar goal!");
}
void VCAI::tryRealize(Goals::RecruitHero & g)
{
const CGTownInstance * t = g.town;
@@ -1591,56 +1562,6 @@ void VCAI::tryRealize(Goals::RecruitHero & g)
}
}
void VCAI::tryRealize(Goals::VisitTile & g)
{
if(!g.hero->movement)
throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!");
if(g.tile == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2)
{
logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile.toString());
throw goalFulfilledException(sptr(g));
}
if(ai->moveHeroToTile(g.tile, g.hero.get()))
{
throw goalFulfilledException(sptr(g));
}
}
void VCAI::tryRealize(Goals::VisitObj & g)
{
auto position = g.tile;
if(!g.hero->movement)
throw cannotFulfillGoalException("Cannot visit object: hero is out of MPs!");
if(position == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2)
{
logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile.toString());
throw goalFulfilledException(sptr(g));
}
if(ai->moveHeroToTile(position, g.hero.get()))
{
throw goalFulfilledException(sptr(g));
}
}
void VCAI::tryRealize(Goals::VisitHero & g)
{
if(!g.hero->movement)
throw cannotFulfillGoalException("Cannot visit target hero: hero is out of MPs!");
const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(g.objid));
if(obj)
{
if(ai->moveHeroToTile(obj->visitablePos(), g.hero.get()))
{
throw goalFulfilledException(sptr(g));
}
}
else
{
throw cannotFulfillGoalException("Cannot visit hero: object not found!");
}
}
void VCAI::tryRealize(Goals::BuildThis & g)
{
auto b = BuildingID(g.bid);
@@ -1773,7 +1694,7 @@ void VCAI::tryRealize(Goals::Invalid & g)
void VCAI::tryRealize(Goals::AbstractGoal & g)
{
logAi->debug("Attempting realizing goal with code %s", g.name());
logAi->debug("Attempting realizing goal with code %s", g.toString());
throw cannotFulfillGoalException("Unknown type of goal !");
}
@@ -1786,42 +1707,6 @@ const CGTownInstance * VCAI::findTownWithTavern() const
return nullptr;
}
Goals::TSubgoal VCAI::getGoal(HeroPtr h) const
{
auto it = lockedHeroes.find(h);
if(it != lockedHeroes.end())
return it->second;
else
return sptr(Goals::Invalid());
}
std::vector<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
{
auto hs = cb->getHeroesInfo();
@@ -1876,7 +1761,7 @@ void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
if(t->visitingHero)
moveHeroToTile(t->visitablePos(), t->visitingHero.get());
throw goalFulfilledException(sptr(Goals::RecruitHero().settown(t)));
throw goalFulfilledException(sptr(Goals::RecruitHero(t)));
}
else if(throwing)
{

View File

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