From 72aff0241875d2e8c96d9f89afe56c32e476b07d Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Tue, 9 Oct 2018 22:31:44 +0300 Subject: [PATCH] AI pathfinding: buy boat --- AI/VCAI/AIhelper.cpp | 8 +- AI/VCAI/AIhelper.h | 2 +- AI/VCAI/BuildingManager.cpp | 2 +- AI/VCAI/BuildingManager.h | 4 +- AI/VCAI/FuzzyEngines.cpp | 20 +- AI/VCAI/FuzzyEngines.h | 2 +- AI/VCAI/FuzzyHelper.cpp | 17 +- AI/VCAI/FuzzyHelper.h | 1 + AI/VCAI/Goals.cpp | 257 +++++++++++---------- AI/VCAI/Goals.h | 134 ++++++++--- AI/VCAI/Pathfinding/AINodeStorage.cpp | 45 +++- AI/VCAI/Pathfinding/AINodeStorage.h | 19 +- AI/VCAI/Pathfinding/AIPathfinder.cpp | 6 +- AI/VCAI/Pathfinding/AIPathfinder.h | 6 +- AI/VCAI/Pathfinding/AIPathfinderConfig.cpp | 159 ++++++++++++- AI/VCAI/Pathfinding/AIPathfinderConfig.h | 6 +- AI/VCAI/Pathfinding/PathfindingManager.cpp | 43 +++- AI/VCAI/Pathfinding/PathfindingManager.h | 4 +- AI/VCAI/ResourceManager.cpp | 2 +- AI/VCAI/ResourceManager.h | 4 +- AI/VCAI/VCAI.cpp | 11 +- lib/CPathfinder.cpp | 1 + 22 files changed, 530 insertions(+), 223 deletions(-) diff --git a/AI/VCAI/AIhelper.cpp b/AI/VCAI/AIhelper.cpp index e65d28b05..a22d22961 100644 --- a/AI/VCAI/AIhelper.cpp +++ b/AI/VCAI/AIhelper.cpp @@ -29,11 +29,11 @@ bool AIhelper::notifyGoalCompleted(Goals::TSubgoal goal) return resourceManager->notifyGoalCompleted(goal); } -void AIhelper::setCB(CPlayerSpecificInfoCallback * CB) +void AIhelper::init(CPlayerSpecificInfoCallback * CB) { - resourceManager->setCB(CB); - buildingManager->setCB(CB); - pathfindingManager->setCB(CB); + resourceManager->init(CB); + buildingManager->init(CB); + pathfindingManager->init(CB); } void AIhelper::setAI(VCAI * AI) diff --git a/AI/VCAI/AIhelper.h b/AI/VCAI/AIhelper.h index 35b9ad942..450fa1e81 100644 --- a/AI/VCAI/AIhelper.h +++ b/AI/VCAI/AIhelper.h @@ -63,7 +63,7 @@ public: private: bool notifyGoalCompleted(Goals::TSubgoal goal); - void setCB(CPlayerSpecificInfoCallback * CB) override; + void init(CPlayerSpecificInfoCallback * CB) override; void setAI(VCAI * AI) override; }; diff --git a/AI/VCAI/BuildingManager.cpp b/AI/VCAI/BuildingManager.cpp index cfe0dfc2c..56d28f5e9 100644 --- a/AI/VCAI/BuildingManager.cpp +++ b/AI/VCAI/BuildingManager.cpp @@ -127,7 +127,7 @@ bool BuildingManager::tryBuildNextStructure(const CGTownInstance * t, std::vecto return false; //Nothing to build } -void BuildingManager::setCB(CPlayerSpecificInfoCallback * CB) +void BuildingManager::init(CPlayerSpecificInfoCallback * CB) { cb = CB; } diff --git a/AI/VCAI/BuildingManager.h b/AI/VCAI/BuildingManager.h index 0df8ed8ef..f67f5f5e7 100644 --- a/AI/VCAI/BuildingManager.h +++ b/AI/VCAI/BuildingManager.h @@ -29,7 +29,7 @@ class DLL_EXPORT IBuildingManager //: public: IAbstractManager { //info about town development public: virtual ~IBuildingManager() = default; - virtual void setCB(CPlayerSpecificInfoCallback * CB) = 0; + virtual void init(CPlayerSpecificInfoCallback * CB) = 0; virtual void setAI(VCAI * AI) = 0; virtual bool getBuildingOptions(const CGTownInstance * t) = 0; @@ -69,6 +69,6 @@ private: std::vector immediateBuildings; //what we can build right now in current town std::vector expensiveBuildings; //what we coudl build but can't afford - void setCB(CPlayerSpecificInfoCallback * CB) override; + void init(CPlayerSpecificInfoCallback * CB) override; void setAI(VCAI * AI) override; }; diff --git a/AI/VCAI/FuzzyEngines.cpp b/AI/VCAI/FuzzyEngines.cpp index e1a99c6ac..7d9dbc40f 100644 --- a/AI/VCAI/FuzzyEngines.cpp +++ b/AI/VCAI/FuzzyEngines.cpp @@ -76,18 +76,14 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army) return as; } -float HeroMovementGoalEngineBase::calculateTurnDistanceInputValue(const CGHeroInstance * h, int3 tile) const +float HeroMovementGoalEngineBase::calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const { - float turns = 0.0f; - float distance = distanceToTile(h, tile); - if(distance) + if(goal.evaluationContext.movementCost != 0) { - if(distance < h->movement) //we can move there within one turn - turns = (fl::scalar)distance / h->movement; - else - turns = 1 + (fl::scalar)(distance - h->movement) / h->maxMovePoints(true); //bool on land? + return goal.evaluationContext.movementCost / (float)goal.hero->maxMovePoints(true); } - return turns; + + return distanceToTile(goal.hero.h, goal.tile) / (float)goal.hero->maxMovePoints(true); } TacticalAdvantageEngine::TacticalAdvantageEngine() @@ -276,8 +272,8 @@ HeroMovementGoalEngineBase::HeroMovementGoalEngineBase() turnDistance->addTerm(new fl::Ramp("SHORT", 0.5, 0)); turnDistance->addTerm(new fl::Triangle("MEDIUM", 0.1, 0.8)); - turnDistance->addTerm(new fl::Ramp("LONG", 0.5, 3)); - turnDistance->setRange(0.0, 3.0); + turnDistance->addTerm(new fl::Ramp("LONG", 0.5, 10)); + turnDistance->setRange(0.0, 10.0); missionImportance->addTerm(new fl::Ramp("LOW", 2.5, 0)); missionImportance->addTerm(new fl::Triangle("MEDIUM", 2, 3)); @@ -319,7 +315,7 @@ HeroMovementGoalEngineBase::HeroMovementGoalEngineBase() void HeroMovementGoalEngineBase::setSharedFuzzyVariables(Goals::AbstractGoal & goal) { - float turns = calculateTurnDistanceInputValue(goal.hero.h, goal.tile); + float turns = calculateTurnDistanceInputValue(goal); float missionImportanceData = 0; if(vstd::contains(ai->lockedHeroes, goal.hero)) missionImportanceData = ai->lockedHeroes[goal.hero]->priority; diff --git a/AI/VCAI/FuzzyEngines.h b/AI/VCAI/FuzzyEngines.h index e0b028b9b..2797a7e32 100644 --- a/AI/VCAI/FuzzyEngines.h +++ b/AI/VCAI/FuzzyEngines.h @@ -52,7 +52,7 @@ protected: fl::OutputVariable * value; private: - float calculateTurnDistanceInputValue(const CGHeroInstance * h, int3 tile) const; + float calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const; }; class VisitTileEngine : public HeroMovementGoalEngineBase diff --git a/AI/VCAI/FuzzyHelper.cpp b/AI/VCAI/FuzzyHelper.cpp index f44df5317..7c6ca962c 100644 --- a/AI/VCAI/FuzzyHelper.cpp +++ b/AI/VCAI/FuzzyHelper.cpp @@ -11,6 +11,7 @@ #include "FuzzyHelper.h" #include "../../lib/mapObjects/CommonConstructors.h" +#include "Goals.h" #include "VCAI.h" FuzzyHelper * fh; @@ -79,6 +80,19 @@ float FuzzyHelper::evaluate(Goals::VisitTile & g) { return visitTileEngine.evaluate(g); } + +float FuzzyHelper::evaluate(Goals::BuildBoat & g) +{ + const float buildBoatPenalty = 0.25; + + if(!g.parent) + { + return 0; + } + + return g.parent->accept(this) - buildBoatPenalty; +} + float FuzzyHelper::evaluate(Goals::VisitObj & g) { return visitObjEngine.evaluate(g); @@ -90,8 +104,7 @@ float FuzzyHelper::evaluate(Goals::VisitHero & g) return -100; //hero died in the meantime else { - auto dummyGoal = Goals::VisitTile(obj->visitablePos()).sethero(g.hero).setisAbstract(g.isAbstract); - g.setpriority(dummyGoal.accept(this)); + g.setpriority(Goals::VisitTile(obj->visitablePos()).sethero(g.hero).accept(this)); } return g.priority; } diff --git a/AI/VCAI/FuzzyHelper.h b/AI/VCAI/FuzzyHelper.h index 52715eb02..0989403aa 100644 --- a/AI/VCAI/FuzzyHelper.h +++ b/AI/VCAI/FuzzyHelper.h @@ -29,6 +29,7 @@ public: float evaluate(Goals::CollectRes & g); float evaluate(Goals::Build & g); float evaluate(Goals::BuyArmy & g); + float evaluate(Goals::BuildBoat & g); float evaluate(Goals::GatherArmy & g); float evaluate(Goals::ClearWayTo & g); float evaluate(Goals::Invalid & g); diff --git a/AI/VCAI/Goals.cpp b/AI/VCAI/Goals.cpp index 69936e15e..15d9d8a52 100644 --- a/AI/VCAI/Goals.cpp +++ b/AI/VCAI/Goals.cpp @@ -115,70 +115,94 @@ std::string Goals::AbstractGoal::name() const //TODO: virtualize return desc; } -bool Goals::AbstractGoal::operator==(AbstractGoal & g) +bool Goals::BuildBoat::operator==(const BuildBoat & other) const { - /*this operator checks if goals are EQUIVALENT, ie. if they represent same objective - it does not not check isAbstract or isElementar, as this is up to VCAI decomposition logic - */ - if(g.goalType != goalType) - return false; + return shipyard->o->id == other.shipyard->o->id; +} - switch(goalType) - { - //no parameters - case INVALID: - case WIN: - case DO_NOT_LOSE: - case RECRUIT_HERO: //overloaded - return true; - break; +bool Goals::Explore::operator==(const Explore & other) const +{ + return other.hero.h == hero.h; +} - //assigned to hero, no parameters - case CONQUER: - case EXPLORE: - case BOOST_HERO: - return g.hero.h == hero.h; //how comes HeroPtrs are equal for different heroes? - break; +bool Goals::Conquer::operator==(const Conquer & other) const +{ + return other.hero.h == hero.h; +} - case GATHER_ARMY: //actual value is indifferent - return (g.hero.h == hero.h || town == g.town); //TODO: gather army for town maybe? - break; +bool Goals::GatherArmy::operator==(const GatherArmy & other) const +{ + return other.hero.h == hero.h || town == other.town; +} - //assigned hero and tile - case VISIT_TILE: - case CLEAR_WAY_TO: - case DIG_AT_TILE: - return (g.hero.h == hero.h && g.tile == tile); - break; +bool Goals::BuyArmy::operator==(const BuyArmy & other) const +{ + return town == other.town; +} - //assigned hero and object - case VISIT_OBJ: - case FIND_OBJ: //TODO: use subtype? - case VISIT_HERO: - case GET_ART_TYPE: - return (g.hero.h == hero.h && g.objid == objid); - break; +bool Goals::BoostHero::operator==(const BoostHero & other) const +{ + return other.hero.h == hero.h; +} - case BUILD_STRUCTURE: - return (town == g.town && bid == g.bid); //build specific structure in specific town - break; +bool Goals::BuildThis::operator==(const BuildThis & other) const +{ + return town == other.town && bid == other.bid; +} - case BUY_ARMY: - return town == g.town; +bool Goals::CollectRes::operator==(const CollectRes & other) const +{ + return resID == other.resID; +} - //no check atm - case COLLECT_RES: - case TRADE: //TODO - return (resID == g.resID); //every hero may collect resources - break; - case BUILD: //abstract build is indentical, TODO: consider building anything in town - return true; - break; - case GATHER_TROOPS: - case ISSUE_COMMAND: - default: - return false; - } +bool Goals::Trade::operator==(const Trade & other) const +{ + return resID == other.resID; +} + +bool Goals::GatherTroops::operator==(const GatherTroops & other) const +{ + return objid == other.objid; +} + +bool Goals::VisitObj::operator==(const VisitObj & other) const +{ + return other.hero.h == hero.h && other.objid == objid; +} + +bool Goals::FindObj::operator==(const FindObj & other) const +{ + return other.hero.h == hero.h && other.objid == objid; +} + +bool Goals::VisitHero::operator==(const VisitHero & other) const +{ + return other.hero.h == hero.h && other.objid == objid; +} + +bool Goals::GetArtOfType::operator==(const GetArtOfType & other) const +{ + return other.hero.h == hero.h && other.objid == objid; +} + +bool Goals::VisitTile::operator==(const VisitTile & other) const +{ + return other.hero.h == hero.h && other.tile == tile; +} + +bool Goals::ClearWayTo::operator==(const ClearWayTo & other) const +{ + return other.hero.h == hero.h && other.tile == tile; +} + +bool Goals::DigAtTile::operator==(const DigAtTile & other) const +{ + return other.hero.h == hero.h && other.tile == tile; +} + +bool Goals::AbstractGoal::operator==(const AbstractGoal & g) const +{ + return false; } bool Goals::AbstractGoal::operator<(AbstractGoal & g) //for std::unique @@ -263,14 +287,6 @@ namespace Goals return get() < rhs.get(); //compae by value } - bool BuyArmy::operator==(AbstractGoal & g) - { - if (g.goalType != goalType) - return false; - //if (hero && hero != g.hero) - // return false; - return town == g.town; - } bool BuyArmy::fulfillsMe(TSubgoal goal) { //if (hero && hero != goal->hero) @@ -294,16 +310,6 @@ TSubgoal Trade::whatToDoToAchieve() { return iAmElementar(); } -bool Trade::operator==(AbstractGoal & g) -{ - if (g.goalType != goalType) - return false; - if (g.resID == resID) - if (g.value == value) //TODO: not sure if that logic is consitent - return true; - - return false; -} //TSubgoal AbstractGoal::whatToDoToAchieve() //{ @@ -465,6 +471,57 @@ TSubgoal Win::whatToDoToAchieve() return sptr(Goals::Invalid()); } +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()); +} + +void BuildBoat::accept(VCAI * ai) +{ + TResources boatCost; + shipyard->getBoatCost(boatCost); + + if(!cb->getResourceAmount().canAfford(boatCost)) + { + throw cannotFulfillGoalException("Can not afford boat"); + } + + if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) + { + throw cannotFulfillGoalException("Can not build boat in enemy shipyard"); + } + + if(shipyard->shipyardStatus() != IShipyard::GOOD) + { + throw cannotFulfillGoalException("Shipyard is busy."); + } + + cb->buildBoat(shipyard); +} + +std::string BuildBoat::name() const +{ + return "BuildBoat"; +} + +std::string BuildBoat::completeMessage() const +{ + return "Boat have been built at " + shipyard->o->visitablePos().toString(); +} + TSubgoal FindObj::whatToDoToAchieve() { const CGObjectInstance * o = nullptr; @@ -575,13 +632,6 @@ Goals::VisitObj::VisitObj(int Objid) : CGoal(Goals::VISIT_OBJ) priority = 3; } -bool Goals::VisitObj::operator==(AbstractGoal & g) -{ - if (g.goalType != goalType) - return false; - return g.objid == objid; -} - bool VisitObj::fulfillsMe(TSubgoal goal) { if(goal->goalType == Goals::VISIT_TILE) @@ -622,13 +672,6 @@ TSubgoal VisitHero::whatToDoToAchieve() return sptr(Goals::Invalid()); } -bool Goals::VisitHero::operator==(AbstractGoal & g) -{ - if (g.goalType != goalType) - return false; - return g.hero == hero && g.objid == objid; -} - bool VisitHero::fulfillsMe(TSubgoal goal) { //TODO: VisitObj shoudl not be used for heroes, but... @@ -665,13 +708,6 @@ TSubgoal ClearWayTo::whatToDoToAchieve() return (fh->chooseSolution(getAllPossibleSubgoals())); } -bool Goals::ClearWayTo::operator==(AbstractGoal & g) -{ - if (g.goalType != goalType) - return false; - return g.goalType == goalType && g.tile == tile; -} - bool Goals::ClearWayTo::fulfillsMe(TSubgoal goal) { if (goal->goalType == Goals::VISIT_TILE) @@ -876,14 +912,6 @@ TSubgoal RecruitHero::whatToDoToAchieve() return ai->ah->whatToDo(res, iAmElementar()); //either buy immediately, or collect res } -bool Goals::RecruitHero::operator==(AbstractGoal & g) -{ - if (g.goalType != goalType) - return false; - //TODO: check town and hero - return true; //for now, recruiting any hero will do -} - std::string VisitTile::completeMessage() const { return "Hero " + hero.get()->name + " visited tile " + tile.toString(); @@ -909,13 +937,6 @@ TSubgoal VisitTile::whatToDoToAchieve() return ret; } -bool Goals::VisitTile::operator==(AbstractGoal & g) -{ - if (g.goalType != goalType) - return false; - return g.goalType == goalType && g.tile == tile; -} - TGoalVec VisitTile::getAllPossibleSubgoals() { assert(cb->isInTheMap(tile)); @@ -970,13 +991,6 @@ TSubgoal DigAtTile::whatToDoToAchieve() return sptr(Goals::VisitTile(tile)); } -bool Goals::DigAtTile::operator==(AbstractGoal & g) -{ - if (g.goalType != goalType) - return false; - return g.goalType == goalType && g.tile == tile; -} - TSubgoal BuildThis::whatToDoToAchieve() { auto b = BuildingID(bid); @@ -1195,17 +1209,6 @@ bool CollectRes::fulfillsMe(TSubgoal goal) return false; } -bool Goals::CollectRes::operator==(AbstractGoal & g) -{ - if (g.goalType != goalType) - return false; - if (g.resID == resID) - if (g.value == value) //TODO: not sure if that logic is consitent - return true; - - return false; -} - TSubgoal GatherTroops::whatToDoToAchieve() { std::vector dwellings; diff --git a/AI/VCAI/Goals.h b/AI/VCAI/Goals.h index 45c76fb7f..3adb933e2 100644 --- a/AI/VCAI/Goals.h +++ b/AI/VCAI/Goals.h @@ -59,7 +59,8 @@ enum EGoals CLEAR_WAY_TO, DIG_AT_TILE,//elementar with hero on tile BUY_ARMY, //at specific town - TRADE //val resID at object objid + TRADE, //val resID at object objid + BUILD_BOAT }; //method chaining + clone pattern @@ -74,6 +75,18 @@ enum {LOW_PR = -1}; DLL_EXPORT TSubgoal sptr(const AbstractGoal & tmp); +struct DLL_EXPORT EvaluationContext +{ + uint64_t movementCost; + int manaCost; + uint64_t danger; + + EvaluationContext() + :movementCost(0), danger(0), manaCost(0) + { + } +}; + class DLL_EXPORT AbstractGoal { public: @@ -88,9 +101,11 @@ public: HeroPtr hero; VSETTER(HeroPtr, hero) const CGTownInstance *town; VSETTER(CGTownInstance *, town) int bid; VSETTER(int, bid) + TSubgoal parent; VSETTER(TSubgoal, parent) + EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext) AbstractGoal(EGoals goal = INVALID) - : goalType (goal) + : goalType (goal), evaluationContext() { priority = 0; isElementar = false; @@ -120,7 +135,7 @@ public: EGoals goalType; - std::string name() const; + virtual std::string name() const; virtual std::string completeMessage() const { return "This goal is unspecified!"; @@ -130,20 +145,24 @@ public: 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(); ///Visitor pattern //TODO: make accept work for std::shared_ptr... somehow virtual void accept(VCAI * ai); //unhandled goal will report standard error virtual float accept(FuzzyHelper * f); - virtual bool operator==(AbstractGoal & g); + virtual bool operator==(const AbstractGoal & g) const; bool operator<(AbstractGoal & g); //final virtual bool fulfillsMe(Goals::TSubgoal goal) //TODO: multimethod instead of type check { return false; //use this method to check if goal is fulfilled by another (not equal) goal, operator == is handled spearately } + bool operator!=(const AbstractGoal & g) const + { + return !(*this == g); + } + template void serialize(Handler & h, const int version) { h & goalType; @@ -209,6 +228,16 @@ public: //h & goalType & isElementar & isAbstract & priority; //h & value & resID & objid & aid & tile & hero & town & bid; } + + virtual bool operator==(const AbstractGoal & g) const override + { + if(goalType != g.goalType) + return false; + + return (*this) == (dynamic_cast(g)); + } + + virtual bool operator==(const T & other) const = 0; }; class DLL_EXPORT Invalid : public CGoal @@ -224,6 +253,11 @@ public: return TGoalVec(); } TSubgoal whatToDoToAchieve() override; + + virtual bool operator==(const Invalid & other) const override + { + return true; + } }; class DLL_EXPORT Win : public CGoal @@ -239,6 +273,11 @@ public: return TGoalVec(); } TSubgoal whatToDoToAchieve() override; + + virtual bool operator==(const Win & other) const override + { + return true; + } }; class DLL_EXPORT NotLose : public CGoal @@ -254,6 +293,11 @@ public: return TGoalVec(); } //TSubgoal whatToDoToAchieve() override; + + virtual bool operator==(const NotLose & other) const override + { + return true; + } }; class DLL_EXPORT Conquer : public CGoal @@ -266,6 +310,7 @@ public: } TGoalVec getAllPossibleSubgoals() override; TSubgoal whatToDoToAchieve() override; + virtual bool operator==(const Conquer & other) const override; }; class DLL_EXPORT Build : public CGoal @@ -279,6 +324,33 @@ public: TGoalVec getAllPossibleSubgoals() override; TSubgoal whatToDoToAchieve() override; bool fulfillsMe(TSubgoal goal) override; + + virtual bool operator==(const Build & other) const override + { + return true; + } +}; + +class DLL_EXPORT BuildBoat : public CGoal +{ +private: + const IShipyard * shipyard; + +public: + BuildBoat(const IShipyard * shipyard) + : CGoal(Goals::BUILD_BOAT), shipyard(shipyard) + { + priority = 0; + } + TGoalVec getAllPossibleSubgoals() override + { + return TGoalVec(); + } + TSubgoal whatToDoToAchieve() override; + void accept(VCAI * ai) override; + std::string name() const override; + std::string completeMessage() const override; + virtual bool operator==(const BuildBoat & other) const override; }; class DLL_EXPORT Explore : public CGoal @@ -299,6 +371,7 @@ public: TSubgoal whatToDoToAchieve() override; std::string completeMessage() const override; bool fulfillsMe(TSubgoal goal) override; + virtual bool operator==(const Explore & other) const override; }; class DLL_EXPORT GatherArmy : public CGoal @@ -317,6 +390,7 @@ public: TGoalVec getAllPossibleSubgoals() override; TSubgoal whatToDoToAchieve() override; std::string completeMessage() const override; + virtual bool operator==(const GatherArmy & other) const override; }; class DLL_EXPORT BuyArmy : public CGoal @@ -333,11 +407,11 @@ public: value = val; //expressed in AI unit strength priority = 3;//TODO: evaluate? } - bool operator==(AbstractGoal & g) override; bool fulfillsMe(TSubgoal goal) override; TSubgoal whatToDoToAchieve() override; std::string completeMessage() const override; + virtual bool operator==(const BuyArmy & other) const override; }; class DLL_EXPORT BoostHero : public CGoal @@ -352,6 +426,7 @@ public: { return TGoalVec(); } + virtual bool operator==(const BoostHero & other) const override; //TSubgoal whatToDoToAchieve() override {return sptr(Invalid());}; }; @@ -363,12 +438,18 @@ public: { priority = 1; } + TGoalVec getAllPossibleSubgoals() override { return TGoalVec(); } + TSubgoal whatToDoToAchieve() override; - bool operator==(AbstractGoal & g) override; + + virtual bool operator==(const RecruitHero & other) const override + { + return true; + } }; class DLL_EXPORT BuildThis : public CGoal @@ -396,6 +477,7 @@ public: } TSubgoal whatToDoToAchieve() override; //bool fulfillsMe(TSubgoal goal) override; + virtual bool operator==(const BuildThis & other) const override; }; class DLL_EXPORT CollectRes : public CGoal @@ -416,7 +498,7 @@ public: TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToTrade(); bool fulfillsMe(TSubgoal goal) override; //TODO: Trade - bool operator==(AbstractGoal & g) override; + virtual bool operator==(const CollectRes & other) const override; }; class DLL_EXPORT Trade : public CGoal @@ -435,7 +517,7 @@ public: priority = 3; //trading is instant, but picking resources is free } TSubgoal whatToDoToAchieve() override; - bool operator==(AbstractGoal & g) override; + virtual bool operator==(const Trade & other) const override; }; class DLL_EXPORT GatherTroops : public CGoal @@ -459,6 +541,7 @@ public: } TSubgoal whatToDoToAchieve() override; bool fulfillsMe(TSubgoal goal) override; + virtual bool operator==(const GatherTroops & other) const override; }; class DLL_EXPORT VisitObj : public CGoal //this goal was previously known as GetObj @@ -469,9 +552,9 @@ public: TGoalVec getAllPossibleSubgoals() override; TSubgoal whatToDoToAchieve() override; - bool operator==(AbstractGoal & g) override; bool fulfillsMe(TSubgoal goal) override; std::string completeMessage() const override; + virtual bool operator==(const VisitObj & other) const override; }; class DLL_EXPORT FindObj : public CGoal @@ -499,6 +582,7 @@ public: } TSubgoal whatToDoToAchieve() override; bool fulfillsMe(TSubgoal goal) override; + virtual bool operator==(const FindObj & other) const override; }; class DLL_EXPORT VisitHero : public CGoal @@ -519,9 +603,9 @@ public: return TGoalVec(); } TSubgoal whatToDoToAchieve() override; - bool operator==(AbstractGoal & g) override; bool fulfillsMe(TSubgoal goal) override; std::string completeMessage() const override; + virtual bool operator==(const VisitHero & other) const override; }; class DLL_EXPORT GetArtOfType : public CGoal @@ -542,6 +626,7 @@ public: return TGoalVec(); } TSubgoal whatToDoToAchieve() override; + virtual bool operator==(const GetArtOfType & other) const override; }; class DLL_EXPORT VisitTile : public CGoal @@ -558,8 +643,8 @@ public: } TGoalVec getAllPossibleSubgoals() override; TSubgoal whatToDoToAchieve() override; - bool operator==(AbstractGoal & g) override; std::string completeMessage() const override; + virtual bool operator==(const VisitTile & other) const override; }; class DLL_EXPORT ClearWayTo : public CGoal @@ -584,8 +669,8 @@ public: } TGoalVec getAllPossibleSubgoals() override; TSubgoal whatToDoToAchieve() override; - bool operator==(AbstractGoal & g) override; bool fulfillsMe(TSubgoal goal) override; + virtual bool operator==(const ClearWayTo & other) const override; }; class DLL_EXPORT DigAtTile : public CGoal @@ -607,28 +692,7 @@ public: return TGoalVec(); } TSubgoal whatToDoToAchieve() override; - bool operator==(AbstractGoal & g) override; -}; - -class DLL_EXPORT CIssueCommand : public CGoal -{ - std::function command; - -public: - CIssueCommand() - : CGoal(ISSUE_COMMAND) - { - } - CIssueCommand(std::function _command) - : CGoal(ISSUE_COMMAND), command(_command) - { - priority = 1e10; - } - TGoalVec getAllPossibleSubgoals() override - { - return TGoalVec(); - } - //TSubgoal whatToDoToAchieve() override {return sptr(Invalid());} + virtual bool operator==(const DigAtTile & other) const override; }; } diff --git a/AI/VCAI/Pathfinding/AINodeStorage.cpp b/AI/VCAI/Pathfinding/AINodeStorage.cpp index 54d58a977..638658064 100644 --- a/AI/VCAI/Pathfinding/AINodeStorage.cpp +++ b/AI/VCAI/Pathfinding/AINodeStorage.cpp @@ -38,15 +38,34 @@ bool AINodeStorage::isBattleNode(const CGPathNode * node) const return (getAINode(node)->chainMask & BATTLE_CHAIN) > 0; } -AIPathNode * AINodeStorage::getNode(const int3 & coord, const EPathfindingLayer layer, int chainNumber) +boost::optional AINodeStorage::getOrCreateNode(const int3 & pos, const EPathfindingLayer layer, int chainNumber) { - return &nodes[coord.x][coord.y][coord.z][layer][chainNumber]; + auto chains = nodes[pos.x][pos.y][pos.z][layer]; + + for(AIPathNode & node : chains) + { + if(node.chainMask == chainNumber) + { + return &node; + } + + if(node.chainMask == 0) + { + node.chainMask = chainNumber; + + return &node; + } + } + + return boost::none; } CGPathNode * AINodeStorage::getInitialNode() { auto hpos = hero->getPosition(false); - auto initialNode = getNode(hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND, 0); + auto initialNode = + getOrCreateNode(hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND, NORMAL_CHAIN) + .get(); initialNode->turns = 0; initialNode->moveRemains = hero->movement; @@ -61,7 +80,9 @@ void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPat { AIPathNode & heroNode = nodes[coord.x][coord.y][coord.z][layer][i]; - heroNode.chainMask = i; + heroNode.chainMask = 0; + heroNode.danger = 0; + heroNode.specialAction.reset(); heroNode.update(coord, layer, accessibility); } } @@ -92,12 +113,12 @@ std::vector AINodeStorage::calculateNeighbours( { for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1)) { - auto nextNode = getNode(neighbour, i, srcNode->chainMask); + auto nextNode = getOrCreateNode(neighbour, i, srcNode->chainMask); - if(nextNode->accessible == CGPathNode::NOT_SET) + if(!nextNode || nextNode.get()->accessible == CGPathNode::NOT_SET) continue; - neighbours.push_back(nextNode); + neighbours.push_back(nextNode.get()); } } @@ -115,9 +136,12 @@ std::vector AINodeStorage::calculateTeleportations( for(auto & neighbour : accessibleExits) { - auto node = getNode(neighbour, source.node->layer, srcNode->chainMask); + auto node = getOrCreateNode(neighbour, source.node->layer, srcNode->chainMask); - neighbours.push_back(node); + if(!node) + continue; + + neighbours.push_back(node.get()); } return neighbours; @@ -183,8 +207,11 @@ std::vector AINodeStorage::getChainInfo(int3 pos) const pathNode.coord = current->coord; path.nodes.push_back(pathNode); + path.specialAction = current->specialAction; + current = getAINode(current->theNodeBefore); } + paths.push_back(path); } diff --git a/AI/VCAI/Pathfinding/AINodeStorage.h b/AI/VCAI/Pathfinding/AINodeStorage.h index 2b6db226a..da657a105 100644 --- a/AI/VCAI/Pathfinding/AINodeStorage.h +++ b/AI/VCAI/Pathfinding/AINodeStorage.h @@ -13,17 +13,19 @@ #include "../../../lib/CPathfinder.h" #include "../../../lib/mapObjects/CGHeroInstance.h" #include "../AIUtility.h" +#include "../Goals.h" -class IVirtualObject +class ISpecialAction { public: - virtual void materialize(); + virtual Goals::TSubgoal whatToDo(HeroPtr hero) const = 0; }; struct AIPathNode : public CGPathNode { uint32_t chainMask; uint64_t danger; + std::shared_ptr specialAction; }; struct AIPathNodeInfo @@ -38,6 +40,7 @@ struct AIPathNodeInfo struct AIPath { std::vector nodes; + std::shared_ptr specialAction; AIPath(); @@ -63,9 +66,13 @@ private: public: /// more than 1 chain layer allows us to have more than 1 path to each tile so we can chose more optimal one. - static const int NUM_CHAINS = 2; - static const int NORMAL_CHAIN = 0; - static const int BATTLE_CHAIN = 1; + static const int NUM_CHAINS = 3; + + // chain flags, can be combined + static const int NORMAL_CHAIN = 1; + static const int BATTLE_CHAIN = 2; + static const int CAST_CHAIN = 4; + static const int RESOURCE_CHAIN = 8; AINodeStorage(const int3 & sizes); ~AINodeStorage(); @@ -90,7 +97,7 @@ public: bool isBattleNode(const CGPathNode * node) const; bool hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const; - AIPathNode * getNode(const int3 & coord, const EPathfindingLayer layer, int chainNumber); + boost::optional getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, int chainNumber); std::vector getChainInfo(int3 pos) const; void setHero(HeroPtr heroPtr) diff --git a/AI/VCAI/Pathfinding/AIPathfinder.cpp b/AI/VCAI/Pathfinding/AIPathfinder.cpp index efa32b18f..f9f42371b 100644 --- a/AI/VCAI/Pathfinding/AIPathfinder.cpp +++ b/AI/VCAI/Pathfinding/AIPathfinder.cpp @@ -16,8 +16,8 @@ std::vector> AIPathfinder::storagePool; std::map> AIPathfinder::storageMap; boost::mutex AIPathfinder::storageMutex; -AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb) - :cb(cb) +AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai) + :cb(cb), ai(ai) { } @@ -48,7 +48,7 @@ std::vector AIPathfinder::getPathInfo(HeroPtr hero, int3 tile) storageMap[hero] = nodeStorage; - auto config = std::make_shared(cb, nodeStorage); + auto config = std::make_shared(cb, ai, nodeStorage); nodeStorage->setHero(hero.get()); cb->calculatePaths(config, hero.get()); diff --git a/AI/VCAI/Pathfinding/AIPathfinder.h b/AI/VCAI/Pathfinding/AIPathfinder.h index 11d7c6155..971c0790f 100644 --- a/AI/VCAI/Pathfinding/AIPathfinder.h +++ b/AI/VCAI/Pathfinding/AIPathfinder.h @@ -10,8 +10,9 @@ #pragma once -#include "../AIUtility.h" #include "AINodeStorage.h" +#include "../AIUtility.h" +#include "../VCAI.h" class AIPathfinder { @@ -20,9 +21,10 @@ private: static std::map> storageMap; static boost::mutex storageMutex; CPlayerSpecificInfoCallback * cb; + VCAI * ai; public: - AIPathfinder(CPlayerSpecificInfoCallback * cb); + AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai); std::vector getPathInfo(HeroPtr hero, int3 tile); void clear(); }; diff --git a/AI/VCAI/Pathfinding/AIPathfinderConfig.cpp b/AI/VCAI/Pathfinding/AIPathfinderConfig.cpp index 1957ddbc4..ea5279a23 100644 --- a/AI/VCAI/Pathfinding/AIPathfinderConfig.cpp +++ b/AI/VCAI/Pathfinding/AIPathfinderConfig.cpp @@ -10,6 +10,119 @@ #include "StdInc.h" #include "AIPathfinderConfig.h" #include "../../../CCallback.h" +#include "../../../lib/mapping/CMap.h" +#include "../../../lib/mapObjects/MapObjects.h" + +class BuildBoatAction : public ISpecialAction +{ +private: + const IShipyard * shipyard; + +public: + BuildBoatAction(const IShipyard * shipyard) + :shipyard(shipyard) + { + } + + virtual Goals::TSubgoal whatToDo(HeroPtr hero) const override + { + return sptr(Goals::BuildBoat(shipyard)); + } +}; + +class AILayerTransitionRule : public LayerTransitionRule +{ +private: + CPlayerSpecificInfoCallback * cb; + VCAI * ai; + std::map> virtualBoats; + std::shared_ptr nodeStorage; + +public: + AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr nodeStorage) + :cb(cb), ai(ai), nodeStorage(nodeStorage) + { + setup(); + } + + virtual void process( + const PathNodeInfo & source, + CDestinationNodeInfo & destination, + const PathfinderConfig * pathfinderConfig, + CPathfinderHelper * pathfinderHelper) const override + { + LayerTransitionRule::process(source, destination, pathfinderConfig, pathfinderHelper); + + if(!destination.blocked) + { + return; + } + + if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL + && vstd::contains(virtualBoats, destination.coord)) + { + logAi->trace("Bypassing virtual boat at %s!", destination.coord.toString()); + + nodeStorage->updateAINode(destination.node, [&](AIPathNode * node) + { + std::shared_ptr virtualBoat = virtualBoats.at(destination.coord); + + auto boatNodeOptional = nodeStorage->getOrCreateNode( + node->coord, + node->layer, + node->chainMask | AINodeStorage::RESOURCE_CHAIN); + + if(boatNodeOptional) + { + AIPathNode * boatNode = boatNodeOptional.get(); + + boatNode->specialAction = virtualBoat; + destination.blocked = false; + destination.action = CGPathNode::ENodeAction::EMBARK; + destination.node = boatNode; + } + else + { + logAi->trace( + "Can not allocate boat node while moving %s -> %s", + source.coord.toString(), + destination.coord.toString()); + } + }); + } + } + +private: + void setup() + { + std::vector shipyards; + + for(const CGTownInstance * t : cb->getTownsInfo()) + { + if(t->hasBuilt(BuildingID::SHIPYARD)) + shipyards.push_back(t); + } + + for(const CGObjectInstance * obj : ai->visitableObjs) + { + if(obj->ID != Obj::TOWN) //towns were handled in the previous loop + { + if(const IShipyard * shipyard = IShipyard::castFrom(obj)) + shipyards.push_back(shipyard); + } + } + + for(const IShipyard * shipyard : shipyards) + { + if(shipyard->shipyardStatus() == IShipyard::GOOD) + { + int3 boatLocation = shipyard->bestLocation(); + virtualBoats[boatLocation] = std::make_shared(shipyard); + logAi->debug("Virtual boat added at %s", boatLocation.toString()); + } + } + } +}; class AIMovementAfterDestinationRule : public MovementAfterDestinationRule { @@ -88,8 +201,25 @@ public: return; } - auto destNode = nodeStorage->getAINode(destination.node); - auto battleNode = nodeStorage->getNode(destination.coord, destination.node->layer, destNode->chainMask | AINodeStorage::BATTLE_CHAIN); + const AIPathNode * destNode = nodeStorage->getAINode(destination.node); + auto battleNodeOptional = nodeStorage->getOrCreateNode( + destination.coord, + destination.node->layer, + destNode->chainMask | AINodeStorage::BATTLE_CHAIN); + + if(!battleNodeOptional) + { + logAi->trace( + "Can not allocate battle node while moving %s -> %s", + source.coord.toString(), + destination.coord.toString()); + + destination.blocked = true; + + return; + } + + AIPathNode * battleNode = battleNodeOptional.get(); if(battleNode->locked) { @@ -150,6 +280,13 @@ public: if(blocker == BlockingReason::NONE) return; + if(blocker == BlockingReason::DESTINATION_BLOCKED + && destination.action == CGPathNode::EMBARK + && nodeStorage->getAINode(destination.node)->specialAction) + { + return; + } + if(blocker == BlockingReason::SOURCE_GUARDED && nodeStorage->isBattleNode(source.node)) { auto srcGuardians = cb->getGuardingCreatures(source.coord); @@ -216,6 +353,8 @@ public: "Link src node %s to destination node %s while bypassing guard", source.coord.toString(), destination.coord.toString()); + + return; } } @@ -228,16 +367,27 @@ public: "Link src node %s to destination node %s while bypassing visitable obj", source.coord.toString(), destination.coord.toString()); + + return; + } + + auto aiSourceNode = nodeStorage->getAINode(source.node); + + if(aiSourceNode->specialAction) + { + // there is some action on source tile which should be performed before we can bypass it + destination.node->theNodeBefore = source.node; } } }; std::vector> makeRuleset( CPlayerSpecificInfoCallback * cb, + VCAI * ai, std::shared_ptr nodeStorage) { std::vector> rules = { - std::make_shared(), + std::make_shared(cb, ai, nodeStorage), std::make_shared(), std::make_shared(cb, nodeStorage), std::make_shared(), @@ -250,7 +400,8 @@ std::vector> makeRuleset( AIPathfinderConfig::AIPathfinderConfig( CPlayerSpecificInfoCallback * cb, + VCAI * ai, std::shared_ptr nodeStorage) - :PathfinderConfig(nodeStorage, makeRuleset(cb, nodeStorage)) + :PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage)) { } diff --git a/AI/VCAI/Pathfinding/AIPathfinderConfig.h b/AI/VCAI/Pathfinding/AIPathfinderConfig.h index 6b2cab725..5e05fdf28 100644 --- a/AI/VCAI/Pathfinding/AIPathfinderConfig.h +++ b/AI/VCAI/Pathfinding/AIPathfinderConfig.h @@ -11,9 +11,13 @@ #pragma once #include "AINodeStorage.h" +#include "../VCAI.h" class AIPathfinderConfig : public PathfinderConfig { public: - AIPathfinderConfig(CPlayerSpecificInfoCallback * cb, std::shared_ptr nodeStorage); + AIPathfinderConfig( + CPlayerSpecificInfoCallback * cb, + VCAI * ai, + std::shared_ptr nodeStorage); }; \ No newline at end of file diff --git a/AI/VCAI/Pathfinding/PathfindingManager.cpp b/AI/VCAI/Pathfinding/PathfindingManager.cpp index e4bda806c..72de89f29 100644 --- a/AI/VCAI/Pathfinding/PathfindingManager.cpp +++ b/AI/VCAI/Pathfinding/PathfindingManager.cpp @@ -19,10 +19,10 @@ PathfindingManager::PathfindingManager(CPlayerSpecificInfoCallback * CB, VCAI * { } -void PathfindingManager::setCB(CPlayerSpecificInfoCallback * CB) +void PathfindingManager::init(CPlayerSpecificInfoCallback * CB) { cb = CB; - pathfinder.reset(new AIPathfinder(cb)); + pathfinder.reset(new AIPathfinder(cb, ai)); } void PathfindingManager::setAI(VCAI * AI) @@ -60,10 +60,17 @@ Goals::TGoalVec PathfindingManager::howToVisitObj(ObjectIdRef obj) Goals::TGoalVec PathfindingManager::howToVisitTile(HeroPtr hero, int3 tile, bool allowGatherArmy) { - return findPath(hero, tile, allowGatherArmy, [&](int3 firstTileToGet) -> Goals::TSubgoal + auto result = findPath(hero, tile, allowGatherArmy, [&](int3 firstTileToGet) -> Goals::TSubgoal { return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true)); }); + + for(Goals::TSubgoal solution : result) + { + solution->setparent(sptr(Goals::VisitTile(tile).sethero(hero).setevaluationContext(solution->evaluationContext))); + } + + return result; } Goals::TGoalVec PathfindingManager::howToVisitObj(HeroPtr hero, ObjectIdRef obj, bool allowGatherArmy) @@ -75,13 +82,20 @@ Goals::TGoalVec PathfindingManager::howToVisitObj(HeroPtr hero, ObjectIdRef obj, int3 dest = obj->visitablePos(); - return findPath(hero, dest, allowGatherArmy, [&](int3 firstTileToGet) -> Goals::TSubgoal + auto result = findPath(hero, dest, allowGatherArmy, [&](int3 firstTileToGet) -> Goals::TSubgoal { if(obj->ID.num == Obj::HERO && obj->getOwner() == hero->getOwner()) return sptr(Goals::VisitHero(obj->id.getNum()).sethero(hero).setisAbstract(true)); else return sptr(Goals::VisitObj(obj->id.getNum()).sethero(hero).setisAbstract(true)); }); + + for(Goals::TSubgoal solution : result) + { + solution->setparent(sptr(Goals::VisitObj(obj->id.getNum()).sethero(hero).setevaluationContext(solution->evaluationContext))); + } + + return result; } std::vector PathfindingManager::getPathsToTile(HeroPtr hero, int3 tile) @@ -117,9 +131,24 @@ Goals::TGoalVec PathfindingManager::findPath( { logAi->trace("It's safe for %s to visit tile %s with danger %s", hero->name, dest.toString(), std::to_string(danger)); - auto solution = dest == firstTileToGet - ? doVisitTile(firstTileToGet) - : clearWayTo(hero, firstTileToGet); + Goals::TSubgoal solution; + + if(path.specialAction) + { + solution = path.specialAction->whatToDo(hero); + } + else + { + solution = dest == firstTileToGet + ? doVisitTile(firstTileToGet) + : clearWayTo(hero, firstTileToGet); + } + + if(solution->evaluationContext.danger < danger) + solution->evaluationContext.danger = danger; + + solution->evaluationContext.movementCost += path.movementCost(); + result.push_back(solution); continue; diff --git a/AI/VCAI/Pathfinding/PathfindingManager.h b/AI/VCAI/Pathfinding/PathfindingManager.h index f8626aeef..3bcf9aadd 100644 --- a/AI/VCAI/Pathfinding/PathfindingManager.h +++ b/AI/VCAI/Pathfinding/PathfindingManager.h @@ -17,7 +17,7 @@ class IPathfindingManager { public: virtual ~IPathfindingManager() = default; - virtual void setCB(CPlayerSpecificInfoCallback * CB) = 0; + virtual void init(CPlayerSpecificInfoCallback * CB) = 0; virtual void setAI(VCAI * AI) = 0; virtual void resetPaths() = 0; @@ -49,7 +49,7 @@ public: void resetPaths() override; private: - void setCB(CPlayerSpecificInfoCallback * CB) override; + void init(CPlayerSpecificInfoCallback * CB) override; void setAI(VCAI * AI) override; Goals::TGoalVec findPath( diff --git a/AI/VCAI/ResourceManager.cpp b/AI/VCAI/ResourceManager.cpp index 4c9453a53..cdf1f1acb 100644 --- a/AI/VCAI/ResourceManager.cpp +++ b/AI/VCAI/ResourceManager.cpp @@ -30,7 +30,7 @@ ResourceManager::ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI) { } -void ResourceManager::setCB(CPlayerSpecificInfoCallback * CB) +void ResourceManager::init(CPlayerSpecificInfoCallback * CB) { cb = CB; } diff --git a/AI/VCAI/ResourceManager.h b/AI/VCAI/ResourceManager.h index 93bd2c180..ef9657695 100644 --- a/AI/VCAI/ResourceManager.h +++ b/AI/VCAI/ResourceManager.h @@ -40,7 +40,7 @@ class IResourceManager //: public: IAbstractManager { public: virtual ~IResourceManager() = default; - virtual void setCB(CPlayerSpecificInfoCallback * CB) = 0; + virtual void init(CPlayerSpecificInfoCallback * CB) = 0; virtual void setAI(VCAI * AI) = 0; virtual TResources reservedResources() const = 0; @@ -94,7 +94,7 @@ protected: //not-const actions only for AI virtual TResources estimateIncome() const; virtual Goals::TSubgoal collectResourcesForOurGoal(ResourceObjective &o) const; - void setCB(CPlayerSpecificInfoCallback * CB) override; + void init(CPlayerSpecificInfoCallback * CB) override; void setAI(VCAI * AI) override; private: diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 665c03f74..7a3b30c38 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -613,7 +613,7 @@ void VCAI::init(std::shared_ptr CB) myCb = CB; cbc = CB; - ah->setCB(CB.get()); + ah->init(CB.get()); NET_EVENT_HANDLER; //sets ah->rm->cb playerID = *myCb->getMyColor(); @@ -907,6 +907,14 @@ void VCAI::mainLoop() completeGoal(e.goal); //put in goalsToRemove break; } + catch(cannotFulfillGoalException & e) + { + //it is impossible to continue some goals (like exploration, for example) + //complete abstract goal for now, but maybe main goal finds another path + goalsToRemove.push_back(basicGoal); + logAi->debug("Goal %s decomposition failed: %s", goalToDecompose->name(), e.what()); + break; + } catch (std::exception & e) //decomposition failed, which means we can't decompose entire tree { goalsToRemove.push_back(basicGoal); @@ -2397,6 +2405,7 @@ Goals::TSubgoal VCAI::decomposeGoal(Goals::TSubgoal ultimateGoal) while (maxGoals) { boost::this_thread::interruption_point(); + goal = goal->whatToDoToAchieve(); //may throw if decomposition fails --maxGoals; if (goal == ultimateGoal) //compare objects by value diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index a9972fd1e..d21fa8621 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -696,6 +696,7 @@ void DestinationActionRule::process( { if(destination.action != CGPathNode::ENodeAction::UNKNOWN) { + logAi->trace("Accepted precalculated action at %s", destination.coord.toString()); return; }