mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Merge pull request #497 from nullkiller/ai-pathfinder-boat
AI pathfinding: buy boat
This commit is contained in:
		| @@ -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) | ||||
|   | ||||
| @@ -64,7 +64,7 @@ public: | ||||
| private: | ||||
| 	bool notifyGoalCompleted(Goals::TSubgoal goal); | ||||
|  | ||||
| 	void setCB(CPlayerSpecificInfoCallback * CB) override; | ||||
| 	void init(CPlayerSpecificInfoCallback * CB) override; | ||||
| 	void setAI(VCAI * AI) override; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| @@ -70,6 +70,6 @@ private: | ||||
| 	std::vector<PotentialBuilding> immediateBuildings; //what we can build right now in current town | ||||
| 	std::vector<PotentialBuilding> expensiveBuildings; //what we coudl build but can't afford | ||||
|  | ||||
| 	void setCB(CPlayerSpecificInfoCallback * CB) override; | ||||
| 	void init(CPlayerSpecificInfoCallback * CB) override; | ||||
| 	void setAI(VCAI * AI) override; | ||||
| }; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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<const CGDwelling *> dwellings; | ||||
|   | ||||
							
								
								
									
										134
									
								
								AI/VCAI/Goals.h
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								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<typename Handler> 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<const T &>(g)); | ||||
| 	} | ||||
|  | ||||
| 	virtual bool operator==(const T & other) const = 0; | ||||
| }; | ||||
|  | ||||
| class DLL_EXPORT Invalid : public CGoal<Invalid> | ||||
| @@ -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<Win> | ||||
| @@ -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<NotLose> | ||||
| @@ -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<Conquer> | ||||
| @@ -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<Build> | ||||
| @@ -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<BuildBoat> | ||||
| { | ||||
| 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<Explore> | ||||
| @@ -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<GatherArmy> | ||||
| @@ -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<BuyArmy> | ||||
| @@ -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<BoostHero> | ||||
| @@ -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<BuildThis> | ||||
| @@ -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<CollectRes> | ||||
| @@ -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<Trade> | ||||
| @@ -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<GatherTroops> | ||||
| @@ -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<VisitObj> //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<FindObj> | ||||
| @@ -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<VisitHero> | ||||
| @@ -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<GetArtOfType> | ||||
| @@ -542,6 +626,7 @@ public: | ||||
| 		return TGoalVec(); | ||||
| 	} | ||||
| 	TSubgoal whatToDoToAchieve() override; | ||||
| 	virtual bool operator==(const GetArtOfType & other) const override; | ||||
| }; | ||||
|  | ||||
| class DLL_EXPORT VisitTile : public CGoal<VisitTile> | ||||
| @@ -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<ClearWayTo> | ||||
| @@ -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<DigAtTile> | ||||
| @@ -607,28 +692,7 @@ public: | ||||
| 		return TGoalVec(); | ||||
| 	} | ||||
| 	TSubgoal whatToDoToAchieve() override; | ||||
| 	bool operator==(AbstractGoal & g) override; | ||||
| }; | ||||
|  | ||||
| class DLL_EXPORT CIssueCommand : public CGoal<CIssueCommand> | ||||
| { | ||||
| 	std::function<bool()> command; | ||||
|  | ||||
| public: | ||||
| 	CIssueCommand() | ||||
| 		: CGoal(ISSUE_COMMAND) | ||||
| 	{ | ||||
| 	} | ||||
| 	CIssueCommand(std::function<bool()> _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; | ||||
| }; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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<AIPathNode *> 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<CGPathNode *> 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<CGPathNode *> 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<AIPath> 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); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -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<const ISpecialAction> specialAction; | ||||
| }; | ||||
|  | ||||
| struct AIPathNodeInfo | ||||
| @@ -38,6 +40,7 @@ struct AIPathNodeInfo | ||||
| struct AIPath | ||||
| { | ||||
| 	std::vector<AIPathNodeInfo> nodes; | ||||
| 	std::shared_ptr<const ISpecialAction> 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<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, int chainNumber); | ||||
| 	std::vector<AIPath> getChainInfo(int3 pos) const; | ||||
|  | ||||
| 	void setHero(HeroPtr heroPtr) | ||||
|   | ||||
| @@ -16,8 +16,8 @@ std::vector<std::shared_ptr<AINodeStorage>> AIPathfinder::storagePool; | ||||
| std::map<HeroPtr, std::shared_ptr<AINodeStorage>> 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<AIPath> AIPathfinder::getPathInfo(HeroPtr hero, int3 tile) | ||||
|  | ||||
| 		storageMap[hero] = nodeStorage; | ||||
| 		 | ||||
| 		auto config = std::make_shared<AIPathfinderConfig>(cb, nodeStorage); | ||||
| 		auto config = std::make_shared<AIPathfinderConfig>(cb, ai, nodeStorage); | ||||
|  | ||||
| 		nodeStorage->setHero(hero.get()); | ||||
| 		cb->calculatePaths(config, hero.get()); | ||||
|   | ||||
| @@ -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<HeroPtr, std::shared_ptr<AINodeStorage>> storageMap; | ||||
| 	static boost::mutex storageMutex; | ||||
| 	CPlayerSpecificInfoCallback * cb; | ||||
| 	VCAI * ai; | ||||
|  | ||||
| public: | ||||
| 	AIPathfinder(CPlayerSpecificInfoCallback * cb); | ||||
| 	AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai); | ||||
| 	std::vector<AIPath> getPathInfo(HeroPtr hero, int3 tile); | ||||
| 	void clear(); | ||||
| }; | ||||
|   | ||||
| @@ -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<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats; | ||||
| 	std::shared_ptr<AINodeStorage> nodeStorage; | ||||
|  | ||||
| public: | ||||
| 	AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> 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<const BuildBoatAction> 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<const IShipyard *> 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<BuildBoatAction>(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<std::shared_ptr<IPathfindingRule>> makeRuleset( | ||||
| 	CPlayerSpecificInfoCallback * cb, | ||||
| 	VCAI * ai, | ||||
| 	std::shared_ptr<AINodeStorage> nodeStorage) | ||||
| { | ||||
| 	std::vector<std::shared_ptr<IPathfindingRule>> rules = { | ||||
| 		std::make_shared<LayerTransitionRule>(), | ||||
| 		std::make_shared<AILayerTransitionRule>(cb, ai, nodeStorage), | ||||
| 		std::make_shared<DestinationActionRule>(), | ||||
| 		std::make_shared<AIMovementToDestinationRule>(cb, nodeStorage), | ||||
| 		std::make_shared<MovementCostRule>(), | ||||
| @@ -250,7 +400,8 @@ std::vector<std::shared_ptr<IPathfindingRule>> makeRuleset( | ||||
|  | ||||
| AIPathfinderConfig::AIPathfinderConfig( | ||||
| 	CPlayerSpecificInfoCallback * cb, | ||||
| 	VCAI * ai, | ||||
| 	std::shared_ptr<AINodeStorage> nodeStorage) | ||||
| 	:PathfinderConfig(nodeStorage, makeRuleset(cb, nodeStorage)) | ||||
| 	:PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage)) | ||||
| { | ||||
| } | ||||
|   | ||||
| @@ -11,9 +11,13 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "AINodeStorage.h" | ||||
| #include "../VCAI.h" | ||||
|  | ||||
| class AIPathfinderConfig : public PathfinderConfig | ||||
| { | ||||
| public: | ||||
| 	AIPathfinderConfig(CPlayerSpecificInfoCallback * cb, std::shared_ptr<AINodeStorage> nodeStorage); | ||||
| 	AIPathfinderConfig( | ||||
| 		CPlayerSpecificInfoCallback * cb, | ||||
| 		VCAI * ai,  | ||||
| 		std::shared_ptr<AINodeStorage> nodeStorage); | ||||
| }; | ||||
| @@ -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<AIPath> 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; | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -30,7 +30,7 @@ ResourceManager::ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI) | ||||
| { | ||||
| } | ||||
|  | ||||
| void ResourceManager::setCB(CPlayerSpecificInfoCallback * CB) | ||||
| void ResourceManager::init(CPlayerSpecificInfoCallback * CB) | ||||
| { | ||||
| 	cb = CB; | ||||
| } | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -613,7 +613,7 @@ void VCAI::init(std::shared_ptr<CCallback> 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 | ||||
|   | ||||
| @@ -696,6 +696,7 @@ void DestinationActionRule::process( | ||||
| { | ||||
| 	if(destination.action != CGPathNode::ENodeAction::UNKNOWN) | ||||
| 	{ | ||||
| 		logAi->trace("Accepted precalculated action at %s", destination.coord.toString()); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user