diff --git a/AI/VCAI/Goals.cpp b/AI/VCAI/Goals.cpp index c4c72ee7e..b11a99d55 100644 --- a/AI/VCAI/Goals.cpp +++ b/AI/VCAI/Goals.cpp @@ -18,7 +18,7 @@ extern boost::thread_specific_ptr 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(setisElementar(true)) + TSubgoal Win::whatToDoToAchieve() { @@ -83,11 +83,11 @@ TSubgoal Win::whatToDoToAchieve() switch(cond) { case EVictoryConditionType::ARTIFACT: - return make_shared (Goals::GetArtOfType().setaid(vc.objectId)); + return sptr (Goals::GetArtOfType(vc.objectId)); case EVictoryConditionType::BEATHERO: - return make_shared (Goals::GetObj(vc.obj->id.getNum())); + return sptr (Goals::GetObj(vc.obj->id.getNum())); case EVictoryConditionType::BEATMONSTER: - return make_shared (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::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::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::DigAtTile().settile(grailPos)); + return sptr (Goals::DigAtTile(grailPos)); } //TODO: use FIND_OBJ else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID)) //there are unvisited Obelisks { - return make_shared (Goals::GetObj(obj->id.getNum())); + return sptr (Goals::GetObj(obj->id.getNum())); } else - return make_shared (Goals::Explore()); + return sptr (Goals::Explore()); } break; case EVictoryConditionType::CAPTURECITY: - return make_shared (Goals::GetObj(vc.obj->id.getNum())); + return sptr (Goals::GetObj(vc.obj->id.getNum())); case EVictoryConditionType::GATHERRESOURCE: - return make_shared (Goals::CollectRes().setresID(static_cast(vc.objectId)).setvalue(vc.count)); + return sptr (Goals::CollectRes(static_cast(vc.objectId), vc.count)); //TODO mines? piles? marketplace? //save? break; case EVictoryConditionType::GATHERTROOP: - return make_shared (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::Conquer()); + return sptr (Goals::Conquer()); default: assert(0); } - return make_shared (Goals::INVALID); + return sptr (Goals::Invalid()); } TSubgoal FindObj::whatToDoToAchieve() @@ -181,37 +181,40 @@ TSubgoal FindObj::whatToDoToAchieve() } } if (o && isReachable(o)) - return make_shared (Goals::GetObj(o->id.getNum())); + return sptr (Goals::GetObj(o->id.getNum())); else - return make_shared (Goals::Explore()); + return sptr (Goals::Explore()); } TSubgoal GetObj::whatToDoToAchieve() { const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid)); if(!obj) - return make_shared (Goals::Explore()); + return sptr (Goals::Explore()); int3 pos = obj->visitablePos(); - return make_shared (Goals::VisitTile(pos)); + return sptr (Goals::VisitTile(pos)); } TSubgoal VisitHero::whatToDoToAchieve() { const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid)); if(!obj) - return make_shared (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 (settile(pos).setisElementar(true)); - return make_shared (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::FindObj(Obj::ARTIFACT, aid)); - return make_shared (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::Explore()); + return sptr (Goals::Explore()); } HeroPtr h = hero ? hero : ai->primaryHero(); if(!h) - return make_shared (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::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::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::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::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::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::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::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::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 (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::BuildThis().setbid(BuildingID::TAVERN)); + return sptr (Goals::BuildThis(BuildingID::TAVERN)); if(cb->getResourceAmount(Res::GOLD) < HERO_GOLD_COST) - return make_shared (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::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::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(setisElementar(true)); + return sptr (setisElementar(true)); else { - return make_shared(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::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(firstObj); - return make_shared (sethero(h).setisElementar(true)); + sethero(h).setisElementar(true); + return sptr (*this); } - return make_shared(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::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::GetObj(m->o->id.getNum())); - return make_shared(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::Invalid()); //FIXME: unused? + return sptr (Goals::Invalid()); //FIXME: unused? } TSubgoal GatherTroops::whatToDoToAchieve() @@ -534,7 +543,7 @@ TSubgoal GatherTroops::whatToDoToAchieve() } else { - return make_shared(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::GetObj(dwellings.front()->id.getNum())); + return sptr (Goals::GetObj(dwellings.front()->id.getNum())); } else - return make_shared(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::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::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::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::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::VisitTile(obj->visitablePos()).sethero(h)); + return sptr (Goals::VisitTile(obj->visitablePos()).sethero(h)); } } - return make_shared(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::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::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::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::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::VisitTile(pos).sethero(h)); + return sptr (Goals::VisitTile(pos).sethero(h)); } } } - return make_shared(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::Explore()); + return sptr (Goals::Explore()); } -TSubgoal CGoal::goVisitOrLookFor(const CGObjectInstance *obj) +TSubgoal AbstractGoal::goVisitOrLookFor(const CGObjectInstance *obj) { if(obj) - return make_shared(Goals::GetObj(obj->id.getNum())); + return sptr (Goals::GetObj(obj->id.getNum())); else - return make_shared(Goals::Explore()); + return sptr (Goals::Explore()); } -TSubgoal CGoal::lookForArtSmart(int aid) +TSubgoal AbstractGoal::lookForArtSmart(int aid) { - return make_shared(Goals::Invalid()); + return sptr (Goals::Invalid()); } -bool CGoal::invalid() const +bool AbstractGoal::invalid() const { return goalType == INVALID; } diff --git a/AI/VCAI/Goals.h b/AI/VCAI/Goals.h index 2a5c635d8..2c2ac83fd 100644 --- a/AI/VCAI/Goals.h +++ b/AI/VCAI/Goals.h @@ -20,8 +20,8 @@ struct HeroPtr; namespace Goals { - struct CGoal; - typedef std::shared_ptr TSubgoal; + struct AbstractGoal; + typedef std::shared_ptr 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 & 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 class CGoal : public AbstractGoal +{ +public: + CGoal (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> iAmElementar() + { + return make_shared> (setisElementar(true)); + } +}; + +//There seems to be some ambiguity on these two, template function keeps form consitent +template shared_ptr> sptr(CGoal& tmp) +{ + return make_shared> (tmp); +} +template shared_ptr> sptr(T& obj) +{ + return make_shared> (obj); +} + +class Invalid : public CGoal { public: Invalid() : CGoal (Goals::INVALID){}; TSubgoal whatToDoToAchieve() override; }; -class Win : public CGoal +class Win : public CGoal { public: Win() : CGoal (Goals::WIN){}; TSubgoal whatToDoToAchieve() override; }; -class NotLose : public CGoal +class NotLose : public CGoal { public: NotLose() : CGoal (Goals::DO_NOT_LOSE){}; TSubgoal whatToDoToAchieve() override; }; -class Conquer : public CGoal +class Conquer : public CGoal { public: Conquer() : CGoal (Goals::CONQUER){}; TSubgoal whatToDoToAchieve() override; }; -class Build : public CGoal +class Build : public CGoal { public: Build() : CGoal (Goals::BUILD){}; TSubgoal whatToDoToAchieve() override; }; -class Explore : public CGoal +class Explore : public CGoal { public: Explore() : CGoal (Goals::EXPLORE){}; + Explore(HeroPtr h) : CGoal (Goals::EXPLORE){hero = h;}; TSubgoal whatToDoToAchieve() override; }; -class GatherArmy : public CGoal +class GatherArmy : public CGoal { - 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 { public: BoostHero() : CGoal (Goals::INVALID){}; //TODO TSubgoal whatToDoToAchieve() override; }; -class RecruitHero : public CGoal +class RecruitHero : public CGoal { public: RecruitHero() : CGoal (Goals::RECRUIT_HERO){}; TSubgoal whatToDoToAchieve() override; }; -class BuildThis : public CGoal +class BuildThis : public CGoal { - 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 { - 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 { - 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 { 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 { 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 { - 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 { - 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 //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 { - 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 //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 { std::function command; diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 3fbfe9fb8..bdaa0d188 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -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 > safeCopy; + std::vector > safeCopy; boost::copy(lockedHeroes, std::back_inserter(safeCopy)); - typedef std::pair TItrType; + typedef std::pair 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(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(mainGoal)); } -void VCAI::striveToGoal(const Goals::CGoal &ultimateGoal) +void VCAI::striveToGoal(const Goals::AbstractGoal &ultimateGoal) { if (ultimateGoal.invalid()) return; - std::shared_ptr abstractGoal = make_shared(Goals::Invalid()); + std::shared_ptr abstractGoal = make_shared(Goals::Invalid()); while(1) { - std::shared_ptr goal = make_shared(ultimateGoal); + std::shared_ptr goal = make_shared(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 goal(abstractGoal); + std::shared_ptr 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)); } diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 0a4fc498f..5e9a2af7c 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -128,7 +128,7 @@ public: //std::vector visitedThisWeek; //only OPWs std::map > townVisitsThisWeek; - std::map lockedHeroes; //TODO: allow non-elementar objectives + std::map lockedHeroes; //TODO: allow non-elementar objectives std::map > reservedHeroesMap; //objects reserved by specific heroes std::vector 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 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) { }