1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-26 03:52:01 +02:00

Template magic. Implemented method chaining + clone pattern for Goals.

This commit is contained in:
DjWarmonger 2013-11-15 17:30:16 +00:00
parent 2b3405fa50
commit 900d7a03f0
4 changed files with 240 additions and 179 deletions

View File

@ -18,7 +18,7 @@ extern boost::thread_specific_ptr<VCAI> ai;
using namespace vstd;
using namespace Goals;
std::string Goals::CGoal::name() const //TODO: virtualize
std::string Goals::AbstractGoal::name() const //TODO: virtualize
{
switch (goalType)
{
@ -67,7 +67,7 @@ std::string Goals::CGoal::name() const //TODO: virtualize
}
}
#define I_AM_ELEMENTAR return make_shared<Goals::CGoal>(setisElementar(true))
TSubgoal Win::whatToDoToAchieve()
{
@ -83,11 +83,11 @@ TSubgoal Win::whatToDoToAchieve()
switch(cond)
{
case EVictoryConditionType::ARTIFACT:
return make_shared<Goals::CGoal> (Goals::GetArtOfType().setaid(vc.objectId));
return sptr (Goals::GetArtOfType(vc.objectId));
case EVictoryConditionType::BEATHERO:
return make_shared<Goals::CGoal> (Goals::GetObj(vc.obj->id.getNum()));
return sptr (Goals::GetObj(vc.obj->id.getNum()));
case EVictoryConditionType::BEATMONSTER:
return make_shared<Goals::CGoal> (Goals::GetObj(vc.obj->id.getNum()));
return sptr (Goals::GetObj(vc.obj->id.getNum()));
case EVictoryConditionType::BUILDCITY:
//TODO build castle/capitol
break;
@ -99,7 +99,7 @@ TSubgoal Win::whatToDoToAchieve()
if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, BuildingID::GRAIL))
{
const CGTownInstance *t = h->visitedTown;
return make_shared<Goals::CGoal> (Goals::BuildThis().setbid(BuildingID::GRAIL).settown(t));
return sptr (Goals::BuildThis(BuildingID::GRAIL, t));
}
else
{
@ -113,7 +113,7 @@ TSubgoal Win::whatToDoToAchieve()
boost::sort(towns, isCloser);
if(towns.size())
{
return make_shared<Goals::CGoal> (Goals::VisitTile(towns.front()->visitablePos()).sethero(h));
return sptr (Goals::VisitTile(towns.front()->visitablePos()).sethero(h));
}
}
}
@ -121,25 +121,25 @@ TSubgoal Win::whatToDoToAchieve()
int3 grailPos = cb->getGrailPos(ratio);
if(ratio > 0.99)
{
return make_shared<Goals::CGoal> (Goals::DigAtTile().settile(grailPos));
return sptr (Goals::DigAtTile(grailPos));
} //TODO: use FIND_OBJ
else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
{
return make_shared<Goals::CGoal> (Goals::GetObj(obj->id.getNum()));
return sptr (Goals::GetObj(obj->id.getNum()));
}
else
return make_shared<Goals::CGoal> (Goals::Explore());
return sptr (Goals::Explore());
}
break;
case EVictoryConditionType::CAPTURECITY:
return make_shared<Goals::CGoal> (Goals::GetObj(vc.obj->id.getNum()));
return sptr (Goals::GetObj(vc.obj->id.getNum()));
case EVictoryConditionType::GATHERRESOURCE:
return make_shared<Goals::CGoal> (Goals::CollectRes().setresID(static_cast<Res::ERes>(vc.objectId)).setvalue(vc.count));
return sptr (Goals::CollectRes(static_cast<Res::ERes>(vc.objectId), vc.count));
//TODO mines? piles? marketplace?
//save?
break;
case EVictoryConditionType::GATHERTROOP:
return make_shared<Goals::CGoal> (Goals::GatherTroops().setobjid(vc.objectId).setvalue(vc.count));
return sptr (Goals::GatherTroops(vc.objectId, vc.count));
break;
case EVictoryConditionType::TAKEDWELLINGS:
break;
@ -148,11 +148,11 @@ TSubgoal Win::whatToDoToAchieve()
case EVictoryConditionType::TRANSPORTITEM:
break;
case EVictoryConditionType::WINSTANDARD:
return make_shared<Goals::CGoal> (Goals::Conquer());
return sptr (Goals::Conquer());
default:
assert(0);
}
return make_shared<Goals::CGoal> (Goals::INVALID);
return sptr (Goals::Invalid());
}
TSubgoal FindObj::whatToDoToAchieve()
@ -181,37 +181,40 @@ TSubgoal FindObj::whatToDoToAchieve()
}
}
if (o && isReachable(o))
return make_shared<Goals::CGoal> (Goals::GetObj(o->id.getNum()));
return sptr (Goals::GetObj(o->id.getNum()));
else
return make_shared<Goals::CGoal> (Goals::Explore());
return sptr (Goals::Explore());
}
TSubgoal GetObj::whatToDoToAchieve()
{
const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
if(!obj)
return make_shared<Goals::CGoal> (Goals::Explore());
return sptr (Goals::Explore());
int3 pos = obj->visitablePos();
return make_shared<Goals::CGoal> (Goals::VisitTile(pos));
return sptr (Goals::VisitTile(pos));
}
TSubgoal VisitHero::whatToDoToAchieve()
{
const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
if(!obj)
return make_shared<Goals::CGoal> (Goals::Explore());
return sptr (Goals::Explore());
int3 pos = obj->visitablePos();
if (hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
return make_shared<Goals::CGoal> (settile(pos).setisElementar(true));
return make_shared<Goals::CGoal> (Goals::INVALID);
{
settile(pos).setisElementar(true);
return sptr (*this);
}
return sptr (Goals::Invalid());
}
TSubgoal GetArtOfType::whatToDoToAchieve()
{
TSubgoal alternativeWay = CGoal::lookForArtSmart(aid); //TODO: use
if(alternativeWay->invalid())
return make_shared<Goals::CGoal> (Goals::FindObj(Obj::ARTIFACT, aid));
return make_shared<Goals::CGoal> (Goals::INVALID);
return sptr (Goals::FindObj(Obj::ARTIFACT, aid));
return sptr (Goals::Invalid());
}
TSubgoal ClearWayTo::whatToDoToAchieve()
@ -220,12 +223,12 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
if(!cb->isVisible(tile))
{
logAi->errorStream() << "Clear way should be used with visible tiles!";
return make_shared<Goals::CGoal> (Goals::Explore());
return sptr (Goals::Explore());
}
HeroPtr h = hero ? hero : ai->primaryHero();
if(!h)
return make_shared<Goals::CGoal> (Goals::RecruitHero());
return sptr (Goals::RecruitHero());
cb->setSelection(*h);
@ -238,7 +241,7 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
//if(isSafeToVisit(h, tileToHit))
if(isBlockedBorderGate(tileToHit))
{ //FIXME: this way we'll not visit gate and activate quest :?
return make_shared<Goals::CGoal> (Goals::FindObj (Obj::KEYMASTER, cb->getTile(tileToHit)->visitableObjects.back()->subID));
return sptr (Goals::FindObj (Obj::KEYMASTER, cb->getTile(tileToHit)->visitableObjects.back()->subID));
}
//FIXME: this code shouldn't be necessary
@ -256,7 +259,7 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
throw cannotFulfillGoalException(problem);
}
return make_shared<Goals::CGoal> (Goals::VisitTile(tileToHit).sethero(h));
return sptr (Goals::VisitTile(tileToHit).sethero(h));
//FIXME:: attempts to visit completely unreachable tile with hero results in stall
//TODO czy istnieje lepsza droga?
@ -296,7 +299,7 @@ TSubgoal Explore::whatToDoToAchieve()
auto pos = obj->visitablePos();
//FIXME: this confition fails if everything but guarded subterranen gate was explored. in this case we should gather army for hero
if (isSafeToVisit(hero, pos) && ai->isAccessibleForHero(pos, hero))
return make_shared<Goals::CGoal> (Goals::VisitTile(pos).sethero(hero));
return sptr (Goals::VisitTile(pos).sethero(hero));
}
}
else
@ -305,7 +308,7 @@ TSubgoal Explore::whatToDoToAchieve()
{
auto pos = obj->visitablePos();
if (ai->isAccessible (pos)) //TODO: check safety?
return make_shared<Goals::CGoal> (Goals::VisitTile(pos).sethero(hero));
return sptr (Goals::VisitTile(pos).sethero(hero));
}
}
}
@ -333,12 +336,12 @@ TSubgoal Explore::whatToDoToAchieve()
});
if (objs.size())
{
return make_shared<Goals::CGoal> (Goals::VisitTile(objs.front()->visitablePos()).sethero(hero).setisAbstract(true));
return sptr (Goals::VisitTile(objs.front()->visitablePos()).sethero(hero).setisAbstract(true));
}
else
throw cannotFulfillGoalException("Cannot explore - no possible ways found!");
}
return make_shared<Goals::CGoal> (Goals::VisitTile(t).sethero(hero));
return sptr (Goals::VisitTile(t).sethero(hero));
}
auto hs = cb->getHeroesInfo();
@ -351,7 +354,7 @@ TSubgoal Explore::whatToDoToAchieve()
if(hs.empty()) //all heroes are busy. buy new one
{
if (howManyHeroes < 3 && ai->findTownWithTavern()) //we may want to recruit second hero. TODO: make it smart finally
return make_shared<Goals::CGoal> (Goals::RecruitHero());
return sptr (Goals::RecruitHero());
else //find mobile hero with weakest army
{
hs = cb->getHeroesInfo();
@ -362,7 +365,7 @@ TSubgoal Explore::whatToDoToAchieve()
if (hs.empty())
{
if (howManyHeroes < GameConstants::MAX_HEROES_PER_PLAYER)
return make_shared<Goals::CGoal> (Goals::RecruitHero());
return sptr (Goals::RecruitHero());
else
throw cannotFulfillGoalException("No heroes with remaining MPs for exploring!\n");
}
@ -372,27 +375,27 @@ TSubgoal Explore::whatToDoToAchieve()
const CGHeroInstance *h = hs.front();
return make_shared<Goals::CGoal> (sethero(h).setisAbstract(true));
return sptr (sethero(h).setisAbstract(true));
I_AM_ELEMENTAR; //FIXME: how can this be called?
return iAmElementar(); //FIXME: how can this be called?
};
TSubgoal RecruitHero::whatToDoToAchieve()
{
const CGTownInstance *t = ai->findTownWithTavern();
if(!t)
return make_shared<Goals::CGoal> (Goals::BuildThis().setbid(BuildingID::TAVERN));
return sptr (Goals::BuildThis(BuildingID::TAVERN));
if(cb->getResourceAmount(Res::GOLD) < HERO_GOLD_COST)
return make_shared<Goals::CGoal> (Goals::CollectRes().setresID(Res::GOLD).setvalue(HERO_GOLD_COST));
return sptr (Goals::CollectRes(Res::GOLD, HERO_GOLD_COST));
I_AM_ELEMENTAR;
return iAmElementar();
}
TSubgoal VisitTile::whatToDoToAchieve()
{
if(!cb->isVisible(tile))
return make_shared<Goals::CGoal> (Goals::Explore());
return sptr (Goals::Explore());
if(hero && !ai->isAccessibleForHero(tile, hero))
hero = nullptr;
@ -401,7 +404,7 @@ TSubgoal VisitTile::whatToDoToAchieve()
{
if(cb->getHeroesInfo().empty())
{
return make_shared<Goals::CGoal> (Goals::RecruitHero());
return sptr (Goals::RecruitHero());
}
for(const CGHeroInstance *h : cb->getHeroesInfo())
@ -417,15 +420,15 @@ TSubgoal VisitTile::whatToDoToAchieve()
if(hero)
{
if(isSafeToVisit(hero, tile))
return make_shared<Goals::CGoal>(setisElementar(true));
return sptr (setisElementar(true));
else
{
return make_shared<Goals::CGoal>(Goals::GatherArmy().sethero(hero).setvalue(evaluateDanger(tile, *hero) * SAFE_ATTACK_CONSTANT)); //TODO: should it be abstract?
return sptr (Goals::GatherArmy(evaluateDanger(tile, *hero) * SAFE_ATTACK_CONSTANT).sethero(hero));
}
}
else //inaccessible for all heroes
{
return make_shared<Goals::CGoal>(Goals::ClearWayTo(tile));
return sptr (Goals::ClearWayTo(tile));
}
}
@ -435,10 +438,11 @@ TSubgoal DigAtTile::whatToDoToAchieve()
if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest
{
const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(firstObj);
return make_shared<Goals::CGoal> (sethero(h).setisElementar(true));
sethero(h).setisElementar(true);
return sptr (*this);
}
return make_shared<Goals::CGoal>(Goals::VisitTile(tile));
return sptr (Goals::VisitTile(tile));
}
TSubgoal BuildThis::whatToDoToAchieve()
@ -446,7 +450,7 @@ TSubgoal BuildThis::whatToDoToAchieve()
//TODO check res
//look for town
//prerequisites?
I_AM_ELEMENTAR;
return iAmElementar();
}
TSubgoal CollectRes::whatToDoToAchieve()
@ -482,7 +486,7 @@ TSubgoal CollectRes::whatToDoToAchieve()
for(const CGTownInstance *t : cb->getTownsInfo())
{
if(cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
return make_shared<Goals::CGoal>(Goals::BuildThis().settown(t).setbid(BuildingID::MARKETPLACE));
return sptr (Goals::BuildThis(BuildingID::MARKETPLACE, t));
}
}
else
@ -503,12 +507,17 @@ TSubgoal CollectRes::whatToDoToAchieve()
{
auto backObj = backOrNull(cb->getVisitableObjs(m->o->visitablePos())); //it'll be a hero if we have one there; otherwise marketplace
assert(backObj);
if(backObj->tempOwner != ai->playerID)
return make_shared<Goals::CGoal>(Goals::GetObj(m->o->id.getNum()));
return make_shared<Goals::CGoal>(setobjid(m->o->id.getNum()).setisElementar(true));
if (backObj->tempOwner != ai->playerID)
{
return sptr (Goals::GetObj(m->o->id.getNum()));
}
else
{
return sptr (Goals::GetObj(m->o->id.getNum()).setisElementar(true));
}
}
}
return make_shared<Goals::CGoal>(Goals::Invalid()); //FIXME: unused?
return sptr (Goals::Invalid()); //FIXME: unused?
}
TSubgoal GatherTroops::whatToDoToAchieve()
@ -534,7 +543,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
}
else
{
return make_shared<Goals::CGoal>(Goals::BuildThis().settown(t).setbid(bid));
return sptr (Goals::BuildThis(bid, t));
}
}
}
@ -559,10 +568,10 @@ TSubgoal GatherTroops::whatToDoToAchieve()
if (dwellings.size())
{
boost::sort(dwellings, isCloser);
return make_shared<Goals::CGoal>(Goals::GetObj(dwellings.front()->id.getNum()));
return sptr (Goals::GetObj(dwellings.front()->id.getNum()));
}
else
return make_shared<Goals::CGoal>(Goals::Explore());
return sptr (Goals::Explore());
//TODO: exchange troops between heroes
}
@ -578,7 +587,7 @@ TSubgoal Conquer::whatToDoToAchieve()
if(hs.empty()) //all heroes are busy. buy new one
{
if (howManyHeroes < 3 && ai->findTownWithTavern()) //we may want to recruit second hero. TODO: make it smart finally
return make_shared<Goals::CGoal>(Goals::RecruitHero());
return sptr (Goals::RecruitHero());
else //find mobile hero with weakest army
{
hs = cb->getHeroesInfo();
@ -589,7 +598,7 @@ TSubgoal Conquer::whatToDoToAchieve()
if (hs.empty())
{
if (howManyHeroes < GameConstants::MAX_HEROES_PER_PLAYER)
return make_shared<Goals::CGoal>(Goals::RecruitHero());
return sptr (Goals::RecruitHero());
else
throw cannotFulfillGoalException("No heroes with remaining MPs for exploring!\n");
}
@ -618,7 +627,7 @@ TSubgoal Conquer::whatToDoToAchieve()
}
if(objs.empty())
return make_shared<Goals::CGoal>(Goals::Explore()); //we need to find an enemy
return sptr (Goals::Explore()); //we need to find an enemy
erase_if(objs, [&](const CGObjectInstance *obj)
{
@ -626,7 +635,7 @@ TSubgoal Conquer::whatToDoToAchieve()
});
if(objs.empty())
I_AM_ELEMENTAR;
return iAmElementar();
boost::sort(objs, isCloser);
for(const CGObjectInstance *obj : objs)
@ -636,25 +645,24 @@ TSubgoal Conquer::whatToDoToAchieve()
ai->reserveObject(h, obj); //no one else will capture same object until we fail
if (obj->ID == Obj::HERO)
return make_shared<Goals::CGoal>(
Goals::VisitHero().sethero(h).setobjid(obj->id.getNum()).setisAbstract(true));
return sptr (Goals::VisitHero(obj->id.getNum()).sethero(h).setisAbstract(true));
//track enemy hero
else
return make_shared<Goals::CGoal>(Goals::VisitTile(obj->visitablePos()).sethero(h));
return sptr (Goals::VisitTile(obj->visitablePos()).sethero(h));
}
}
return make_shared<Goals::CGoal>(Goals::Explore()); //enemy is inaccessible
return sptr (Goals::Explore()); //enemy is inaccessible
}
TSubgoal Build::whatToDoToAchieve()
{
I_AM_ELEMENTAR;
return iAmElementar();
}
TSubgoal Invalid::whatToDoToAchieve()
{
I_AM_ELEMENTAR;
return iAmElementar();
}
TSubgoal GatherArmy::whatToDoToAchieve()
@ -681,8 +689,7 @@ TSubgoal GatherArmy::whatToDoToAchieve()
if(townsReachable.size()) //try towns first
{
boost::sort(townsReachable, compareReinforcements);
return make_shared<Goals::CGoal>(
Goals::VisitTile(townsReachable.back()->visitablePos()).sethero(hero));
return sptr (Goals::VisitTile(townsReachable.back()->visitablePos()).sethero(hero));
}
else
{
@ -705,13 +712,11 @@ TSubgoal GatherArmy::whatToDoToAchieve()
secondaryPath = cb->getPathInfo(hero->visitablePos())->turns;
if (primaryPath < secondaryPath)
return make_shared<Goals::CGoal>(
Goals::VisitHero().setisAbstract(true).setobjid(h->id.getNum()).sethero(hero));
return sptr (Goals::VisitHero(h->id.getNum()).setisAbstract(true).sethero(hero));
//go to the other hero if we are faster
else
return make_shared<Goals::CGoal>(
Goals::VisitHero().setisAbstract(true).setobjid(hero->id.getNum()).sethero(h))
; //let the other hero come to us
return sptr (Goals::VisitHero(h->id.getNum()).setisAbstract(true).sethero(h));
//let the other hero come to us
}
}
@ -747,7 +752,7 @@ TSubgoal GatherArmy::whatToDoToAchieve()
return true;
});
if(objs.empty()) //no possible objects, we did eveyrthing already
return make_shared<Goals::CGoal>(Goals::Explore().sethero(hero));
return sptr (Goals::Explore(hero));
//TODO: check if we can recruit any creatures there, evaluate army
else
{
@ -767,34 +772,34 @@ TSubgoal GatherArmy::whatToDoToAchieve()
}
}
if (h && isSafeToVisit(h, pos) && ai->isAccessibleForHero(pos, h))
return make_shared<Goals::CGoal>(Goals::VisitTile(pos).sethero(h));
return sptr (Goals::VisitTile(pos).sethero(h));
}
}
}
return make_shared<Goals::CGoal>(Goals::Explore().sethero(hero)); //find dwelling. use current hero to prevent him from doing nothing.
return sptr (Goals::Explore(hero)); //find dwelling. use current hero to prevent him from doing nothing.
}
TSubgoal CGoal::whatToDoToAchieve()
TSubgoal AbstractGoal::whatToDoToAchieve()
{
logAi->debugStream() << boost::format("Decomposing goal of type %s") % name();
return make_shared<Goals::CGoal>(Goals::Explore());
return sptr (Goals::Explore());
}
TSubgoal CGoal::goVisitOrLookFor(const CGObjectInstance *obj)
TSubgoal AbstractGoal::goVisitOrLookFor(const CGObjectInstance *obj)
{
if(obj)
return make_shared<Goals::CGoal>(Goals::GetObj(obj->id.getNum()));
return sptr (Goals::GetObj(obj->id.getNum()));
else
return make_shared<Goals::CGoal>(Goals::Explore());
return sptr (Goals::Explore());
}
TSubgoal CGoal::lookForArtSmart(int aid)
TSubgoal AbstractGoal::lookForArtSmart(int aid)
{
return make_shared<Goals::CGoal>(Goals::Invalid());
return sptr (Goals::Invalid());
}
bool CGoal::invalid() const
bool AbstractGoal::invalid() const
{
return goalType == INVALID;
}

View File

@ -20,8 +20,8 @@ struct HeroPtr;
namespace Goals
{
struct CGoal;
typedef std::shared_ptr<Goals::CGoal> TSubgoal;
struct AbstractGoal;
typedef std::shared_ptr<Goals::AbstractGoal> TSubgoal;
enum EGoals
{
@ -48,24 +48,32 @@ namespace Goals
DIG_AT_TILE //elementar with hero on tile
};
#define SETTER(type, field) CGoal &set ## field(const type &rhs) { field = rhs; return *this; }
//method chaining + clone pattern
#define VSETTER(type, field) AbstractGoal & set ## field(const type &rhs) { field = rhs; return *this; };
#define OSETTER(type, field) CGoal<T> & set ## field(const type &rhs) { field = rhs; return *this; };
#if 0
#define SETTER
#endif // _DEBUG
enum {LOW_PR = -1};
struct CGoal
class AbstractGoal
{
EGoals goalType;
bool isElementar; SETTER(bool, isElementar)
bool isAbstract; SETTER(bool, isAbstract) //allows to remember abstract goals
int priority; SETTER(bool, priority)
std::string name() const;
public:
bool isElementar; VSETTER(bool, isElementar)
bool isAbstract; VSETTER(bool, isAbstract)
int priority; VSETTER(bool, priority)
int value; VSETTER(int, value)
int resID; VSETTER(int, resID)
int objid; VSETTER(int, objid)
int aid; VSETTER(int, aid)
int3 tile; VSETTER(int3, tile)
HeroPtr hero; VSETTER(HeroPtr, hero)
const CGTownInstance *town; VSETTER(CGTownInstance *, town)
int bid; VSETTER(int, bid)
virtual TSubgoal whatToDoToAchieve();
CGoal(EGoals goal = INVALID) : goalType(goal)
AbstractGoal (EGoals goal = INVALID) : goalType (goal)
{
priority = 0;
isElementar = false;
@ -77,22 +85,18 @@ struct CGoal
town = nullptr;
}
EGoals goalType;
std::string name() const;
bool invalid() const;
static TSubgoal goVisitOrLookFor(const CGObjectInstance *obj); //if obj is nullptr, then we'll explore
static TSubgoal lookForArtSmart(int aid); //checks non-standard ways of obtaining art (merchants, quests, etc.)
static TSubgoal tryRecruitHero();
int value; SETTER(int, value)
int resID; SETTER(int, resID)
int objid; SETTER(int, objid)
int aid; SETTER(int, aid)
int3 tile; SETTER(int3, tile)
HeroPtr hero; SETTER(HeroPtr, hero)
const CGTownInstance *town; SETTER(CGTownInstance *, town)
int bid; SETTER(int, bid)
virtual TSubgoal whatToDoToAchieve();
bool operator== (CGoal &g)
bool operator== (AbstractGoal &g) //TODO: virtualize - comparison returns true only for same subclasses
{
switch (goalType)
{
@ -110,131 +114,188 @@ struct CGoal
}
};
class Invalid : public CGoal
template <typename T = CGoal> class CGoal : public AbstractGoal
{
public:
CGoal<T> (EGoals goal = INVALID) : AbstractGoal (goal)
{
priority = 0;
isElementar = false;
isAbstract = false;
value = 0;
aid = -1;
resID = -1;
tile = int3(-1, -1, -1);
town = nullptr;
}
OSETTER(bool, isElementar)
OSETTER(bool, isAbstract) //FIXME: find out why this setter does not compile?
OSETTER(bool, priority)
OSETTER(int, value)
OSETTER(int, resID)
OSETTER(int, objid)
OSETTER(int, aid)
OSETTER(int3, tile)
OSETTER(HeroPtr, hero)
OSETTER(CGTownInstance *, town)
OSETTER(int, bid)
shared_ptr<CGoal<T>> iAmElementar()
{
return make_shared<CGoal<T>> (setisElementar(true));
}
};
//There seems to be some ambiguity on these two, template function keeps form consitent
template <typename T> shared_ptr<CGoal<T>> sptr(CGoal<T>& tmp)
{
return make_shared<CGoal<T>> (tmp);
}
template <typename T> shared_ptr<CGoal<T>> sptr(T& obj)
{
return make_shared<CGoal<T>> (obj);
}
class Invalid : public CGoal<Invalid>
{
public:
Invalid() : CGoal (Goals::INVALID){};
TSubgoal whatToDoToAchieve() override;
};
class Win : public CGoal
class Win : public CGoal<Win>
{
public:
Win() : CGoal (Goals::WIN){};
TSubgoal whatToDoToAchieve() override;
};
class NotLose : public CGoal
class NotLose : public CGoal<NotLose>
{
public:
NotLose() : CGoal (Goals::DO_NOT_LOSE){};
TSubgoal whatToDoToAchieve() override;
};
class Conquer : public CGoal
class Conquer : public CGoal<Conquer>
{
public:
Conquer() : CGoal (Goals::CONQUER){};
TSubgoal whatToDoToAchieve() override;
};
class Build : public CGoal
class Build : public CGoal<Build>
{
public:
Build() : CGoal (Goals::BUILD){};
TSubgoal whatToDoToAchieve() override;
};
class Explore : public CGoal
class Explore : public CGoal<Explore>
{
public:
Explore() : CGoal (Goals::EXPLORE){};
Explore(HeroPtr h) : CGoal (Goals::EXPLORE){hero = h;};
TSubgoal whatToDoToAchieve() override;
};
class GatherArmy : public CGoal
class GatherArmy : public CGoal<GatherArmy>
{
public:
private:
GatherArmy() : CGoal (Goals::GATHER_ARMY){};
public:
GatherArmy(int val) : CGoal (Goals::GATHER_ARMY){value = val;};
TSubgoal whatToDoToAchieve() override;
};
class BoostHero : public CGoal
class BoostHero : public CGoal<BoostHero>
{
public:
BoostHero() : CGoal (Goals::INVALID){}; //TODO
TSubgoal whatToDoToAchieve() override;
};
class RecruitHero : public CGoal
class RecruitHero : public CGoal<RecruitHero>
{
public:
RecruitHero() : CGoal (Goals::RECRUIT_HERO){};
TSubgoal whatToDoToAchieve() override;
};
class BuildThis : public CGoal
class BuildThis : public CGoal<BuildThis>
{
public:
private:
BuildThis() : CGoal (Goals::BUILD_STRUCTURE){};
public:
BuildThis(BuildingID Bid, const CGTownInstance *tid) : CGoal (Goals::BUILD_STRUCTURE) {bid = Bid; town = tid;};
BuildThis(BuildingID Bid) : CGoal (Goals::BUILD_STRUCTURE) {bid = Bid;};
TSubgoal whatToDoToAchieve() override;
};
class CollectRes : public CGoal
class CollectRes : public CGoal<CollectRes>
{
public:
private:
CollectRes() : CGoal (Goals::COLLECT_RES){};
public:
CollectRes(int rid, int val) : CGoal (Goals::COLLECT_RES) {resID = rid; value = val;};
TSubgoal whatToDoToAchieve() override;
};
class GatherTroops : public CGoal
class GatherTroops : public CGoal<GatherTroops>
{
public:
private:
GatherTroops() : CGoal (Goals::GATHER_TROOPS){};
public:
GatherTroops(int type, int val) : CGoal (Goals::GATHER_TROOPS){objid = type; value = val;};
TSubgoal whatToDoToAchieve() override;
};
class GetObj : public CGoal
class GetObj : public CGoal<GetObj>
{
private:
GetObj() {}; // empty constructor not allowed
public:
GetObj(const int Objid) : CGoal(Goals::GET_OBJ) {setobjid(Objid);};
GetObj(int Objid) : CGoal(Goals::GET_OBJ) {objid = Objid;};
TSubgoal whatToDoToAchieve() override;
};
class FindObj : public CGoal
class FindObj : public CGoal<FindObj>
{
private:
FindObj() {}; // empty constructor not allowed
public:
FindObj(int ID) : CGoal(Goals::FIND_OBJ) {setobjid(ID);};
FindObj(int ID, int subID) : CGoal(Goals::FIND_OBJ) {setobjid(ID).setresID(subID);};
FindObj(int ID) : CGoal(Goals::FIND_OBJ) {objid = ID;};
FindObj(int ID, int subID) : CGoal(Goals::FIND_OBJ) {objid = ID; resID = subID;};
TSubgoal whatToDoToAchieve() override;
};
class VisitHero : public CGoal
class VisitHero : public CGoal<VisitHero>
{
public:
private:
VisitHero() : CGoal (Goals::VISIT_HERO){};
public:
VisitHero(int hid) : CGoal (Goals::VISIT_HERO){objid = hid;};
TSubgoal whatToDoToAchieve() override;
};
class GetArtOfType : public CGoal
class GetArtOfType : public CGoal<GetArtOfType>
{
public:
private:
GetArtOfType() : CGoal (Goals::GET_ART_TYPE){};
public:
GetArtOfType(int type) : CGoal (Goals::GET_ART_TYPE){aid = type;};
TSubgoal whatToDoToAchieve() override;
};
class VisitTile : public CGoal
class VisitTile : public CGoal<VisitTile>
//tile, in conjunction with hero elementar; assumes tile is reachable
{
private:
VisitTile() {}; // empty constructor not allowed
public:
VisitTile(int3 Tile) : CGoal (Goals::VISIT_TILE) {settile(Tile);};
private:
VisitTile() {}; // empty constructor not allowed
public:
VisitTile(int3 Tile) : CGoal (Goals::VISIT_TILE) {tile = Tile;};
TSubgoal whatToDoToAchieve() override;
};
class ClearWayTo : public CGoal
class ClearWayTo : public CGoal<ClearWayTo>
{
public:
ClearWayTo(int3 Tile) : CGoal (Goals::CLEAR_WAY_TO) {settile(Tile);};
public:
ClearWayTo(int3 Tile) : CGoal (Goals::CLEAR_WAY_TO) {tile = Tile;};
TSubgoal whatToDoToAchieve() override;
};
class DigAtTile : public CGoal
class DigAtTile : public CGoal<DigAtTile>
//elementar with hero on tile
{
public:
private:
DigAtTile() : CGoal (Goals::DIG_AT_TILE){};
public:
DigAtTile(int3 Tile) : CGoal (Goals::DIG_AT_TILE) {tile = Tile;};
TSubgoal whatToDoToAchieve() override;
};
class CIssueCommand : CGoal
class CIssueCommand : public CGoal<CIssueCommand>
{
std::function<bool()> command;

View File

@ -257,7 +257,7 @@ void VCAI::heroVisit(const CGHeroInstance *visitor, const CGObjectInstance *visi
markObjectVisited (visitedObj);
erase_if_present(reservedObjs, visitedObj); //unreserve objects
erase_if_present(reservedHeroesMap[visitor], visitedObj);
completeGoal (Goals::CGoal(Goals::GET_OBJ).sethero(visitor)); //we don't need to visit in anymore
completeGoal (Goals::GetObj(visitedObj->id.getNum()).sethero(visitor)); //we don't need to visit in anymore
}
status.heroVisit(visitedObj, start);
@ -311,8 +311,8 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
else if (canGetArmy (secondHero, firstHero))
pickBestCreatures (secondHero, firstHero);
completeGoal(Goals::VisitHero().sethero(firstHero)); //TODO: what if we were visited by other hero in the meantime?
completeGoal(Goals::VisitHero().sethero(secondHero));
completeGoal(Goals::AbstractGoal(Goals::VISIT_HERO).sethero(firstHero)); //TODO: what if we were visited by other hero in the meantime?
completeGoal(Goals::AbstractGoal(Goals::VISIT_HERO).sethero(secondHero));
//TODO: exchange artifacts
answerQuery(query, 0);
@ -667,7 +667,7 @@ void VCAI::makeTurn()
ui64 averageDanger = totalDanger / std::max(dangerousObjects, 1);
if (dangerousObjects && averageDanger > h->getHeroStrength())
{
setGoal (h, Goals::GatherArmy().sethero(h).setvalue(averageDanger * SAFE_ATTACK_CONSTANT).setisAbstract(true));
setGoal (h, Goals::GatherArmy(averageDanger * SAFE_ATTACK_CONSTANT).sethero(h).setisAbstract(true));
}
}
}
@ -723,10 +723,10 @@ void VCAI::makeTurnInternal()
//finally, continue our abstract long-term goals
//heroes tend to die in the process and loose their goals, unsafe to iterate it
std::vector<std::pair<HeroPtr, Goals::CGoal> > safeCopy;
std::vector<std::pair<HeroPtr, Goals::AbstractGoal> > safeCopy;
boost::copy(lockedHeroes, std::back_inserter(safeCopy));
typedef std::pair<HeroPtr, Goals::CGoal> TItrType;
typedef std::pair<HeroPtr, Goals::AbstractGoal> TItrType;
auto lockedHeroesSorter = [](TItrType h1, TItrType h2) -> bool
{
@ -1213,12 +1213,12 @@ void VCAI::wander(HeroPtr h)
}
}
void VCAI::setGoal(HeroPtr h, const Goals::CGoal goal)
void VCAI::setGoal(HeroPtr h, const Goals::AbstractGoal &goal)
{ //TODO: check for presence?
if (goal.goalType == Goals::INVALID)
if (goal.invalid())
erase_if_present(lockedHeroes, h);
else
lockedHeroes[h] = Goals::CGoal(goal).setisElementar(false); //always evaluate goals before realizing
lockedHeroes[h] = Goals::AbstractGoal(goal).setisElementar(false); //always evaluate goals before realizing
}
void VCAI::setGoal(HeroPtr h, Goals::EGoals goalType)
@ -1226,10 +1226,10 @@ void VCAI::setGoal(HeroPtr h, Goals::EGoals goalType)
if (goalType == Goals::INVALID)
erase_if_present(lockedHeroes, h);
else
lockedHeroes[h] = Goals::CGoal(goalType).setisElementar(false); //always evaluate goals before realizing;
lockedHeroes[h] = Goals::AbstractGoal(goalType).setisElementar(false); //always evaluate goals before realizing;
}
void VCAI::completeGoal (const Goals::CGoal goal)
void VCAI::completeGoal (const Goals::AbstractGoal &goal)
{
if (const CGHeroInstance * h = goal.hero.get(true))
{
@ -1425,7 +1425,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
{
logAi->errorStream() << "Hero " << h->name << " cannot reach " << dst;
//setGoal(h, INVALID);
completeGoal (Goals::CGoal(Goals::VISIT_TILE).sethero(h));
completeGoal (Goals::AbstractGoal(Goals::VISIT_TILE).sethero(h));
cb->recalculatePaths();
throw std::runtime_error("Wrong move order!");
}
@ -1617,7 +1617,7 @@ void VCAI::tryRealize(Goals::Invalid g)
throw cannotFulfillGoalException("I don't know how to fulfill this!");
}
void VCAI::tryRealize(Goals::CGoal g)
void VCAI::tryRealize(Goals::AbstractGoal g)
{
logAi->debugStream() << boost::format("Attempting realizing goal with code %s") % g.name();
throw cannotFulfillGoalException("Unknown type of goal !");
@ -1673,7 +1673,7 @@ void VCAI::endTurn()
logAi->debugStream() << "Player " << static_cast<int>(playerID.getNum()) << " ended turn";
}
bool VCAI::fulfillsGoal (Goals::CGoal &goal, Goals::CGoal &mainGoal)
bool VCAI::fulfillsGoal (Goals::AbstractGoal &goal, Goals::AbstractGoal &mainGoal)
{
if (mainGoal.goalType == Goals::GET_OBJ && goal.goalType == Goals::VISIT_TILE) //deduce that GET_OBJ was completed by visiting object's tile
{ //TODO: more universal mechanism
@ -1682,26 +1682,21 @@ bool VCAI::fulfillsGoal (Goals::CGoal &goal, Goals::CGoal &mainGoal)
}
return false;
}
bool VCAI::fulfillsGoal (Goals::CGoal &goal, const Goals::CGoal &mainGoal)
bool VCAI::fulfillsGoal (Goals::AbstractGoal &goal, const Goals::AbstractGoal &mainGoal)
{
if (mainGoal.goalType == Goals::GET_OBJ && goal.goalType == Goals::VISIT_TILE) //deduce that GET_OBJ was completed by visiting object's tile
{ //TODO: more universal mechanism
if (cb->getObj(ObjectInstanceID(mainGoal.objid))->visitablePos() == goal.tile)
return true;
}
return false;
return fulfillsGoal (goal, const_cast<Goals::AbstractGoal&>(mainGoal));
}
void VCAI::striveToGoal(const Goals::CGoal &ultimateGoal)
void VCAI::striveToGoal(const Goals::AbstractGoal &ultimateGoal)
{
if (ultimateGoal.invalid())
return;
std::shared_ptr<Goals::CGoal> abstractGoal = make_shared<Goals::CGoal>(Goals::Invalid());
std::shared_ptr<Goals::AbstractGoal> abstractGoal = make_shared<Goals::AbstractGoal>(Goals::Invalid());
while(1)
{
std::shared_ptr<Goals::CGoal> goal = make_shared<Goals::CGoal>(ultimateGoal);
std::shared_ptr<Goals::AbstractGoal> goal = make_shared<Goals::AbstractGoal>(ultimateGoal); //FIXME: preserve subclass of goal
logAi->debugStream() << boost::format("Striving to goal of type %s") % ultimateGoal.name();
int maxGoals = 100; //preventing deadlock for mutually dependent goals, FIXME: do not try to realize goal when loop didn't suceed
while(!goal->isElementar && !goal->isAbstract && maxGoals)
@ -1779,7 +1774,7 @@ void VCAI::striveToGoal(const Goals::CGoal &ultimateGoal)
{
while (1)
{
std::shared_ptr<Goals::CGoal> goal(abstractGoal);
std::shared_ptr<Goals::AbstractGoal> goal(abstractGoal);
goal->setisAbstract(false);
int maxGoals = 50;
while (!goal->isElementar && maxGoals) //find elementar goal and fulfill it
@ -1852,7 +1847,7 @@ void VCAI::striveToQuest (const QuestInfo &q)
}
for (auto art : q.quest->m5arts)
{
striveToGoal (Goals::GetArtOfType().setaid(art)); //TODO: transport?
striveToGoal (Goals::GetArtOfType(art)); //TODO: transport?
}
break;
}
@ -1883,7 +1878,7 @@ void VCAI::striveToQuest (const QuestInfo &q)
}
for (auto creature : q.quest->m6creatures)
{
striveToGoal (Goals::GatherTroops().setobjid(creature.type->idNumber).setvalue(creature.count));
striveToGoal (Goals::GatherTroops(creature.type->idNumber, creature.count));
}
//TODO: exchange armies... oh my
//BNLOG ("Don't know how to recruit %d of %s\n", (int)(creature.count) % creature.type->namePl);
@ -1902,7 +1897,7 @@ void VCAI::striveToQuest (const QuestInfo &q)
for (int i = 0; i < q.quest->m7resources.size(); ++i)
{
if (q.quest->m7resources[i])
striveToGoal (Goals::CollectRes().setresID(i).setvalue(q.quest->m7resources[i]));
striveToGoal (Goals::CollectRes(i, q.quest->m7resources[i]));
}
}
}
@ -2123,7 +2118,7 @@ void VCAI::checkHeroArmy (HeroPtr h)
if (it != lockedHeroes.end())
{
if (it->second.goalType == Goals::GATHER_ARMY && it->second.value <= h->getArmyStrength())
completeGoal(Goals::GatherArmy().sethero(h));
completeGoal(Goals::GatherArmy(it->second.value).sethero(h));
}
}
@ -2630,7 +2625,7 @@ int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
if(!preds[dst])
{
write("test.txt");
ai->completeGoal (Goals::Explore().sethero(h)); //if we can't find the way, seemingly all tiles were explored
ai->completeGoal (Goals::Explore(h)); //if we can't find the way, seemingly all tiles were explored
//TODO: more organized way?
throw cannotFulfillGoalException(boost::str(boost::format("Cannot find connection between sectors %d and %d") % src->id % dst->id));
}

View File

@ -128,7 +128,7 @@ public:
//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
std::map<HeroPtr, std::vector<const CGTownInstance *> > townVisitsThisWeek;
std::map<HeroPtr, Goals::CGoal> lockedHeroes; //TODO: allow non-elementar objectives
std::map<HeroPtr, Goals::AbstractGoal> lockedHeroes; //TODO: allow non-elementar objectives
std::map<HeroPtr, std::vector<const CGObjectInstance *> > reservedHeroesMap; //objects reserved by specific heroes
std::vector<const CGObjectInstance *> visitableObjs;
@ -147,7 +147,7 @@ public:
VCAI(void);
~VCAI(void);
void tryRealize(Goals::CGoal g);
void tryRealize(Goals::AbstractGoal g);
void tryRealize(Goals::Explore g);
void tryRealize(Goals::RecruitHero g);
void tryRealize(Goals::VisitTile g);
@ -231,15 +231,15 @@ public:
void performTypicalActions();
void buildArmyIn(const CGTownInstance * t);
void striveToGoal(const Goals::CGoal & ultimateGoal);
void striveToGoal(const Goals::AbstractGoal & ultimateGoal);
void endTurn();
void wander(HeroPtr h);
void setGoal(HeroPtr h, const Goals::CGoal goal);
void setGoal(HeroPtr h, const Goals::AbstractGoal &goal);
void setGoal(HeroPtr h, Goals::EGoals goalType = Goals::INVALID);
void completeGoal (const Goals::CGoal goal); //safely removes goal from reserved hero
void completeGoal (const Goals::AbstractGoal &goal); //safely removes goal from reserved hero
void striveToQuest (const QuestInfo &q);
bool fulfillsGoal (Goals::CGoal &goal, Goals::CGoal &mainGoal);
bool fulfillsGoal (Goals::CGoal &goal, const Goals::CGoal &mainGoal); //TODO: something smarter
bool fulfillsGoal (Goals::AbstractGoal &goal, Goals::AbstractGoal &mainGoal);
bool fulfillsGoal (Goals::AbstractGoal &goal, const Goals::AbstractGoal &mainGoal); //TODO: something smarter
void recruitHero(const CGTownInstance * t, bool throwing = false);
std::vector<const CGObjectInstance *> getPossibleDestinations(HeroPtr h);
@ -320,9 +320,9 @@ public:
class goalFulfilledException : public std::exception
{
public:
Goals::CGoal goal;
Goals::AbstractGoal goal;
explicit goalFulfilledException(Goals::CGoal Goal) : goal(Goal)
explicit goalFulfilledException(Goals::AbstractGoal Goal) : goal(Goal)
{
}