mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Nullkiller: initial cleanup of unused code
This commit is contained in:
		
				
					committed by
					
						 Andrii Danylchenko
						Andrii Danylchenko
					
				
			
			
				
	
			
			
			
						parent
						
							5344df51c6
						
					
				
				
					commit
					af0dcf97c4
				
			| @@ -13,8 +13,6 @@ | ||||
|  | ||||
| AIhelper::AIhelper() | ||||
| { | ||||
| 	resourceManager.reset(new ResourceManager()); | ||||
| 	buildingManager.reset(new BuildingManager()); | ||||
| 	pathfindingManager.reset(new PathfindingManager()); | ||||
| 	armyManager.reset(new ArmyManager()); | ||||
| 	heroManager.reset(new HeroManager()); | ||||
| @@ -24,15 +22,8 @@ AIhelper::~AIhelper() | ||||
| { | ||||
| } | ||||
|  | ||||
| bool AIhelper::notifyGoalCompleted(Goals::TSubgoal goal) | ||||
| { | ||||
| 	return resourceManager->notifyGoalCompleted(goal); | ||||
| } | ||||
|  | ||||
| void AIhelper::init(CPlayerSpecificInfoCallback * CB) | ||||
| { | ||||
| 	resourceManager->init(CB); | ||||
| 	buildingManager->init(CB); | ||||
| 	pathfindingManager->init(CB); | ||||
| 	armyManager->init(CB); | ||||
| 	heroManager->init(CB); | ||||
| @@ -40,8 +31,6 @@ void AIhelper::init(CPlayerSpecificInfoCallback * CB) | ||||
|  | ||||
| void AIhelper::setAI(VCAI * AI) | ||||
| { | ||||
| 	resourceManager->setAI(AI); | ||||
| 	buildingManager->setAI(AI); | ||||
| 	pathfindingManager->setAI(AI); | ||||
| 	armyManager->setAI(AI); | ||||
| 	heroManager->setAI(AI); | ||||
| @@ -53,106 +42,6 @@ void AIhelper::update() | ||||
| 	heroManager->update(); | ||||
| } | ||||
|  | ||||
| bool AIhelper::getBuildingOptions(const CGTownInstance * t) | ||||
| { | ||||
| 	return buildingManager->getBuildingOptions(t); | ||||
| } | ||||
|  | ||||
| BuildingID AIhelper::getMaxPossibleGoldBuilding(const CGTownInstance * t) | ||||
| { | ||||
| 	return buildingManager->getMaxPossibleGoldBuilding(t); | ||||
| } | ||||
|  | ||||
| boost::optional<PotentialBuilding> AIhelper::immediateBuilding() const | ||||
| { | ||||
| 	return buildingManager->immediateBuilding(); | ||||
| } | ||||
|  | ||||
| boost::optional<PotentialBuilding> AIhelper::expensiveBuilding() const | ||||
| { | ||||
| 	return buildingManager->expensiveBuilding(); | ||||
| } | ||||
|  | ||||
| boost::optional<BuildingID> AIhelper::canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const | ||||
| { | ||||
| 	return buildingManager->canBuildAnyStructure(t, buildList, maxDays); | ||||
| } | ||||
|  | ||||
| Goals::TSubgoal AIhelper::whatToDo(TResources & res, Goals::TSubgoal goal) | ||||
| { | ||||
| 	return resourceManager->whatToDo(res, goal); | ||||
| } | ||||
|  | ||||
| Goals::TSubgoal AIhelper::whatToDo() const | ||||
| { | ||||
| 	return resourceManager->whatToDo(); | ||||
| } | ||||
|  | ||||
| bool AIhelper::containsObjective(Goals::TSubgoal goal) const | ||||
| { | ||||
| 	return resourceManager->containsObjective(goal); | ||||
| } | ||||
|  | ||||
| bool AIhelper::hasTasksLeft() const | ||||
| { | ||||
| 	return resourceManager->hasTasksLeft(); | ||||
| } | ||||
|  | ||||
| bool AIhelper::removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal&)> predicate) | ||||
| { | ||||
| 	return resourceManager->removeOutdatedObjectives(predicate); | ||||
| } | ||||
|  | ||||
| bool AIhelper::canAfford(const TResources & cost) const | ||||
| { | ||||
| 	return resourceManager->canAfford(cost); | ||||
| } | ||||
|  | ||||
| TResources AIhelper::reservedResources() const | ||||
| { | ||||
| 	return resourceManager->reservedResources(); | ||||
| } | ||||
|  | ||||
| TResources AIhelper::freeResources() const | ||||
| { | ||||
| 	return resourceManager->freeResources(); | ||||
| } | ||||
|  | ||||
| TResource AIhelper::freeGold() const | ||||
| { | ||||
| 	return resourceManager->freeGold(); | ||||
| } | ||||
|  | ||||
| TResources AIhelper::allResources() const | ||||
| { | ||||
| 	return resourceManager->allResources(); | ||||
| } | ||||
|  | ||||
| TResource AIhelper::allGold() const | ||||
| { | ||||
| 	return resourceManager->allGold(); | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec AIhelper::howToVisitTile(const int3 & tile, bool allowGatherArmy) const | ||||
| { | ||||
| 	return pathfindingManager->howToVisitTile(tile, allowGatherArmy); | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec AIhelper::howToVisitObj(ObjectIdRef obj, bool allowGatherArmy) const | ||||
| { | ||||
| 	return pathfindingManager->howToVisitObj(obj, allowGatherArmy); | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec AIhelper::howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy) const | ||||
| { | ||||
| 	return pathfindingManager->howToVisitTile(hero, tile, allowGatherArmy); | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec AIhelper::howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy) const | ||||
| { | ||||
| 	return pathfindingManager->howToVisitObj(hero, obj, allowGatherArmy); | ||||
| } | ||||
|  | ||||
| std::vector<AIPath> AIhelper::getPathsToTile(const HeroPtr & hero, const int3 & tile) const | ||||
| { | ||||
| 	return pathfindingManager->getPathsToTile(hero, tile); | ||||
|   | ||||
| @@ -14,8 +14,6 @@ | ||||
| 	!!!	Note: Include THIS file at the end of include list to avoid "undefined base class" error | ||||
| */ | ||||
|  | ||||
| #include "ResourceManager.h" | ||||
| #include "BuildingManager.h" | ||||
| #include "ArmyManager.h" | ||||
| #include "HeroManager.h" | ||||
| #include "Pathfinding/PathfindingManager.h" | ||||
| @@ -24,14 +22,11 @@ class ResourceManager; | ||||
| class BuildingManager; | ||||
|  | ||||
|  | ||||
| //indirection interface for various modules | ||||
| class DLL_EXPORT AIhelper : public IResourceManager, public IBuildingManager, public IPathfindingManager, public IArmyManager, public IHeroManager | ||||
| //TODO: remove class, put managers to engine | ||||
| class DLL_EXPORT AIhelper : public IPathfindingManager, public IArmyManager, public IHeroManager | ||||
| { | ||||
| 	friend class VCAI; | ||||
| 	friend struct SetGlobalState; //mess? | ||||
|  | ||||
| 	std::shared_ptr<ResourceManager> resourceManager; | ||||
| 	std::shared_ptr<BuildingManager> buildingManager; | ||||
| 	//std::shared_ptr<ResourceManager> resourceManager; | ||||
| 	//std::shared_ptr<BuildingManager> buildingManager; | ||||
| 	std::shared_ptr<PathfindingManager> pathfindingManager; | ||||
| 	std::shared_ptr<ArmyManager> armyManager; | ||||
| 	std::shared_ptr<HeroManager> heroManager; | ||||
| @@ -40,29 +35,6 @@ public: | ||||
| 	AIhelper(); | ||||
| 	~AIhelper(); | ||||
|  | ||||
| 	bool canAfford(const TResources & cost) const; | ||||
| 	TResources reservedResources() const override; | ||||
| 	TResources freeResources() const override; | ||||
| 	TResource freeGold() const override; | ||||
| 	TResources allResources() const override; | ||||
| 	TResource allGold() const override; | ||||
|  | ||||
| 	Goals::TSubgoal whatToDo(TResources &res, Goals::TSubgoal goal) override; | ||||
| 	Goals::TSubgoal whatToDo() const override; | ||||
| 	bool containsObjective(Goals::TSubgoal goal) const override; | ||||
| 	bool hasTasksLeft() const override; | ||||
| 	bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) override; | ||||
|  | ||||
| 	bool getBuildingOptions(const CGTownInstance * t) override; | ||||
| 	BuildingID getMaxPossibleGoldBuilding(const CGTownInstance * t); | ||||
| 	boost::optional<PotentialBuilding> immediateBuilding() const override; | ||||
| 	boost::optional<PotentialBuilding> expensiveBuilding() const override; | ||||
| 	boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays = 7) const override; | ||||
|  | ||||
| 	Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const override; | ||||
| 	Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const override; | ||||
| 	Goals::TGoalVec howToVisitTile(const int3 & tile, bool allowGatherArmy = true) const override; | ||||
| 	Goals::TGoalVec howToVisitObj(ObjectIdRef obj, bool allowGatherArmy = true) const override; | ||||
| 	std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override; | ||||
| 	std::vector<AIPath> getPathsToTile(const int3 & tile) const override; | ||||
| 	void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override; | ||||
| @@ -95,9 +67,6 @@ public: | ||||
|  | ||||
| 	void update() override; | ||||
|  | ||||
| private: | ||||
| 	bool notifyGoalCompleted(Goals::TSubgoal goal) override; | ||||
|  | ||||
| 	void init(CPlayerSpecificInfoCallback * CB) override; | ||||
| 	void setAI(VCAI * AI) override; | ||||
| }; | ||||
|   | ||||
| @@ -91,6 +91,11 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks() | ||||
| 				if(ai->ah->getHeroRole(hero) == HeroRole::SCOUT && danger == 0 && path.exchangeCount > 1) | ||||
| 					continue; | ||||
|  | ||||
| 				if(path.specialAction && !path.specialAction->canAct(path.targetHero)) | ||||
| 				{ | ||||
| 					auto subGoal = path.specialAction->whatToDo(path.targetHero); | ||||
| 				} | ||||
|  | ||||
| 				auto isSafe = isSafeToVisit(hero, path.heroArmy, danger); | ||||
| 				 | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
|   | ||||
| @@ -148,7 +148,9 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta | ||||
| 		if(treatIsUnderControl) | ||||
| 			continue; | ||||
|  | ||||
| 		if(cb->getResourceAmount(Res::GOLD) > GameConstants::HERO_GOLD_COST) | ||||
| 		if(!town->visitingHero | ||||
| 			&& town->hasBuilt(BuildingID::TAVERN) | ||||
| 			&& cb->getResourceAmount(Res::GOLD) > GameConstants::HERO_GOLD_COST) | ||||
| 		{ | ||||
| 			auto heroesInTavern = cb->getAvailableHeroes(town); | ||||
|  | ||||
|   | ||||
| @@ -56,6 +56,8 @@ Goals::TGoalVec GatherArmyBehavior::getTasks() | ||||
| 	{ | ||||
| 		vstd::concatenate(tasks, upgradeArmy(town)); | ||||
| 	} | ||||
|  | ||||
| 	return tasks; | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * hero) const | ||||
|   | ||||
| @@ -1,257 +0,0 @@ | ||||
| /* | ||||
| * BuildingManager.cpp, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "BuildingManager.h" | ||||
|  | ||||
| #include "../../CCallback.h" | ||||
| #include "../../lib/mapObjects/MapObjects.h" | ||||
|  | ||||
| bool BuildingManager::tryBuildThisStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays) | ||||
| { | ||||
| 	if (maxDays == 0) | ||||
| 	{ | ||||
| 		logAi->warn("Request to build building %d in 0 days!", building.toEnum()); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if (!vstd::contains(t->town->buildings, building)) | ||||
| 		return false; // no such building in town | ||||
|  | ||||
| 	if (t->hasBuilt(building)) //Already built? Shouldn't happen in general | ||||
| 		return true; | ||||
|  | ||||
| 	const CBuilding * buildPtr = t->town->buildings.at(building); | ||||
|  | ||||
| 	auto toBuild = buildPtr->requirements.getFulfillmentCandidates([&](const BuildingID & buildID) | ||||
| 	{ | ||||
| 		return t->hasBuilt(buildID); | ||||
| 	}); | ||||
| 	toBuild.push_back(building); | ||||
|  | ||||
| 	for (BuildingID buildID : toBuild) | ||||
| 	{ | ||||
| 		EBuildingState::EBuildingState canBuild = cb->canBuildStructure(t, buildID); | ||||
| 		if (canBuild == EBuildingState::HAVE_CAPITAL || canBuild == EBuildingState::FORBIDDEN || canBuild == EBuildingState::NO_WATER) | ||||
| 			return false; //we won't be able to build this | ||||
| 	} | ||||
|  | ||||
| 	if (maxDays && toBuild.size() > maxDays) | ||||
| 		return false; | ||||
|  | ||||
| 	//TODO: calculate if we have enough resources to build it in maxDays? | ||||
|  | ||||
| 	for (const auto & buildID : toBuild) | ||||
| 	{ | ||||
| 		const CBuilding * b = t->town->buildings.at(buildID); | ||||
|  | ||||
| 		EBuildingState::EBuildingState canBuild = cb->canBuildStructure(t, buildID); | ||||
| 		if (canBuild == EBuildingState::ALLOWED) | ||||
| 		{ | ||||
| 			PotentialBuilding pb; | ||||
| 			pb.bid = buildID; | ||||
| 			pb.price = t->getBuildingCost(buildID); | ||||
| 			immediateBuildings.push_back(pb); //these are checked again in try | ||||
| 			return true; | ||||
| 		} | ||||
| 		else if (canBuild == EBuildingState::PREREQUIRES) | ||||
| 		{ | ||||
| 			// can happen when dependencies have their own missing dependencies | ||||
| 			if (tryBuildThisStructure(t, buildID, maxDays - 1)) | ||||
| 				return true; | ||||
| 		} | ||||
| 		else if (canBuild == EBuildingState::MISSING_BASE) | ||||
| 		{ | ||||
| 			if (tryBuildThisStructure(t, b->upgrade, maxDays - 1)) | ||||
| 				return true; | ||||
| 		} | ||||
| 		else if (canBuild == EBuildingState::NO_RESOURCES) | ||||
| 		{ | ||||
| 			//we may need to gather resources for those | ||||
| 			PotentialBuilding pb; | ||||
| 			pb.bid = buildID; | ||||
| 			pb.price = t->getBuildingCost(buildID); | ||||
| 			expensiveBuildings.push_back(pb); //these are checked again in try | ||||
| 			return false; | ||||
| 		} | ||||
| 		else | ||||
| 			return false; | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool BuildingManager::tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays) | ||||
| { | ||||
| 	for (const auto & building : buildList) | ||||
| 	{ | ||||
| 		if (t->hasBuilt(building)) | ||||
| 			continue; | ||||
| 		return tryBuildThisStructure(t, building, maxDays); | ||||
|  | ||||
| 	} | ||||
| 	return false; //Can't build anything | ||||
| } | ||||
|  | ||||
| boost::optional<BuildingID> BuildingManager::canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const | ||||
| { | ||||
| 	for (const auto & building : buildList) | ||||
| 	{ | ||||
| 		if (t->hasBuilt(building)) | ||||
| 			continue; | ||||
| 		switch (cb->canBuildStructure(t, building)) | ||||
| 		{ | ||||
| 			case EBuildingState::ALLOWED: | ||||
| 			case EBuildingState::NO_RESOURCES: //TODO: allow this via optional parameter? | ||||
| 				return boost::optional<BuildingID>(building); | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
| 	return boost::optional<BuildingID>(); //Can't build anything | ||||
| } | ||||
|  | ||||
| bool BuildingManager::tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays) | ||||
| { | ||||
| 	for (const auto & building : buildList) | ||||
| 	{ | ||||
| 		if (t->hasBuilt(building)) | ||||
| 			continue; | ||||
| 		return tryBuildThisStructure(t, building, maxDays); | ||||
| 	} | ||||
| 	return false; //Nothing to build | ||||
| } | ||||
|  | ||||
| void BuildingManager::init(CPlayerSpecificInfoCallback * CB) | ||||
| { | ||||
| 	cb = CB; | ||||
| } | ||||
|  | ||||
| void BuildingManager::setAI(VCAI * AI) | ||||
| { | ||||
| 	ai = AI; | ||||
| } | ||||
| //Set of buildings for different goals. Does not include any prerequisites. | ||||
| static const std::vector<BuildingID> essential = { BuildingID::TAVERN, BuildingID::TOWN_HALL }; | ||||
| static const std::vector<BuildingID> basicGoldSource = { BuildingID::TOWN_HALL, BuildingID::CITY_HALL }; | ||||
| static const std::vector<BuildingID> capitolAndRequirements = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::CAPITOL }; | ||||
| static const std::vector<BuildingID> unitsSource = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3, | ||||
| BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7 }; | ||||
| static const std::vector<BuildingID> unitsUpgrade = { BuildingID::DWELL_LVL_1_UP, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP, | ||||
| BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP }; | ||||
| static const std::vector<BuildingID> unitGrowth = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::HORDE_1, | ||||
| BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR }; | ||||
| static const std::vector<BuildingID> _spells = { BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3, | ||||
| BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5 }; | ||||
| static const std::vector<BuildingID> extra = { BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3, | ||||
| BuildingID::SPECIAL_4, BuildingID::SHIPYARD }; // all remaining buildings | ||||
|  | ||||
| bool BuildingManager::getBuildingOptions(const CGTownInstance * t) | ||||
| { | ||||
| 	//TODO make *real* town development system | ||||
| 	//TODO: faction-specific development: use special buildings, build dwellings in better order, etc | ||||
| 	//TODO: build resource silo, defences when needed | ||||
| 	//Possible - allow "locking" on specific building (build prerequisites and then building itself) | ||||
| 	 | ||||
| 	//TODO: There is some disabled building code in GatherTroops and GatherArmy - take it into account when enhancing building. For now AI works best with building only via Build goal. | ||||
|  | ||||
| 	immediateBuildings.clear(); | ||||
| 	expensiveBuildings.clear(); | ||||
|  | ||||
| 	//below algorithm focuses on economy growth at start of the game, saving money instead of build rushing is handled by Build goal | ||||
| 	//changing code blocks order will alter behavior by changing order of adding elements to immediateBuildings / expensiveBuildings | ||||
|  | ||||
| 	TResources currentRes = cb->getResourceAmount(); | ||||
| 	TResources currentIncome = t->dailyIncome(); | ||||
|  | ||||
| 	if(tryBuildAnyStructure(t, essential)) | ||||
| 		return true; | ||||
|  | ||||
| 	//the more gold the better and less problems later //TODO: what about building mage guild / marketplace etc. with city hall disabled in editor? | ||||
| 	if(tryBuildNextStructure(t, basicGoldSource)) | ||||
| 		return true; | ||||
|  | ||||
| 	//workaround for mantis #2696 - build capitol with separate algorithm if it is available | ||||
| 	if(vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && getMaxPossibleGoldBuilding(t) == BuildingID::CAPITOL) | ||||
| 	{ | ||||
| 		if(tryBuildNextStructure(t, capitolAndRequirements)) | ||||
| 			return true; | ||||
| 	} | ||||
|  | ||||
| 	if(!t->hasBuilt(BuildingID::FORT)) //in vast majority of situations fort is top priority building if we already have city hall, TODO: unite with unitGrowth building chain | ||||
| 		if(tryBuildThisStructure(t, BuildingID::FORT)) | ||||
| 			return true; | ||||
|  | ||||
|  | ||||
|  | ||||
| 	if (cb->getDate(Date::DAY_OF_WEEK) > 6) // last 2 days of week - try to focus on growth | ||||
| 	{ | ||||
| 		if (tryBuildNextStructure(t, unitGrowth, 2)) | ||||
| 			return true; | ||||
| 	} | ||||
|  | ||||
| 	//try building dwellings | ||||
| 	if (t->hasBuilt(BuildingID::FORT)) | ||||
| 	{ | ||||
| 		if (tryBuildAnyStructure(t, unitsSource, 8 - cb->getDate(Date::DAY_OF_WEEK))) | ||||
| 			return true; | ||||
| 	} | ||||
|  | ||||
| 	//try to upgrade dwelling | ||||
| 	for (int i = 0; i < unitsUpgrade.size(); i++) | ||||
| 	{ | ||||
| 		if (t->hasBuilt(unitsSource[i]) && !t->hasBuilt(unitsUpgrade[i]) && t->hasBuilt(BuildingID::FORT)) | ||||
| 		{ | ||||
| 			if (tryBuildThisStructure(t, unitsUpgrade[i])) | ||||
| 				return true; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//remaining tasks | ||||
| 	if (tryBuildNextStructure(t, _spells)) | ||||
| 		return true; | ||||
| 	if (tryBuildAnyStructure(t, extra)) | ||||
| 		return true; | ||||
|  | ||||
| 	//at the end, try to get and build any extra buildings with nonstandard slots (for example HotA 3rd level dwelling) | ||||
| 	std::vector<BuildingID> extraBuildings; | ||||
| 	for (auto buildingInfo : t->town->buildings) | ||||
| 	{ | ||||
| 		if (buildingInfo.first > 43) | ||||
| 			extraBuildings.push_back(buildingInfo.first); | ||||
| 	} | ||||
| 	return tryBuildAnyStructure(t, extraBuildings); | ||||
| } | ||||
|  | ||||
| BuildingID BuildingManager::getMaxPossibleGoldBuilding(const CGTownInstance * t) | ||||
| { | ||||
| 	if(cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN) | ||||
| 		return BuildingID::CAPITOL; | ||||
| 	else if(cb->canBuildStructure(t, BuildingID::CITY_HALL) != EBuildingState::FORBIDDEN) | ||||
| 		return BuildingID::CITY_HALL; | ||||
| 	else if(cb->canBuildStructure(t, BuildingID::TOWN_HALL) != EBuildingState::FORBIDDEN) | ||||
| 		return BuildingID::TOWN_HALL; | ||||
| 	else | ||||
| 		return BuildingID::VILLAGE_HALL; | ||||
| } | ||||
|  | ||||
| boost::optional<PotentialBuilding> BuildingManager::immediateBuilding() const | ||||
| { | ||||
| 	if (immediateBuildings.size()) | ||||
| 		return boost::optional<PotentialBuilding>(immediateBuildings.front()); //back? whatever | ||||
| 	else | ||||
| 		return boost::optional<PotentialBuilding>(); | ||||
| } | ||||
|  | ||||
| boost::optional<PotentialBuilding> BuildingManager::expensiveBuilding() const | ||||
| { | ||||
| 	if (expensiveBuildings.size()) | ||||
| 		return boost::optional<PotentialBuilding>(expensiveBuildings.front()); | ||||
| 	else | ||||
| 		return boost::optional<PotentialBuilding>(); | ||||
| } | ||||
| @@ -1,75 +0,0 @@ | ||||
| /* | ||||
| * BuildingManager.h, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "AIUtility.h" | ||||
|  | ||||
| #include "../../lib/GameConstants.h" | ||||
| #include "../../lib/VCMI_Lib.h" | ||||
| #include "../../lib/CTownHandler.h" | ||||
| #include "../../lib/CBuildingHandler.h" | ||||
| #include "VCAI.h" | ||||
|  | ||||
| struct DLL_EXPORT PotentialBuilding | ||||
| { | ||||
| 	BuildingID bid; | ||||
| 	TResources price; | ||||
| 	//days to build? | ||||
| }; | ||||
|  | ||||
| class DLL_EXPORT IBuildingManager //: public: IAbstractManager | ||||
| { //info about town development | ||||
| public: | ||||
| 	virtual ~IBuildingManager() = default; | ||||
| 	virtual void init(CPlayerSpecificInfoCallback * CB) = 0; | ||||
| 	virtual void setAI(VCAI * AI) = 0; | ||||
|  | ||||
| 	virtual bool getBuildingOptions(const CGTownInstance * t) = 0; | ||||
| 	virtual boost::optional<PotentialBuilding> immediateBuilding() const = 0; | ||||
| 	virtual boost::optional<PotentialBuilding> expensiveBuilding() const = 0; | ||||
| 	virtual boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const = 0; | ||||
| }; | ||||
|  | ||||
| class DLL_EXPORT BuildingManager : public IBuildingManager | ||||
| { | ||||
| 	friend class VCAI; | ||||
| 	friend class AIhelper; | ||||
| 	friend struct SetGlobalState; | ||||
|  | ||||
| 	CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback | ||||
| 	VCAI * ai; | ||||
|  | ||||
| public: | ||||
|  | ||||
| 	//try build anything in given town, and execute resulting Goal if any | ||||
| 	bool getBuildingOptions(const CGTownInstance * t) override; | ||||
| 	BuildingID getMaxPossibleGoldBuilding(const CGTownInstance * t); | ||||
| 	boost::optional<PotentialBuilding> immediateBuilding() const override; | ||||
| 	boost::optional<PotentialBuilding> expensiveBuilding() const override; | ||||
| 	boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays = 7) const override; | ||||
|  | ||||
| protected: | ||||
|  | ||||
| 	bool tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7); | ||||
| 	//try build first unbuilt structure | ||||
| 	bool tryBuildThisStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays = 7); | ||||
| 	//try build ANY unbuilt structure | ||||
| 	 | ||||
| 	bool tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7); | ||||
|  | ||||
| private: | ||||
| 	//TODO: remember current town? | ||||
| 	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 init(CPlayerSpecificInfoCallback * CB) override; | ||||
| 	void setAI(VCAI * AI) override; | ||||
| }; | ||||
| @@ -17,19 +17,12 @@ set(VCAI_SRCS | ||||
| 		AIhelper.cpp | ||||
| 		ArmyManager.cpp | ||||
| 		HeroManager.cpp | ||||
| 		ResourceManager.cpp | ||||
| 		BuildingManager.cpp | ||||
| 		SectorMap.cpp | ||||
| 		BuildingManager.cpp | ||||
| 		MapObjectsEvaluator.cpp | ||||
| 		FuzzyEngines.cpp | ||||
| 		FuzzyHelper.cpp | ||||
| 		Goals/AbstractGoal.cpp | ||||
| 		Goals/BuildBoat.cpp | ||||
| 		Goals/Build.cpp | ||||
| 		Goals/BuildThis.cpp | ||||
| 		Goals/Explore.cpp | ||||
| 		Goals/GatherArmy.cpp | ||||
| 		Goals/DismissHero.cpp | ||||
| 		Goals/GatherTroops.cpp | ||||
| 		Goals/BuyArmy.cpp | ||||
| @@ -41,8 +34,6 @@ set(VCAI_SRCS | ||||
| 		Goals/CollectRes.cpp | ||||
| 		Goals/Trade.cpp | ||||
| 		Goals/RecruitHero.cpp | ||||
| 		Goals/Conquer.cpp | ||||
| 		Goals/ClearWayTo.cpp | ||||
| 		Goals/DigAtTile.cpp | ||||
| 		Goals/GetArtOfType.cpp | ||||
| 		Goals/FindObj.cpp | ||||
| @@ -85,10 +76,6 @@ set(VCAI_HEADERS | ||||
| 		AIhelper.h | ||||
| 		ArmyManager.h | ||||
| 		HeroManager.h | ||||
| 		ResourceManager.h | ||||
| 		BuildingManager.h | ||||
| 		SectorMap.h | ||||
| 		BuildingManager.h | ||||
| 		MapObjectsEvaluator.h | ||||
| 		FuzzyEngines.h | ||||
| 		FuzzyHelper.h | ||||
| @@ -96,10 +83,7 @@ set(VCAI_HEADERS | ||||
| 		Goals/CGoal.h | ||||
| 		Goals/Invalid.h | ||||
| 		Goals/BuildBoat.h | ||||
| 		Goals/Build.h | ||||
| 		Goals/BuildThis.h | ||||
| 		Goals/Explore.h | ||||
| 		Goals/GatherArmy.h | ||||
| 		Goals/DismissHero.h | ||||
| 		Goals/GatherTroops.h | ||||
| 		Goals/BuyArmy.h | ||||
| @@ -111,8 +95,6 @@ set(VCAI_HEADERS | ||||
| 		Goals/CollectRes.h | ||||
| 		Goals/Trade.h | ||||
| 		Goals/RecruitHero.h | ||||
| 		Goals/Conquer.h | ||||
| 		Goals/ClearWayTo.h | ||||
| 		Goals/DigAtTile.h | ||||
| 		Goals/GetArtOfType.h | ||||
| 		Goals/FindObj.h | ||||
|   | ||||
| @@ -184,7 +184,7 @@ void Nullkiller::makeTurn() | ||||
| 		} | ||||
| 		catch(goalFulfilledException &) | ||||
| 		{ | ||||
| 			logAi->trace(bestTask->completeMessage()); | ||||
| 			logAi->trace("Task %s completed", bestTask->name()); | ||||
| 		} | ||||
| 		catch(std::exception & e) | ||||
| 		{ | ||||
|   | ||||
| @@ -23,7 +23,8 @@ extern FuzzyHelper * fh; | ||||
|  | ||||
| engineBase::engineBase() | ||||
| { | ||||
| 	engine.addRuleBlock(&rules); | ||||
| 	rules = new fl::RuleBlock(); | ||||
| 	engine.addRuleBlock(rules); | ||||
| } | ||||
|  | ||||
| void engineBase::configure() | ||||
| @@ -34,7 +35,7 @@ void engineBase::configure() | ||||
|  | ||||
| void engineBase::addRule(const std::string & txt) | ||||
| { | ||||
| 	rules.addRule(fl::Rule::parse(txt, &engine)); | ||||
| 	rules->addRule(fl::Rule::parse(txt, &engine)); | ||||
| } | ||||
|  | ||||
| struct armyStructure | ||||
|   | ||||
| @@ -17,7 +17,7 @@ class engineBase //subclasses create fuzzylite variables with "new" that are not | ||||
| { | ||||
| protected: | ||||
| 	fl::Engine engine; | ||||
| 	fl::RuleBlock rules; | ||||
| 	fl::RuleBlock * rules; | ||||
| 	virtual void configure(); | ||||
| 	void addRule(const std::string & txt); | ||||
| public: | ||||
|   | ||||
| @@ -19,44 +19,6 @@ FuzzyHelper * fh; | ||||
| extern boost::thread_specific_ptr<VCAI> ai; | ||||
| extern boost::thread_specific_ptr<CCallback> cb; | ||||
|  | ||||
| Goals::TSubgoal FuzzyHelper::chooseSolution(Goals::TGoalVec vec) | ||||
| { | ||||
| 	if(vec.empty()) | ||||
| 	{ | ||||
| 		logAi->debug("FuzzyHelper found no goals. Returning Goals::Invalid."); | ||||
|  | ||||
| 		//no possibilities found | ||||
| 		return sptr(Goals::Invalid()); | ||||
| 	} | ||||
|  | ||||
| 	//a trick to switch between heroes less often - calculatePaths is costly | ||||
| 	auto sortByHeroes = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool | ||||
| 	{ | ||||
| 		return lhs->hero.h < rhs->hero.h; | ||||
| 	}; | ||||
| 	boost::sort(vec, sortByHeroes); | ||||
|  | ||||
| 	for(auto g : vec) | ||||
| 	{ | ||||
| 		setPriority(g); | ||||
| 	} | ||||
|  | ||||
| 	auto compareGoals = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool | ||||
| 	{ | ||||
| 		return lhs->priority < rhs->priority; | ||||
| 	}; | ||||
|  | ||||
| 	for(auto goal : vec) | ||||
| 	{ | ||||
| 		logAi->trace("FuzzyHelper evaluated goal %s, priority=%.4f", goal->name(), goal->priority); | ||||
| 	} | ||||
|  | ||||
| 	Goals::TSubgoal result = *boost::max_element(vec, compareGoals); | ||||
|  | ||||
| 	logAi->debug("FuzzyHelper returned goal %s, priority=%.4f", result->name(), result->priority); | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| ui64 FuzzyHelper::estimateBankDanger(const CBank * bank) | ||||
| { | ||||
| @@ -77,134 +39,6 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank) | ||||
|  | ||||
| } | ||||
|  | ||||
| float FuzzyHelper::evaluate(Goals::VisitTile & g) | ||||
| { | ||||
| 	if(g.parent) | ||||
| 	{ | ||||
| 		g.parent->accept(this); | ||||
| 	} | ||||
|  | ||||
| 	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::AdventureSpellCast & g) | ||||
| { | ||||
| 	if(!g.parent) | ||||
| 	{ | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	const CSpell * spell = g.getSpell(); | ||||
| 	const float spellCastPenalty = (float)g.hero->getSpellCost(spell) / g.hero->mana; | ||||
|  | ||||
| 	return g.parent->accept(this) - spellCastPenalty; | ||||
| } | ||||
|  | ||||
| float FuzzyHelper::evaluate(Goals::CompleteQuest & g) | ||||
| { | ||||
| 	// TODO: How to evaluate quest complexity? | ||||
| 	const float questPenalty = 0.2; | ||||
|  | ||||
| 	if(!g.parent) | ||||
| 	{ | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return g.parent->accept(this) * questPenalty; | ||||
| } | ||||
|  | ||||
| float FuzzyHelper::evaluate(Goals::VisitObj & g) | ||||
| { | ||||
| 	if(g.parent) | ||||
| 	{ | ||||
| 		g.parent->accept(this); | ||||
| 	} | ||||
|  | ||||
| 	return visitObjEngine.evaluate(g); | ||||
| } | ||||
|  | ||||
| float FuzzyHelper::evaluate(Goals::VisitHero & g) | ||||
| { | ||||
| 	auto obj = ai->myCb->getObj(ObjectInstanceID(g.objid)); //we assume for now that these goals are similar | ||||
| 	if(!obj) | ||||
| 		return -100; //hero died in the meantime | ||||
| 	else | ||||
| 	{ | ||||
| 		g.setpriority(Goals::VisitTile(obj->visitablePos()).sethero(g.hero).accept(this)); | ||||
| 	} | ||||
| 	return g.priority; | ||||
| } | ||||
| float FuzzyHelper::evaluate(Goals::GatherArmy & g) | ||||
| { | ||||
| 	//the more army we need, the more important goal | ||||
| 	//the more army we lack, the less important goal | ||||
| 	float army = g.hero->getArmyStrength(); | ||||
| 	float ratio = g.value / std::max(g.value - army, 2000.0f); //2000 is about the value of hero recruited from tavern | ||||
| 	return 5 * (ratio / (ratio + 2)); //so 50% army gives 2.5, asymptotic 5 | ||||
| } | ||||
|  | ||||
| float FuzzyHelper::evaluate(Goals::ClearWayTo & g) | ||||
| { | ||||
| 	if (!g.hero.h) | ||||
| 		return 0; //lowest priority | ||||
|  | ||||
| 	return g.whatToDoToAchieve()->accept(this); | ||||
| } | ||||
|  | ||||
| float FuzzyHelper::evaluate(Goals::BuildThis & g) | ||||
| { | ||||
| 	return g.priority; //TODO | ||||
| } | ||||
| float FuzzyHelper::evaluate(Goals::DigAtTile & g) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| float FuzzyHelper::evaluate(Goals::CollectRes & g) | ||||
| { | ||||
| 	return g.priority; //handled by ResourceManager | ||||
| } | ||||
| float FuzzyHelper::evaluate(Goals::Build & g) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| float FuzzyHelper::evaluate(Goals::BuyArmy & g) | ||||
| { | ||||
| 	return g.priority; | ||||
| } | ||||
| float FuzzyHelper::evaluate(Goals::Explore & g) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
| float FuzzyHelper::evaluate(Goals::RecruitHero & g) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
| float FuzzyHelper::evaluate(Goals::Invalid & g) | ||||
| { | ||||
| 	return -1e10; | ||||
| } | ||||
| float FuzzyHelper::evaluate(Goals::AbstractGoal & g) | ||||
| { | ||||
| 	logAi->warn("Cannot evaluate goal %s", g.name()); | ||||
| 	return g.priority; | ||||
| } | ||||
| void FuzzyHelper::setPriority(Goals::TSubgoal & g) //calls evaluate - Visitor pattern | ||||
| { | ||||
| 	g->setpriority(g->accept(this)); //this enforces returned value is set | ||||
| } | ||||
|  | ||||
| ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor) | ||||
| { | ||||
| 	return evaluateDanger(tile, visitor, ai.get()); | ||||
|   | ||||
| @@ -16,33 +16,9 @@ class DLL_EXPORT FuzzyHelper | ||||
| { | ||||
| public: | ||||
| 	TacticalAdvantageEngine tacticalAdvantageEngine; | ||||
| 	VisitTileEngine visitTileEngine; | ||||
| 	VisitObjEngine visitObjEngine; | ||||
|  | ||||
| 	float evaluate(Goals::Explore & g); | ||||
| 	float evaluate(Goals::RecruitHero & g); | ||||
| 	float evaluate(Goals::VisitTile & g); | ||||
| 	float evaluate(Goals::VisitObj & g); | ||||
| 	float evaluate(Goals::VisitHero & g); | ||||
| 	float evaluate(Goals::BuildThis & g); | ||||
| 	float evaluate(Goals::DigAtTile & g); | ||||
| 	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::CompleteQuest & g); | ||||
| 	float evaluate(Goals::AdventureSpellCast & g); | ||||
| 	float evaluate(Goals::Invalid & g); | ||||
| 	float evaluate(Goals::AbstractGoal & g); | ||||
| 	void setPriority(Goals::TSubgoal & g); | ||||
|  | ||||
| 	ui64 estimateBankDanger(const CBank * bank); //TODO: move to another class? | ||||
|  | ||||
| 	Goals::TSubgoal chooseSolution(Goals::TGoalVec vec); | ||||
| 	//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec); | ||||
|  | ||||
| 	ui64 evaluateDanger(const CGObjectInstance * obj, const VCAI * ai); | ||||
| 	ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai, bool checkGuards = true); | ||||
| 	ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor); | ||||
|   | ||||
| @@ -12,8 +12,6 @@ | ||||
| #include "../VCAI.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../FuzzyHelper.h" | ||||
| #include "../ResourceManager.h" | ||||
| #include "../BuildingManager.h" | ||||
| #include "../../../lib/mapping/CMap.h" //for victory conditions | ||||
| #include "../../../lib/CPathfinder.h" | ||||
| #include "../../../lib/StringConstants.h" | ||||
| @@ -115,63 +113,12 @@ bool AbstractGoal::operator==(const AbstractGoal & g) const | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool AbstractGoal::operator<(AbstractGoal & g) //for std::unique | ||||
| { | ||||
| 	//TODO: make sure it gets goals consistent with == operator | ||||
| 	if (goalType < g.goalType) | ||||
| 		return true; | ||||
| 	if (goalType > g.goalType) | ||||
| 		return false; | ||||
| 	if (hero < g.hero) | ||||
| 		return true; | ||||
| 	if (hero > g.hero) | ||||
| 		return false; | ||||
| 	if (tile < g.tile) | ||||
| 		return true; | ||||
| 	if (g.tile < tile) | ||||
| 		return false; | ||||
| 	if (objid < g.objid) | ||||
| 		return true; | ||||
| 	if (objid > g.objid) | ||||
| 		return false; | ||||
| 	if (town < g.town) | ||||
| 		return true; | ||||
| 	if (town > g.town) | ||||
| 		return false; | ||||
| 	if (value < g.value) | ||||
| 		return true; | ||||
| 	if (value > g.value) | ||||
| 		return false; | ||||
| 	if (priority < g.priority) | ||||
| 		return true; | ||||
| 	if (priority > g.priority) | ||||
| 		return false; | ||||
| 	if (resID < g.resID) | ||||
| 		return true; | ||||
| 	if (resID > g.resID) | ||||
| 		return false; | ||||
| 	if (bid < g.bid) | ||||
| 		return true; | ||||
| 	if (bid > g.bid) | ||||
| 		return false; | ||||
| 	if (aid < g.aid) | ||||
| 		return true; | ||||
| 	if (aid > g.aid) | ||||
| 		return false; | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| //TODO: find out why the following are not generated automatically on MVS? | ||||
| bool TSubgoal::operator==(const TSubgoal & rhs) const | ||||
| { | ||||
| 	return *get() == *rhs.get(); //comparison for Goals is overloaded, so they don't need to be identical to match | ||||
| } | ||||
|  | ||||
| bool TSubgoal::operator<(const TSubgoal & rhs) const | ||||
| { | ||||
| 	return get() < rhs.get(); //compae by value | ||||
| } | ||||
|  | ||||
| bool AbstractGoal::invalid() const | ||||
| { | ||||
| 	return goalType == EGoals::INVALID; | ||||
| @@ -182,11 +129,6 @@ void AbstractGoal::accept(VCAI * ai) | ||||
| 	ai->tryRealize(*this); | ||||
| } | ||||
|  | ||||
| float AbstractGoal::accept(FuzzyHelper * f) | ||||
| { | ||||
| 	return f->evaluate(*this); | ||||
| } | ||||
|  | ||||
| EvaluationContext::EvaluationContext() | ||||
| 	: movementCost(0.0), | ||||
| 	manaCost(0), | ||||
|   | ||||
| @@ -30,7 +30,6 @@ namespace Goals | ||||
| 	class BuildThis; | ||||
| 	class DigAtTile; | ||||
| 	class CollectRes; | ||||
| 	class Build; | ||||
| 	class BuyArmy; | ||||
| 	class BuildBoat; | ||||
| 	class GatherArmy; | ||||
| @@ -43,7 +42,8 @@ namespace Goals | ||||
| 	enum EGoals | ||||
| 	{ | ||||
| 		INVALID = -1, | ||||
| 		WIN, CONQUER, BUILD, //build needs to get a real reasoning | ||||
| 		WIN, CONQUER, | ||||
| 		BUILD, | ||||
| 		EXPLORE, GATHER_ARMY, | ||||
| 		BOOST_HERO, | ||||
| 		RECRUIT_HERO, | ||||
| @@ -162,25 +162,15 @@ namespace Goals | ||||
| 		EGoals goalType; | ||||
|  | ||||
| 		virtual std::string name() const; | ||||
| 		virtual std::string completeMessage() const | ||||
| 		{ | ||||
| 			return "This goal is unspecified!"; | ||||
| 		} | ||||
|  | ||||
| 		bool invalid() const; | ||||
|  | ||||
| 		///Visitor pattern | ||||
| 		//TODO: make accept work for std::shared_ptr... somehow | ||||
| 		virtual void accept(VCAI * ai); //unhandled goal will report standard error | ||||
| 		virtual float accept(FuzzyHelper * f); | ||||
|  | ||||
| 		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); | ||||
|   | ||||
| @@ -77,8 +77,3 @@ std::string AdventureSpellCast::name() const | ||||
| { | ||||
| 	return "AdventureSpellCast " + spellID.toSpell()->name; | ||||
| } | ||||
|  | ||||
| std::string AdventureSpellCast::completeMessage() const | ||||
| { | ||||
| 	return "Spell casted successfully  " + spellID.toSpell()->name; | ||||
| } | ||||
|   | ||||
| @@ -38,7 +38,6 @@ namespace Goals | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		void accept(VCAI * ai) override; | ||||
| 		std::string name() const override; | ||||
| 		std::string completeMessage() const override; | ||||
| 		virtual bool operator==(const AdventureSpellCast & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -26,23 +26,23 @@ bool BuildBoat::operator==(const BuildBoat & other) const | ||||
| 	return shipyard->o->id == other.shipyard->o->id; | ||||
| } | ||||
|  | ||||
| TSubgoal BuildBoat::whatToDoToAchieve() | ||||
| { | ||||
| 	if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) | ||||
| 	{ | ||||
| 		return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o)); | ||||
| 	} | ||||
|  | ||||
| 	if(shipyard->shipyardStatus() != IShipyard::GOOD) | ||||
| 	{ | ||||
| 		throw cannotFulfillGoalException("Shipyard is busy."); | ||||
| 	} | ||||
|  | ||||
| 	TResources boatCost; | ||||
| 	shipyard->getBoatCost(boatCost); | ||||
|  | ||||
| 	return ai->ah->whatToDo(boatCost, this->iAmElementar()); | ||||
| } | ||||
| //TSubgoal BuildBoat::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) | ||||
| { | ||||
| @@ -79,8 +79,3 @@ std::string BuildBoat::name() const | ||||
| { | ||||
| 	return "BuildBoat"; | ||||
| } | ||||
|  | ||||
| std::string BuildBoat::completeMessage() const | ||||
| { | ||||
| 	return "Boat have been built at " + shipyard->o->visitablePos().toString(); | ||||
| } | ||||
|   | ||||
| @@ -28,10 +28,8 @@ namespace Goals | ||||
| 		{ | ||||
| 			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; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -13,8 +13,6 @@ | ||||
| #include "../AIUtility.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../FuzzyHelper.h" | ||||
| #include "../ResourceManager.h" | ||||
| #include "../BuildingManager.h" | ||||
| #include "../../../lib/mapping/CMap.h" //for victory conditions | ||||
| #include "../../../lib/CPathfinder.h" | ||||
| #include "../../../lib/StringConstants.h" | ||||
| @@ -34,46 +32,4 @@ bool BuildThis::operator==(const BuildThis & other) const | ||||
| std::string BuildThis::name() const | ||||
| { | ||||
| 	return "Build " + buildingInfo.name + "(" + std::to_string(bid) + ") in " + town->name; | ||||
| } | ||||
|  | ||||
| TSubgoal BuildThis::whatToDoToAchieve() | ||||
| { | ||||
| 	auto b = BuildingID(bid); | ||||
|  | ||||
| 	// find town if not set | ||||
| 	if(!town && hero) | ||||
| 		town = hero->visitedTown; | ||||
|  | ||||
| 	if(!town) | ||||
| 	{ | ||||
| 		for(const CGTownInstance * t : cb->getTownsInfo()) | ||||
| 		{ | ||||
| 			switch(cb->canBuildStructure(town, b)) | ||||
| 			{ | ||||
| 			case EBuildingState::ALLOWED: | ||||
| 				town = t; | ||||
| 				break; //TODO: look for prerequisites? this is not our reponsibility | ||||
| 			default: | ||||
| 				continue; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(town) //we have specific town to build this | ||||
| 	{ | ||||
| 		switch(cb->canBuildStructure(town, b)) | ||||
| 		{ | ||||
| 		case EBuildingState::ALLOWED: | ||||
| 		case EBuildingState::NO_RESOURCES: | ||||
| 		{ | ||||
| 			auto res = town->town->buildings.at(BuildingID(bid))->resources; | ||||
| 			return ai->ah->whatToDo(res, iAmElementar()); //realize immediately or gather resources | ||||
| 		} | ||||
| 		break; | ||||
| 		default: | ||||
| 			throw cannotFulfillGoalException("Not possible to build"); | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 		throw cannotFulfillGoalException("Cannot find town to build this"); | ||||
| } | ||||
| } | ||||
| @@ -47,12 +47,6 @@ namespace Goals | ||||
| 			bid = Bid; | ||||
| 			priority = 1; | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		//bool fulfillsMe(TSubgoal goal) override; | ||||
| 		virtual bool operator==(const BuildThis & other) const override; | ||||
| 		virtual std::string name() const override; | ||||
| 	}; | ||||
|   | ||||
| @@ -25,21 +25,7 @@ bool BuyArmy::operator==(const BuyArmy & other) const | ||||
| 	return town == other.town && objid == other.objid; | ||||
| } | ||||
|  | ||||
| bool BuyArmy::fulfillsMe(TSubgoal goal) | ||||
| { | ||||
| 	//if (hero && hero != goal->hero) | ||||
| 	//	return false; | ||||
| 	return town == goal->town && goal->value >= value; //can always buy more army | ||||
| } | ||||
| TSubgoal BuyArmy::whatToDoToAchieve() | ||||
| { | ||||
| 	//TODO: calculate the actual cost of units instead | ||||
| 	TResources price; | ||||
| 	price[Res::GOLD] = value * 0.4f; //some approximate value | ||||
| 	return ai->ah->whatToDo(price, iAmElementar()); //buy right now or gather resources | ||||
| } | ||||
|  | ||||
| std::string BuyArmy::completeMessage() const | ||||
| { | ||||
| 	return boost::format("Bought army of value %d in town of %s") % value, town->name; | ||||
| } | ||||
| 	return iAmElementar(); | ||||
| } | ||||
| @@ -32,10 +32,8 @@ namespace Goals | ||||
| 			value = val; //expressed in AI unit strength | ||||
| 			priority = 3;//TODO: evaluate? | ||||
| 		} | ||||
| 		bool fulfillsMe(TSubgoal goal) override; | ||||
|  | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		std::string completeMessage() const override; | ||||
| 		virtual bool operator==(const BuyArmy & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -51,11 +51,6 @@ namespace Goals | ||||
| 			ai->tryRealize(static_cast<T &>(*this)); //casting enforces template instantiation | ||||
| 		} | ||||
|  | ||||
| 		float accept(FuzzyHelper * f) override | ||||
| 		{ | ||||
| 			return f->evaluate(static_cast<T &>(*this)); //casting enforces template instantiation | ||||
| 		} | ||||
|  | ||||
| 		CGoal<T> * clone() const override | ||||
| 		{ | ||||
| 			return new T(static_cast<T const &>(*this)); //casting enforces template instantiation | ||||
|   | ||||
| @@ -13,8 +13,6 @@ | ||||
| #include "../AIUtility.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../FuzzyHelper.h" | ||||
| #include "../ResourceManager.h" | ||||
| #include "../BuildingManager.h" | ||||
| #include "../../../lib/mapping/CMap.h" //for victory conditions | ||||
| #include "../../../lib/CPathfinder.h" | ||||
| #include "../../../lib/StringConstants.h" | ||||
| @@ -35,80 +33,80 @@ TGoalVec CollectRes::getAllPossibleSubgoals() | ||||
| { | ||||
| 	TGoalVec ret; | ||||
|  | ||||
| 	auto givesResource = [this](const CGObjectInstance * obj) -> bool | ||||
| 	{ | ||||
| 		//TODO: move this logic to object side | ||||
| 		//TODO: remember mithril exists | ||||
| 		//TODO: water objects | ||||
| 		//TODO: Creature banks | ||||
| 	//auto givesResource = [this](const CGObjectInstance * obj) -> bool | ||||
| 	//{ | ||||
| 	//	//TODO: move this logic to object side | ||||
| 	//	//TODO: remember mithril exists | ||||
| 	//	//TODO: water objects | ||||
| 	//	//TODO: Creature banks | ||||
|  | ||||
| 		//return false first from once-visitable, before checking if they were even visited | ||||
| 		switch (obj->ID.num) | ||||
| 		{ | ||||
| 		case Obj::TREASURE_CHEST: | ||||
| 			return resID == Res::GOLD; | ||||
| 			break; | ||||
| 		case Obj::RESOURCE: | ||||
| 			return obj->subID == resID; | ||||
| 			break; | ||||
| 		case Obj::MINE: | ||||
| 			return (obj->subID == resID && | ||||
| 				(cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines | ||||
| 			break; | ||||
| 		case Obj::CAMPFIRE: | ||||
| 			return true; //contains all resources | ||||
| 			break; | ||||
| 		case Obj::WINDMILL: | ||||
| 			switch (resID) | ||||
| 			{ | ||||
| 			case Res::GOLD: | ||||
| 			case Res::WOOD: | ||||
| 				return false; | ||||
| 			} | ||||
| 			break; | ||||
| 		case Obj::WATER_WHEEL: | ||||
| 			if (resID != Res::GOLD) | ||||
| 				return false; | ||||
| 			break; | ||||
| 		case Obj::MYSTICAL_GARDEN: | ||||
| 			if ((resID != Res::GOLD) && (resID != Res::GEMS)) | ||||
| 				return false; | ||||
| 			break; | ||||
| 		case Obj::LEAN_TO: | ||||
| 		case Obj::WAGON: | ||||
| 			if (resID != Res::GOLD) | ||||
| 				return false; | ||||
| 			break; | ||||
| 		default: | ||||
| 			return false; | ||||
| 			break; | ||||
| 		} | ||||
| 		return !vstd::contains(ai->alreadyVisited, obj); //for weekly / once visitable | ||||
| 	}; | ||||
| 	//	//return false first from once-visitable, before checking if they were even visited | ||||
| 	//	switch (obj->ID.num) | ||||
| 	//	{ | ||||
| 	//	case Obj::TREASURE_CHEST: | ||||
| 	//		return resID == Res::GOLD; | ||||
| 	//		break; | ||||
| 	//	case Obj::RESOURCE: | ||||
| 	//		return obj->subID == resID; | ||||
| 	//		break; | ||||
| 	//	case Obj::MINE: | ||||
| 	//		return (obj->subID == resID && | ||||
| 	//			(cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines | ||||
| 	//		break; | ||||
| 	//	case Obj::CAMPFIRE: | ||||
| 	//		return true; //contains all resources | ||||
| 	//		break; | ||||
| 	//	case Obj::WINDMILL: | ||||
| 	//		switch (resID) | ||||
| 	//		{ | ||||
| 	//		case Res::GOLD: | ||||
| 	//		case Res::WOOD: | ||||
| 	//			return false; | ||||
| 	//		} | ||||
| 	//		break; | ||||
| 	//	case Obj::WATER_WHEEL: | ||||
| 	//		if (resID != Res::GOLD) | ||||
| 	//			return false; | ||||
| 	//		break; | ||||
| 	//	case Obj::MYSTICAL_GARDEN: | ||||
| 	//		if ((resID != Res::GOLD) && (resID != Res::GEMS)) | ||||
| 	//			return false; | ||||
| 	//		break; | ||||
| 	//	case Obj::LEAN_TO: | ||||
| 	//	case Obj::WAGON: | ||||
| 	//		if (resID != Res::GOLD) | ||||
| 	//			return false; | ||||
| 	//		break; | ||||
| 	//	default: | ||||
| 	//		return false; | ||||
| 	//		break; | ||||
| 	//	} | ||||
| 	//	return !vstd::contains(ai->alreadyVisited, obj); //for weekly / once visitable | ||||
| 	//}; | ||||
|  | ||||
| 	std::vector<const CGObjectInstance *> objs; | ||||
| 	for (auto obj : ai->visitableObjs) | ||||
| 	{ | ||||
| 		if (givesResource(obj)) | ||||
| 			objs.push_back(obj); | ||||
| 	} | ||||
| 	for (auto h : cb->getHeroesInfo()) | ||||
| 	{ | ||||
| 		std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects | ||||
| 	//std::vector<const CGObjectInstance *> objs; | ||||
| 	//for (auto obj : ai->visitableObjs) | ||||
| 	//{ | ||||
| 	//	if (givesResource(obj)) | ||||
| 	//		objs.push_back(obj); | ||||
| 	//} | ||||
| 	//for (auto h : cb->getHeroesInfo()) | ||||
| 	//{ | ||||
| 	//	std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects | ||||
|  | ||||
| 		for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero | ||||
| 		{ | ||||
| 			if (givesResource(obj)) | ||||
| 				ourObjs.push_back(obj); | ||||
| 		} | ||||
| 	//	for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero | ||||
| 	//	{ | ||||
| 	//		if (givesResource(obj)) | ||||
| 	//			ourObjs.push_back(obj); | ||||
| 	//	} | ||||
|  | ||||
| 		for (auto obj : ourObjs) | ||||
| 		{ | ||||
| 			auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj)); | ||||
| 	//	for (auto obj : ourObjs) | ||||
| 	//	{ | ||||
| 	//		auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj)); | ||||
|  | ||||
| 			vstd::concatenate(ret, waysToGo); | ||||
| 		} | ||||
| 	} | ||||
| 	//		vstd::concatenate(ret, waysToGo); | ||||
| 	//	} | ||||
| 	//} | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| @@ -119,90 +117,78 @@ TSubgoal CollectRes::whatToDoToAchieve() | ||||
| 	if (!trade->invalid()) | ||||
| 		goals.push_back(trade); | ||||
|  | ||||
| 	if (goals.empty()) | ||||
| 		return sptr(Explore()); //we can always do that | ||||
| 	else | ||||
| 		return fh->chooseSolution(goals); //TODO: evaluate trading | ||||
| 	return sptr(Invalid()); //we can always do that | ||||
| } | ||||
|  | ||||
| TSubgoal CollectRes::whatToDoToTrade() | ||||
| { | ||||
| 	std::vector<const IMarket *> markets; | ||||
| 	//std::vector<const IMarket *> markets; | ||||
|  | ||||
| 	std::vector<const CGObjectInstance *> visObjs; | ||||
| 	ai->retrieveVisitableObjs(visObjs, true); | ||||
| 	for (const CGObjectInstance * obj : visObjs) | ||||
| 	{ | ||||
| 		if (const IMarket * m = IMarket::castFrom(obj, false)) | ||||
| 		{ | ||||
| 			if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE)) | ||||
| 				markets.push_back(m); | ||||
| 			else if (obj->ID == Obj::TRADING_POST) | ||||
| 				markets.push_back(m); | ||||
| 		} | ||||
| 	} | ||||
| 	//std::vector<const CGObjectInstance *> visObjs; | ||||
| 	//ai->retrieveVisitableObjs(visObjs, true); | ||||
| 	//for (const CGObjectInstance * obj : visObjs) | ||||
| 	//{ | ||||
| 	//	if (const IMarket * m = IMarket::castFrom(obj, false)) | ||||
| 	//	{ | ||||
| 	//		if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE)) | ||||
| 	//			markets.push_back(m); | ||||
| 	//		else if (obj->ID == Obj::TRADING_POST) | ||||
| 	//			markets.push_back(m); | ||||
| 	//	} | ||||
| 	//} | ||||
|  | ||||
| 	boost::sort(markets, [](const IMarket * m1, const IMarket * m2) -> bool | ||||
| 	{ | ||||
| 		return m1->getMarketEfficiency() < m2->getMarketEfficiency(); | ||||
| 	}); | ||||
| 	//boost::sort(markets, [](const IMarket * m1, const IMarket * m2) -> bool | ||||
| 	//{ | ||||
| 	//	return m1->getMarketEfficiency() < m2->getMarketEfficiency(); | ||||
| 	//}); | ||||
|  | ||||
| 	markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool | ||||
| 	{ | ||||
| 		if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID)) | ||||
| 		{ | ||||
| 			if (!ai->isAccessible(market->o->visitablePos())) | ||||
| 				return true; | ||||
| 		} | ||||
| 		return false; | ||||
| 	}), markets.end()); | ||||
| 	//markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool | ||||
| 	//{ | ||||
| 	//	if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID)) | ||||
| 	//	{ | ||||
| 	//		if (!ai->isAccessible(market->o->visitablePos())) | ||||
| 	//			return true; | ||||
| 	//	} | ||||
| 	//	return false; | ||||
| 	//}), markets.end()); | ||||
|  | ||||
| 	if (!markets.size()) | ||||
| 	{ | ||||
| 		for (const CGTownInstance * t : cb->getTownsInfo()) | ||||
| 		{ | ||||
| 			if (cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED) | ||||
| 				return sptr(BuildThis(BuildingID::MARKETPLACE, t).setpriority(2)); | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		const IMarket * m = markets.back(); | ||||
| 		//attempt trade at back (best prices) | ||||
| 		int howManyCanWeBuy = 0; | ||||
| 		for (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1)) | ||||
| 		{ | ||||
| 			if (i == resID) | ||||
| 				continue; | ||||
| 			int toGive = -1, toReceive = -1; | ||||
| 			m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE); | ||||
| 			assert(toGive > 0 && toReceive > 0); | ||||
| 			howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive); | ||||
| 		} | ||||
| 	//if (!markets.size()) | ||||
| 	//{ | ||||
| 	//	for (const CGTownInstance * t : cb->getTownsInfo()) | ||||
| 	//	{ | ||||
| 	//		if (cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED) | ||||
| 	//			return sptr(BuildThis(BuildingID::MARKETPLACE, t).setpriority(2)); | ||||
| 	//	} | ||||
| 	//} | ||||
| 	//else | ||||
| 	//{ | ||||
| 	//	const IMarket * m = markets.back(); | ||||
| 	//	//attempt trade at back (best prices) | ||||
| 	//	int howManyCanWeBuy = 0; | ||||
| 	//	for (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1)) | ||||
| 	//	{ | ||||
| 	//		if (i == resID) | ||||
| 	//			continue; | ||||
| 	//		int toGive = -1, toReceive = -1; | ||||
| 	//		m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE); | ||||
| 	//		assert(toGive > 0 && toReceive > 0); | ||||
| 	//		howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive); | ||||
| 	//	} | ||||
|  | ||||
| 		if (howManyCanWeBuy >= value) | ||||
| 		{ | ||||
| 			auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace | ||||
| 			assert(backObj); | ||||
| 			auto objid = m->o->id.getNum(); | ||||
| 			if (backObj->tempOwner != ai->playerID) //top object not owned | ||||
| 			{ | ||||
| 				return sptr(VisitObj(objid)); //just go there | ||||
| 			} | ||||
| 			else //either it's our town, or we have hero there | ||||
| 			{ | ||||
| 				return sptr(Trade(resID, value, objid).setisElementar(true)); //we can do this immediately | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	//	if (howManyCanWeBuy >= value) | ||||
| 	//	{ | ||||
| 	//		auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace | ||||
| 	//		assert(backObj); | ||||
| 	//		auto objid = m->o->id.getNum(); | ||||
| 	//		if (backObj->tempOwner != ai->playerID) //top object not owned | ||||
| 	//		{ | ||||
| 	//			return sptr(VisitObj(objid)); //just go there | ||||
| 	//		} | ||||
| 	//		else //either it's our town, or we have hero there | ||||
| 	//		{ | ||||
| 	//			return sptr(Trade(resID, value, objid).setisElementar(true)); //we can do this immediately | ||||
| 	//		} | ||||
| 	//	} | ||||
| 	//} | ||||
| 	return sptr(Invalid()); //cannot trade | ||||
| } | ||||
|  | ||||
| bool CollectRes::fulfillsMe(TSubgoal goal) | ||||
| { | ||||
| 	if (goal->resID == resID) | ||||
| 		if (goal->value >= value) | ||||
| 			return true; | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|   | ||||
| @@ -34,7 +34,6 @@ namespace Goals | ||||
| 		TGoalVec getAllPossibleSubgoals() override; | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		TSubgoal whatToDoToTrade(); | ||||
| 		bool fulfillsMe(TSubgoal goal) override; //TODO: Trade | ||||
| 		virtual bool operator==(const CollectRes & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -82,9 +82,8 @@ TSubgoal CompleteQuest::whatToDoToAchieve() | ||||
|  | ||||
| 	TGoalVec solutions = getAllPossibleSubgoals(); | ||||
|  | ||||
| 	if(solutions.empty()) | ||||
| 		throw cannotFulfillGoalException("Can not complete quest " + questToString()); | ||||
|  | ||||
| 	throw cannotFulfillGoalException("Can not complete quest " + questToString()); | ||||
| /* | ||||
| 	TSubgoal result = fh->chooseSolution(solutions); | ||||
|  | ||||
| 	logAi->trace( | ||||
| @@ -94,7 +93,7 @@ TSubgoal CompleteQuest::whatToDoToAchieve() | ||||
| 		result->objid, | ||||
| 		result->hero.validAndSet() ? result->hero->name : "not specified"); | ||||
|  | ||||
| 	return result; | ||||
| 	return result;*/ | ||||
| } | ||||
|  | ||||
| std::string CompleteQuest::name() const | ||||
| @@ -102,11 +101,6 @@ std::string CompleteQuest::name() const | ||||
| 	return "CompleteQuest"; | ||||
| } | ||||
|  | ||||
| std::string CompleteQuest::completeMessage() const | ||||
| { | ||||
| 	return "Completed quest " + questToString(); | ||||
| } | ||||
|  | ||||
| std::string CompleteQuest::questToString() const | ||||
| { | ||||
| 	if(q.quest->missionType == CQuest::MISSION_NONE) | ||||
| @@ -128,7 +122,7 @@ TGoalVec CompleteQuest::tryCompleteQuest() const | ||||
| 	{ | ||||
| 		if(q.quest->checkQuest(hero)) | ||||
| 		{ | ||||
| 			vstd::concatenate(solutions, ai->ah->howToVisitObj(hero, ObjectIdRef(q.obj->id))); | ||||
| 			//vstd::concatenate(solutions, ai->ah->howToVisitObj(hero, ObjectIdRef(q.obj->id))); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -228,7 +222,7 @@ TGoalVec CompleteQuest::missionResources() const | ||||
| 	{ | ||||
| 		if(q.quest->checkQuest(heroes.front())) //it doesn't matter which hero it is | ||||
| 		{ | ||||
| 			return ai->ah->howToVisitObj(q.obj); | ||||
| 			return solutions;// ai->ah->howToVisitObj(q.obj); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| @@ -254,7 +248,7 @@ TGoalVec CompleteQuest::missionDestroyObj() const | ||||
| 	auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val); | ||||
|  | ||||
| 	if(!obj) | ||||
| 		return ai->ah->howToVisitObj(q.obj); | ||||
| 		return solutions;// ai->ah->howToVisitObj(q.obj); | ||||
|  | ||||
| 	if(obj->ID == Obj::HERO) | ||||
| 	{ | ||||
| @@ -264,11 +258,11 @@ TGoalVec CompleteQuest::missionDestroyObj() const | ||||
| 		{ | ||||
| 			auto heroToProtect = cb->getHero(obj->id); | ||||
|  | ||||
| 			solutions.push_back(sptr(GatherArmy().sethero(heroToProtect))); | ||||
| 			//solutions.push_back(sptr(GatherArmy().sethero(heroToProtect))); | ||||
| 		} | ||||
| 		else if(relations == PlayerRelations::ENEMIES) | ||||
| 		{ | ||||
| 			solutions = ai->ah->howToVisitObj(obj); | ||||
| 			//solutions = ai->ah->howToVisitObj(obj); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -28,7 +28,6 @@ namespace Goals | ||||
| 		TGoalVec getAllPossibleSubgoals() override; | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		std::string name() const override; | ||||
| 		std::string completeMessage() const override; | ||||
| 		virtual bool operator==(const CompleteQuest & other) const override; | ||||
|  | ||||
| 	private: | ||||
|   | ||||
| @@ -48,8 +48,3 @@ std::string DismissHero::name() const | ||||
| { | ||||
| 	return "DismissHero " + hero.name; | ||||
| } | ||||
|  | ||||
| std::string DismissHero::completeMessage() const | ||||
| { | ||||
| 	return "Hero dismissed successfully " + hero.name; | ||||
| } | ||||
|   | ||||
| @@ -30,7 +30,6 @@ namespace Goals | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		void accept(VCAI * ai) override; | ||||
| 		std::string name() const override; | ||||
| 		std::string completeMessage() const override; | ||||
| 		virtual bool operator==(const DismissHero & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -36,11 +36,6 @@ std::string ExchangeSwapTownHeroes::name() const | ||||
| 	return "Exchange and swap heroes of " + town->name; | ||||
| } | ||||
|  | ||||
| std::string ExchangeSwapTownHeroes::completeMessage() const | ||||
| { | ||||
| 	return "Exchange and swap heroes of " + town->name + " compelete"; | ||||
| } | ||||
|  | ||||
| bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) const | ||||
| { | ||||
| 	return town == other.town; | ||||
|   | ||||
| @@ -35,7 +35,6 @@ namespace Goals | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		void accept(VCAI * ai) override; | ||||
| 		std::string name() const override; | ||||
| 		std::string completeMessage() const override; | ||||
| 		virtual bool operator==(const ExchangeSwapTownHeroes & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -72,27 +72,29 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 	{ | ||||
| 		auto & node = chainPath.nodes[i]; | ||||
|  | ||||
| 		HeroPtr hero = node.targetHero; | ||||
| 		const CGHeroInstance * hero = node.targetHero; | ||||
| 		HeroPtr heroPtr = hero; | ||||
|  | ||||
| 		if(vstd::contains(blockedIndexes, i)) | ||||
| 		{ | ||||
| 			blockedIndexes.insert(node.parentIndex); | ||||
| 			ai->nullkiller->lockHero(hero.get(), HeroLockedReason::HERO_CHAIN); | ||||
| 			ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN); | ||||
|  | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		logAi->debug("Executing chain node %d. Moving hero %s to %s", i, hero.name, node.coord.toString()); | ||||
| 		logAi->debug("Executing chain node %d. Moving hero %s to %s", i, hero->name, node.coord.toString()); | ||||
|  | ||||
| 		try | ||||
| 		{ | ||||
| 			if(hero->movement) | ||||
| 			{ | ||||
| 				ai->nullkiller->setActive(hero.get(), node.coord); | ||||
|  | ||||
| 				ai->nullkiller->setActive(hero, node.coord); | ||||
|  | ||||
| 				if(node.specialAction) | ||||
| 				{ | ||||
| 					if(node.specialAction->canAct(hero.get())) | ||||
| 					if(node.specialAction->canAct(hero)) | ||||
| 					{ | ||||
| 						auto specialGoal = node.specialAction->whatToDo(hero); | ||||
|  | ||||
| @@ -100,12 +102,12 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						//TODO: decompose | ||||
| 						throw cannotFulfillGoalException("Path is nondeterministic."); | ||||
| 					} | ||||
|  | ||||
| 					if(!hero.validAndSet()) | ||||
| 					 | ||||
| 					if(!heroPtr.validAndSet()) | ||||
| 					{ | ||||
| 						logAi->error("Hero %s was lost trying to execute special action. Exit hero chain.", hero.name); | ||||
| 						logAi->error("Hero %s was lost trying to execute special action. Exit hero chain.", heroPtr.name); | ||||
|  | ||||
| 						return; | ||||
| 					} | ||||
| @@ -113,7 +115,7 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
|  | ||||
| 				if(node.turns == 0 && node.coord != hero->visitablePos()) | ||||
| 				{ | ||||
| 					auto targetNode = cb->getPathsInfo(hero.get())->getPathInfo(node.coord); | ||||
| 					auto targetNode = cb->getPathsInfo(hero)->getPathInfo(node.coord); | ||||
|  | ||||
| 					if(targetNode->accessible == CGPathNode::EAccessibility::NOT_SET | ||||
| 						|| targetNode->accessible == CGPathNode::EAccessibility::BLOCKED | ||||
| @@ -122,7 +124,7 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 					{ | ||||
| 						logAi->error( | ||||
| 							"Enable to complete chain. Expected hero %s to arive to %s in 0 turns but he can not do this", | ||||
| 							hero.name, | ||||
| 							hero->name, | ||||
| 							node.coord.toString()); | ||||
|  | ||||
| 						return; | ||||
| @@ -137,9 +139,9 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 					} | ||||
| 					catch(cannotFulfillGoalException) | ||||
| 					{ | ||||
| 						if(!hero.validAndSet()) | ||||
| 						if(!heroPtr.validAndSet()) | ||||
| 						{ | ||||
| 							logAi->error("Hero %s was lost. Exit hero chain.", hero.name); | ||||
| 							logAi->error("Hero %s was lost. Exit hero chain.", heroPtr.name); | ||||
|  | ||||
| 							return; | ||||
| 						} | ||||
| @@ -147,13 +149,13 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 						if(hero->movement > 0) | ||||
| 						{ | ||||
| 							CGPath path; | ||||
| 							bool isOk = cb->getPathsInfo(hero.get())->getPath(path, node.coord); | ||||
| 							bool isOk = cb->getPathsInfo(hero)->getPath(path, node.coord); | ||||
|  | ||||
| 							if(isOk && path.nodes.back().turns > 0) | ||||
| 							{ | ||||
| 								logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero.name, hero->movement, node.coord.toString()); | ||||
| 								logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero->name, hero->movement, node.coord.toString()); | ||||
|  | ||||
| 								ai->nullkiller->lockHero(hero.get(), HeroLockedReason::HERO_CHAIN); | ||||
| 								ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN); | ||||
| 								return; | ||||
| 							} | ||||
| 						} | ||||
| @@ -170,7 +172,7 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 			{ | ||||
| 				logAi->error( | ||||
| 					"Enable to complete chain. Expected hero %s to arive to %s but he is at %s",  | ||||
| 					hero.name,  | ||||
| 					hero->name,  | ||||
| 					node.coord.toString(), | ||||
| 					hero->visitablePos().toString()); | ||||
|  | ||||
| @@ -178,14 +180,14 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 			} | ||||
| 			 | ||||
| 			// no exception means we were not able to rich the tile | ||||
| 			ai->nullkiller->lockHero(hero.get(), HeroLockedReason::HERO_CHAIN); | ||||
| 			ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN); | ||||
| 			blockedIndexes.insert(node.parentIndex); | ||||
| 		} | ||||
| 		catch(goalFulfilledException) | ||||
| 		{ | ||||
| 			if(!hero) | ||||
| 			if(!heroPtr.validAndSet()) | ||||
| 			{ | ||||
| 				logAi->debug("Hero %s was killed while attempting to rich %s", hero.name, node.coord.toString()); | ||||
| 				logAi->debug("Hero %s was killed while attempting to rich %s", heroPtr.name, node.coord.toString()); | ||||
|  | ||||
| 				return; | ||||
| 			} | ||||
| @@ -196,9 +198,4 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| std::string ExecuteHeroChain::name() const | ||||
| { | ||||
| 	return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->name; | ||||
| } | ||||
|  | ||||
| std::string ExecuteHeroChain::completeMessage() const | ||||
| { | ||||
| 	return "Hero chain completed"; | ||||
| } | ||||
| @@ -30,7 +30,6 @@ namespace Goals | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		void accept(VCAI * ai) override; | ||||
| 		std::string name() const override; | ||||
| 		std::string completeMessage() const override; | ||||
| 		virtual bool operator==(const ExecuteHeroChain & other) const override; | ||||
| 		const AIPath & getPath() const { return chainPath; } | ||||
| 	}; | ||||
|   | ||||
| @@ -233,14 +233,9 @@ bool Explore::operator==(const Explore & other) const | ||||
| 	return other.hero.h == hero.h && other.allowGatherArmy == allowGatherArmy; | ||||
| } | ||||
|  | ||||
| std::string Explore::completeMessage() const | ||||
| { | ||||
| 	return "Hero " + hero.get()->name + " completed exploration"; | ||||
| } | ||||
|  | ||||
| TSubgoal Explore::whatToDoToAchieve() | ||||
| { | ||||
| 	return fh->chooseSolution(getAllPossibleSubgoals()); | ||||
| 	return sptr(Goals::Invalid()); | ||||
| } | ||||
|  | ||||
| TGoalVec Explore::getAllPossibleSubgoals() | ||||
| @@ -361,18 +356,6 @@ TGoalVec Explore::getAllPossibleSubgoals() | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| bool Explore::fulfillsMe(TSubgoal goal) | ||||
| { | ||||
| 	if(goal->goalType == EXPLORE) | ||||
| 	{ | ||||
| 		if(goal->hero) | ||||
| 			return hero == goal->hero; | ||||
| 		else | ||||
| 			return true; //cancel ALL exploration | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| TSubgoal Explore::explorationBestNeighbour(int3 hpos, HeroPtr h) const | ||||
| { | ||||
| 	ExplorationHelper scanResult(h, allowGatherArmy); | ||||
|   | ||||
| @@ -1,66 +1,64 @@ | ||||
| /* | ||||
| * Explore.h, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| #include "CGoal.h" | ||||
|  | ||||
| struct HeroPtr; | ||||
| class VCAI; | ||||
| class FuzzyHelper; | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	struct ExplorationHelper; | ||||
|  | ||||
| 	class DLL_EXPORT Explore : public CGoal<Explore> | ||||
| 	{ | ||||
| 	private: | ||||
| 		bool allowGatherArmy; | ||||
|  | ||||
| 	public: | ||||
| 		Explore(bool allowGatherArmy) | ||||
| 			: CGoal(Goals::EXPLORE), allowGatherArmy(allowGatherArmy) | ||||
| 		{ | ||||
| 			priority = 1; | ||||
| 		} | ||||
|  | ||||
| 		Explore() | ||||
| 			: Explore(true) | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 		Explore(HeroPtr h) | ||||
| 			: CGoal(Goals::EXPLORE) | ||||
| 		{ | ||||
| 			hero = h; | ||||
| 			priority = 1; | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override; | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		std::string completeMessage() const override; | ||||
| 		bool fulfillsMe(TSubgoal goal) override; | ||||
| 		virtual bool operator==(const Explore & other) const override; | ||||
|  | ||||
| 	private: | ||||
| 		TSubgoal exploreNearestNeighbour(HeroPtr h) const; | ||||
| 		TSubgoal explorationNewPoint(HeroPtr h) const; | ||||
| 		TSubgoal explorationBestNeighbour(int3 hpos, HeroPtr h) const; | ||||
| 		void explorationScanTile(const int3 & tile, ExplorationHelper & scanResult) const; | ||||
| 		bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const; | ||||
|  | ||||
| 		void getVisibleNeighbours( | ||||
| 			const std::vector<int3> & tiles, | ||||
| 			std::vector<int3> & out, | ||||
| 			CCallback * cbp, | ||||
| 			const TeamState * ts) const; | ||||
|  | ||||
| 		int howManyTilesWillBeDiscovered(const int3 & pos, ExplorationHelper & scanResult) const; | ||||
| 	}; | ||||
| } | ||||
| ///* | ||||
| //* Explore.h, part of VCMI engine | ||||
| //* | ||||
| //* Authors: listed in file AUTHORS in main folder | ||||
| //* | ||||
| //* License: GNU General Public License v2.0 or later | ||||
| //* Full text of license available in license.txt file, in main folder | ||||
| //* | ||||
| //*/ | ||||
| //#pragma once | ||||
| // | ||||
| //#include "CGoal.h" | ||||
| // | ||||
| //struct HeroPtr; | ||||
| //class VCAI; | ||||
| //class FuzzyHelper; | ||||
| // | ||||
| //namespace Goals | ||||
| //{ | ||||
| //	struct ExplorationHelper; | ||||
| // | ||||
| //	class DLL_EXPORT Explore : public CGoal<Explore> | ||||
| //	{ | ||||
| //	private: | ||||
| //		bool allowGatherArmy; | ||||
| // | ||||
| //	public: | ||||
| //		Explore(bool allowGatherArmy) | ||||
| //			: CGoal(Goals::EXPLORE), allowGatherArmy(allowGatherArmy) | ||||
| //		{ | ||||
| //			priority = 1; | ||||
| //		} | ||||
| // | ||||
| //		Explore() | ||||
| //			: Explore(true) | ||||
| //		{ | ||||
| //		} | ||||
| // | ||||
| //		Explore(HeroPtr h) | ||||
| //			: CGoal(Goals::EXPLORE) | ||||
| //		{ | ||||
| //			hero = h; | ||||
| //			priority = 1; | ||||
| //		} | ||||
| //		TGoalVec getAllPossibleSubgoals() override; | ||||
| //		TSubgoal whatToDoToAchieve() override; | ||||
| //		virtual bool operator==(const Explore & other) const override; | ||||
| // | ||||
| //	private: | ||||
| //		TSubgoal exploreNearestNeighbour(HeroPtr h) const; | ||||
| //		TSubgoal explorationNewPoint(HeroPtr h) const; | ||||
| //		TSubgoal explorationBestNeighbour(int3 hpos, HeroPtr h) const; | ||||
| //		void explorationScanTile(const int3 & tile, ExplorationHelper & scanResult) const; | ||||
| //		bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const; | ||||
| // | ||||
| //		void getVisibleNeighbours( | ||||
| //			const std::vector<int3> & tiles, | ||||
| //			std::vector<int3> & out, | ||||
| //			CCallback * cbp, | ||||
| //			const TeamState * ts) const; | ||||
| // | ||||
| //		int howManyTilesWillBeDiscovered(const int3 & pos, ExplorationHelper & scanResult) const; | ||||
| //	}; | ||||
| //} | ||||
|   | ||||
| @@ -10,7 +10,6 @@ | ||||
| #include "StdInc.h" | ||||
| #include "FindObj.h" | ||||
| #include "VisitObj.h" | ||||
| #include "Explore.h" | ||||
| #include "../VCAI.h" | ||||
| #include "../AIUtility.h" | ||||
|  | ||||
| @@ -52,19 +51,4 @@ TSubgoal FindObj::whatToDoToAchieve() | ||||
| 	} | ||||
| 	if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is | ||||
| 		return sptr(VisitObj(o->id.getNum())); | ||||
| 	else | ||||
| 		return sptr(Explore()); | ||||
| } | ||||
|  | ||||
| bool FindObj::fulfillsMe(TSubgoal goal) | ||||
| { | ||||
| 	if (goal->goalType == VISIT_TILE) //visiting tile visits object at same time | ||||
| 	{ | ||||
| 		if (!hero || hero == goal->hero) | ||||
| 			for (auto obj : cb->getVisitableObjs(goal->tile)) //check if any object on that tile matches criteria | ||||
| 				if (obj->visitablePos() == goal->tile) //object could be removed | ||||
| 					if (obj->ID == objid && obj->subID == resID) //same type and subtype | ||||
| 						return true; | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| } | ||||
| @@ -41,7 +41,6 @@ namespace Goals | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		bool fulfillsMe(TSubgoal goal) override; | ||||
| 		virtual bool operator==(const FindObj & other) const override; | ||||
| 	}; | ||||
| } | ||||
| @@ -13,8 +13,6 @@ | ||||
| #include "../AIUtility.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../FuzzyHelper.h" | ||||
| #include "../ResourceManager.h" | ||||
| #include "../BuildingManager.h" | ||||
| #include "../../../lib/mapping/CMap.h" //for victory conditions | ||||
| #include "../../../lib/CPathfinder.h" | ||||
| #include "../../../lib/StringConstants.h" | ||||
| @@ -62,12 +60,7 @@ TSubgoal GatherTroops::whatToDoToAchieve() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	TGoalVec solutions = getAllPossibleSubgoals(); | ||||
|  | ||||
| 	if(solutions.empty()) | ||||
| 		return sptr(Explore()); | ||||
|  | ||||
| 	return fh->chooseSolution(solutions); | ||||
| 	return sptr(Invalid()); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -75,75 +68,74 @@ TGoalVec GatherTroops::getAllPossibleSubgoals() | ||||
| { | ||||
| 	TGoalVec solutions; | ||||
|  | ||||
| 	for(const CGTownInstance * t : cb->getTownsInfo()) | ||||
| 	{ | ||||
| 		int count = getCreaturesCount(t->getUpperArmy()); | ||||
| 	//for(const CGTownInstance * t : cb->getTownsInfo()) | ||||
| 	//{ | ||||
| 	//	int count = getCreaturesCount(t->getUpperArmy()); | ||||
|  | ||||
| 		if(count >= this->value) | ||||
| 		{ | ||||
| 			if(t->visitingHero) | ||||
| 			{ | ||||
| 				solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->visitingHero.get()))); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				vstd::concatenate(solutions, ai->ah->howToVisitObj(t)); | ||||
| 			} | ||||
| 	//	if(count >= this->value) | ||||
| 	//	{ | ||||
| 	//		if(t->visitingHero) | ||||
| 	//		{ | ||||
| 	//			solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->visitingHero.get()))); | ||||
| 	//		} | ||||
| 	//		else | ||||
| 	//		{ | ||||
| 	//			vstd::concatenate(solutions, ai->ah->howToVisitObj(t)); | ||||
| 	//		} | ||||
|  | ||||
| 			continue; | ||||
| 		} | ||||
| 	//		continue; | ||||
| 	//	} | ||||
|  | ||||
| 		auto creature = VLC->creh->creatures[objid]; | ||||
| 		if(t->subID == creature->faction) //TODO: how to force AI to build unupgraded creatures? :O | ||||
| 		{ | ||||
| 			auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1); | ||||
| 			if(!creatures) | ||||
| 				continue; | ||||
| 	//	auto creature = VLC->creh->creatures[objid]; | ||||
| 	//	if(t->subID == creature->faction) //TODO: how to force AI to build unupgraded creatures? :O | ||||
| 	//	{ | ||||
| 	//		auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1); | ||||
| 	//		if(!creatures) | ||||
| 	//			continue; | ||||
|  | ||||
| 			int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber); | ||||
| 			if(upgradeNumber < 0) | ||||
| 				continue; | ||||
| 	//		int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber); | ||||
| 	//		if(upgradeNumber < 0) | ||||
| 	//			continue; | ||||
|  | ||||
| 			BuildingID bid(BuildingID::DWELL_FIRST + creature->level - 1 + upgradeNumber * GameConstants::CREATURES_PER_TOWN); | ||||
| 			if(t->hasBuilt(bid) && ai->ah->freeResources().canAfford(creature->cost)) //this assumes only creatures with dwellings are assigned to faction | ||||
| 			{ | ||||
| 				solutions.push_back(sptr(BuyArmy(t, creature->AIValue * this->value).setobjid(objid))); | ||||
| 			} | ||||
| 			/*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm | ||||
| 			{	 | ||||
| 				return sptr(BuildThis(bid, t).setpriority(priority)); | ||||
| 			}*/ | ||||
| 		} | ||||
| 	} | ||||
| 	for(auto obj : ai->visitableObjs) | ||||
| 	{ | ||||
| 		auto d = dynamic_cast<const CGDwelling *>(obj); | ||||
| 	//		BuildingID bid(BuildingID::DWELL_FIRST + creature->level - 1 + upgradeNumber * GameConstants::CREATURES_PER_TOWN); | ||||
| 	//		if(t->hasBuilt(bid) && ai->ah->freeResources().canAfford(creature->cost)) //this assumes only creatures with dwellings are assigned to faction | ||||
| 	//		{ | ||||
| 	//			solutions.push_back(sptr(BuyArmy(t, creature->AIValue * this->value).setobjid(objid))); | ||||
| 	//		} | ||||
| 	//		/*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm | ||||
| 	//		{	 | ||||
| 	//			return sptr(BuildThis(bid, t).setpriority(priority)); | ||||
| 	//		}*/ | ||||
| 	//	} | ||||
| 	//} | ||||
|  | ||||
| 		if(!d || obj->ID == Obj::TOWN) | ||||
| 			continue; | ||||
| 	//for(auto obj : ai->visitableObjs) | ||||
| 	//{ | ||||
| 	//	auto d = dynamic_cast<const CGDwelling *>(obj); | ||||
|  | ||||
| 		for(auto creature : d->creatures) | ||||
| 		{ | ||||
| 			if(creature.first) //there are more than 0 creatures avaliabe | ||||
| 			{ | ||||
| 				for(auto type : creature.second) | ||||
| 				{ | ||||
| 					if(type == objid && ai->ah->freeResources().canAfford(VLC->creh->creatures[type]->cost)) | ||||
| 						vstd::concatenate(solutions, ai->ah->howToVisitObj(obj)); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	//	if(!d || obj->ID == Obj::TOWN) | ||||
| 	//		continue; | ||||
|  | ||||
| 	//	for(auto creature : d->creatures) | ||||
| 	//	{ | ||||
| 	//		if(creature.first) //there are more than 0 creatures avaliabe | ||||
| 	//		{ | ||||
| 	//			for(auto type : creature.second) | ||||
| 	//			{ | ||||
| 	//				if(type == objid && ai->ah->freeResources().canAfford(VLC->creh->creatures[type]->cost)) | ||||
| 	//					vstd::concatenate(solutions, ai->ah->howToVisitObj(obj)); | ||||
| 	//			} | ||||
| 	//		} | ||||
| 	//	} | ||||
| 	//} | ||||
|  | ||||
| 	//CreatureID creID = CreatureID(objid); | ||||
|  | ||||
| 	//vstd::erase_if(solutions, [&](TSubgoal goal)->bool | ||||
| 	//{ | ||||
| 	//	return goal->hero && !goal->hero->getSlotFor(creID).validSlot() && !goal->hero->getFreeSlot().validSlot(); | ||||
| 	//}); | ||||
|  | ||||
| 	return solutions; | ||||
| 	//TODO: exchange troops between heroes | ||||
| } | ||||
|  | ||||
| bool GatherTroops::fulfillsMe(TSubgoal goal) | ||||
| { | ||||
| 	if (!hero || hero == goal->hero) //we got army for desired hero or any hero | ||||
| 		if (goal->objid == objid) //same creature type //TODO: consider upgrades? | ||||
| 			if (goal->value >= value) //notify every time we get resources? | ||||
| 				return true; | ||||
| 	return false; | ||||
| } | ||||
|   | ||||
| @@ -34,7 +34,6 @@ namespace Goals | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override; | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		bool fulfillsMe(TSubgoal goal) override; | ||||
| 		virtual bool operator==(const GatherTroops & other) const override; | ||||
|  | ||||
| 	private: | ||||
|   | ||||
| @@ -12,22 +12,17 @@ | ||||
| #include "CGoal.h" | ||||
| #include "Invalid.h" | ||||
| #include "BuildBoat.h" | ||||
| #include "Build.h" | ||||
| #include "BuildThis.h" | ||||
| #include "Conquer.h" | ||||
| #include "GatherArmy.h" | ||||
| #include "Win.h" | ||||
| #include "VisitObj.h" | ||||
| #include "VisitTile.h" | ||||
| #include "VisitHero.h" | ||||
| #include "Explore.h" | ||||
| #include "BuyArmy.h" | ||||
| #include "GatherTroops.h" | ||||
| #include "Trade.h" | ||||
| #include "CollectRes.h" | ||||
| #include "RecruitHero.h" | ||||
| #include "GetArtOfType.h" | ||||
| #include "ClearWayTo.h" | ||||
| #include "DigAtTile.h" | ||||
| #include "FindObj.h" | ||||
| #include "CompleteQuest.h" | ||||
|   | ||||
| @@ -13,8 +13,6 @@ | ||||
| #include "../AIUtility.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../FuzzyHelper.h" | ||||
| #include "../ResourceManager.h" | ||||
| #include "../BuildingManager.h" | ||||
| #include "../../../lib/mapping/CMap.h" //for victory conditions | ||||
| #include "../../../lib/CPathfinder.h" | ||||
| #include "../../../lib/StringConstants.h" | ||||
| @@ -25,14 +23,3 @@ extern boost::thread_specific_ptr<VCAI> ai; | ||||
| extern FuzzyHelper * fh; | ||||
|  | ||||
| using namespace Goals; | ||||
|  | ||||
| TSubgoal RecruitHero::whatToDoToAchieve() | ||||
| { | ||||
| 	const CGTownInstance * t = ai->findTownWithTavern(); | ||||
| 	if(!t) | ||||
| 		return sptr(BuildThis(BuildingID::TAVERN).setpriority(2)); | ||||
|  | ||||
| 	TResources res; | ||||
| 	res[Res::GOLD] = GameConstants::HERO_GOLD_COST; | ||||
| 	return ai->ah->whatToDo(res, iAmElementar()); //either buy immediately, or collect res | ||||
| } | ||||
|   | ||||
| @@ -26,13 +26,6 @@ namespace Goals | ||||
| 			priority = 1; | ||||
| 		} | ||||
|  | ||||
| 		TGoalVec getAllPossibleSubgoals() override | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
|  | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
|  | ||||
| 		virtual bool operator==(const RecruitHero & other) const override | ||||
| 		{ | ||||
| 			return true; | ||||
|   | ||||
| @@ -9,14 +9,11 @@ | ||||
| */ | ||||
| #include "StdInc.h" | ||||
| #include "VisitHero.h" | ||||
| #include "Explore.h" | ||||
| #include "Invalid.h" | ||||
| #include "../VCAI.h" | ||||
| #include "../AIUtility.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../FuzzyHelper.h" | ||||
| #include "../ResourceManager.h" | ||||
| #include "../BuildingManager.h" | ||||
| #include "../../../lib/mapping/CMap.h" //for victory conditions | ||||
|  | ||||
|  | ||||
| @@ -29,46 +26,4 @@ using namespace Goals; | ||||
| bool VisitHero::operator==(const VisitHero & other) const | ||||
| { | ||||
| 	return other.hero.h == hero.h && other.objid == objid; | ||||
| } | ||||
|  | ||||
| std::string VisitHero::completeMessage() const | ||||
| { | ||||
| 	return "hero " + hero.get()->name + " visited hero " + boost::lexical_cast<std::string>(objid); | ||||
| } | ||||
|  | ||||
| TSubgoal VisitHero::whatToDoToAchieve() | ||||
| { | ||||
| 	const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid)); | ||||
| 	if(!obj) | ||||
| 		return sptr(Explore()); | ||||
| 	int3 pos = obj->visitablePos(); | ||||
|  | ||||
| 	if(hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements | ||||
| 	{ | ||||
| 		if(hero->visitablePos() == pos) | ||||
| 			logAi->error("Hero %s tries to visit himself.", hero.name); | ||||
| 		else | ||||
| 		{ | ||||
| 			//can't use VISIT_TILE here as tile appears blocked by target hero | ||||
| 			//FIXME: elementar goal should not be abstract | ||||
| 			return sptr(VisitHero(objid).sethero(hero).settile(pos).setisElementar(true)); | ||||
| 		} | ||||
| 	} | ||||
| 	return sptr(Invalid()); | ||||
| } | ||||
|  | ||||
| bool VisitHero::fulfillsMe(TSubgoal goal) | ||||
| { | ||||
| 	//TODO: VisitObj shoudl not be used for heroes, but... | ||||
| 	if(goal->goalType == VISIT_TILE) | ||||
| 	{ | ||||
| 		auto obj = cb->getObj(ObjectInstanceID(objid)); | ||||
| 		if (!obj) | ||||
| 		{ | ||||
| 			logAi->error("Hero %s: VisitHero::fulfillsMe at %s: object %d not found", hero.name, goal->tile.toString(), objid); | ||||
| 			return false; | ||||
| 		} | ||||
| 		return obj->visitablePos() == goal->tile; | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| } | ||||
| @@ -30,13 +30,6 @@ namespace Goals | ||||
| 			objid = hid; | ||||
| 			priority = 4; | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override | ||||
| 		{ | ||||
| 			return TGoalVec(); | ||||
| 		} | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		bool fulfillsMe(TSubgoal goal) override; | ||||
| 		std::string completeMessage() const override; | ||||
| 		virtual bool operator==(const VisitHero & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -13,8 +13,6 @@ | ||||
| #include "../AIUtility.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../FuzzyHelper.h" | ||||
| #include "../ResourceManager.h" | ||||
| #include "../BuildingManager.h" | ||||
| #include "../../../lib/mapping/CMap.h" //for victory conditions | ||||
| #include "../../../lib/CPathfinder.h" | ||||
| #include "../../../lib/StringConstants.h" | ||||
| @@ -31,65 +29,6 @@ bool VisitObj::operator==(const VisitObj & other) const | ||||
| 	return other.hero.h == hero.h && other.objid == objid; | ||||
| } | ||||
|  | ||||
| std::string VisitObj::completeMessage() const | ||||
| { | ||||
| 	return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid); | ||||
| } | ||||
|  | ||||
| TGoalVec VisitObj::getAllPossibleSubgoals() | ||||
| { | ||||
| 	TGoalVec goalList; | ||||
| 	const CGObjectInstance * obj = cb->getObjInstance(ObjectInstanceID(objid)); | ||||
| 	if(!obj) | ||||
| 	{ | ||||
| 		throw cannotFulfillGoalException("Object is missing - goal is invalid now!"); | ||||
| 	} | ||||
|  | ||||
| 	int3 pos = obj->visitablePos(); | ||||
| 	if(hero) | ||||
| 	{ | ||||
| 		if(ai->isAccessibleForHero(pos, hero)) | ||||
| 		{ | ||||
| 			if(isSafeToVisit(hero, pos)) | ||||
| 				goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(hero))); | ||||
| 			else | ||||
| 				goalList.push_back(sptr(GatherArmy(fh->evaluateDanger(pos, hero.h) * SAFE_ATTACK_CONSTANT).sethero(hero).setisAbstract(true))); | ||||
|  | ||||
| 			return goalList; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		for(auto potentialVisitor : cb->getHeroesInfo()) | ||||
| 		{ | ||||
| 			if(ai->isAccessibleForHero(pos, potentialVisitor)) | ||||
| 			{ | ||||
| 				if(isSafeToVisit(potentialVisitor, pos)) | ||||
| 					goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(potentialVisitor))); | ||||
| 				else | ||||
| 					goalList.push_back(sptr(GatherArmy(fh->evaluateDanger(pos, potentialVisitor) * SAFE_ATTACK_CONSTANT).sethero(potentialVisitor).setisAbstract(true))); | ||||
| 			} | ||||
| 		} | ||||
| 		if(!goalList.empty()) | ||||
| 		{ | ||||
| 			return goalList; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	goalList.push_back(sptr(ClearWayTo(pos))); | ||||
| 	return goalList; | ||||
| } | ||||
|  | ||||
| TSubgoal VisitObj::whatToDoToAchieve() | ||||
| { | ||||
| 	auto bestGoal = fh->chooseSolution(getAllPossibleSubgoals()); | ||||
|  | ||||
| 	if(bestGoal->goalType == VISIT_OBJ && bestGoal->hero) | ||||
| 		bestGoal->setisElementar(true); | ||||
|  | ||||
| 	return bestGoal; | ||||
| } | ||||
|  | ||||
| VisitObj::VisitObj(int Objid) | ||||
| 	: CGoal(VISIT_OBJ) | ||||
| { | ||||
| @@ -101,18 +40,4 @@ VisitObj::VisitObj(int Objid) | ||||
| 		logAi->error("VisitObj constructed with invalid object instance %d", Objid); | ||||
|  | ||||
| 	priority = 3; | ||||
| } | ||||
|  | ||||
| bool VisitObj::fulfillsMe(TSubgoal goal) | ||||
| { | ||||
| 	if(goal->goalType == VISIT_TILE) | ||||
| 	{ | ||||
| 		if (!hero || hero == goal->hero) | ||||
| 		{ | ||||
| 			auto obj = cb->getObjInstance(ObjectInstanceID(objid)); | ||||
| 			if (obj && obj->visitablePos() == goal->tile) //object could be removed | ||||
| 				return true; | ||||
| 		} | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| } | ||||
| @@ -23,10 +23,6 @@ namespace Goals | ||||
| 		VisitObj() = delete; // empty constructor not allowed | ||||
| 		VisitObj(int Objid); | ||||
|  | ||||
| 		TGoalVec getAllPossibleSubgoals() override; | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		bool fulfillsMe(TSubgoal goal) override; | ||||
| 		std::string completeMessage() const override; | ||||
| 		virtual bool operator==(const VisitObj & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -13,8 +13,6 @@ | ||||
| #include "../AIUtility.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../FuzzyHelper.h" | ||||
| #include "../ResourceManager.h" | ||||
| #include "../BuildingManager.h" | ||||
| #include "../../../lib/mapping/CMap.h" //for victory conditions | ||||
| #include "../../../lib/CPathfinder.h" | ||||
| #include "../../../lib/StringConstants.h" | ||||
| @@ -30,69 +28,3 @@ bool VisitTile::operator==(const VisitTile & other) const | ||||
| { | ||||
| 	return other.hero.h == hero.h && other.tile == tile; | ||||
| } | ||||
|  | ||||
| std::string VisitTile::completeMessage() const | ||||
| { | ||||
| 	return "Hero " + hero.get()->name + " visited tile " + tile.toString(); | ||||
| } | ||||
|  | ||||
| TSubgoal VisitTile::whatToDoToAchieve() | ||||
| { | ||||
| 	auto ret = fh->chooseSolution(getAllPossibleSubgoals()); | ||||
|  | ||||
| 	if(ret->hero) | ||||
| 	{ | ||||
| 		if(isSafeToVisit(ret->hero, tile) && ai->isAccessibleForHero(tile, ret->hero)) | ||||
| 		{ | ||||
| 			ret->setisElementar(true); | ||||
| 			return ret; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			return sptr(GatherArmy(fh->evaluateDanger(tile, *ret->hero) * SAFE_ATTACK_CONSTANT) | ||||
| 				    .sethero(ret->hero).setisAbstract(true)); | ||||
| 		} | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| TGoalVec VisitTile::getAllPossibleSubgoals() | ||||
| { | ||||
| 	assert(cb->isInTheMap(tile)); | ||||
|  | ||||
| 	TGoalVec ret; | ||||
| 	if(!cb->isVisible(tile)) | ||||
| 		ret.push_back(sptr(Explore())); //what sense does it make? | ||||
| 	else | ||||
| 	{ | ||||
| 		std::vector<const CGHeroInstance *> heroes; | ||||
| 		if(hero) | ||||
| 			heroes.push_back(hero.h); //use assigned hero if any | ||||
| 		else | ||||
| 			heroes = cb->getHeroesInfo(); //use most convenient hero | ||||
|  | ||||
| 		for(auto h : heroes) | ||||
| 		{ | ||||
| 			if(ai->isAccessibleForHero(tile, h)) | ||||
| 				ret.push_back(sptr(VisitTile(tile).sethero(h))); | ||||
| 		} | ||||
| 		if(ai->canRecruitAnyHero()) | ||||
| 			ret.push_back(sptr(RecruitHero())); | ||||
| 	} | ||||
| 	if(ret.empty()) | ||||
| 	{ | ||||
| 		auto obj = vstd::frontOrNull(cb->getVisitableObjs(tile)); | ||||
| 		if(obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile | ||||
| 		{ | ||||
| 			if(hero.get(true) && hero->id == obj->id) //if it's assigned hero, visit tile. If it's different hero, we can't visit tile now | ||||
| 				ret.push_back(sptr(VisitTile(tile).sethero(dynamic_cast<const CGHeroInstance *>(obj)).setisElementar(true))); | ||||
| 			else | ||||
| 				throw cannotFulfillGoalException("Tile is already occupied by another hero "); //FIXME: we should give up this tile earlier | ||||
| 		} | ||||
| 		else | ||||
| 			ret.push_back(sptr(ClearWayTo(tile))); | ||||
| 	} | ||||
|  | ||||
| 	//important - at least one sub-goal must handle case which is impossible to fulfill (unreachable tile) | ||||
| 	return ret; | ||||
| } | ||||
|   | ||||
| @@ -29,9 +29,6 @@ namespace Goals | ||||
| 			tile = Tile; | ||||
| 			priority = 5; | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override; | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		std::string completeMessage() const override; | ||||
| 		virtual bool operator==(const VisitTile & other) const override; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -13,8 +13,6 @@ | ||||
| #include "../AIUtility.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../FuzzyHelper.h" | ||||
| #include "../ResourceManager.h" | ||||
| #include "../BuildingManager.h" | ||||
| #include "../../../lib/mapping/CMap.h" //for victory conditions | ||||
| #include "../../../lib/CPathfinder.h" | ||||
| #include "../../../lib/StringConstants.h" | ||||
| @@ -62,9 +60,9 @@ TSubgoal Win::whatToDoToAchieve() | ||||
| 			if(goal.object) | ||||
| 			{ | ||||
| 				auto obj = cb->getObj(goal.object->id); | ||||
| 				if(obj) | ||||
| 				/*if(obj) | ||||
| 					if(obj->getOwner() == ai->playerID) //we can't capture our own object | ||||
| 						return sptr(Conquer()); | ||||
| 						return sptr(Conquer());*/ | ||||
|  | ||||
|  | ||||
| 				return sptr(VisitObj(goal.object->id.getNum())); | ||||
| @@ -122,8 +120,8 @@ TSubgoal Win::whatToDoToAchieve() | ||||
| 				} //TODO: use FIND_OBJ | ||||
| 				else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks | ||||
| 					return sptr(VisitObj(obj->id.getNum())); | ||||
| 				else | ||||
| 					return sptr(Explore()); | ||||
| 				/*else | ||||
| 					return sptr(Explore());*/ | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| @@ -166,7 +164,7 @@ TSubgoal Win::whatToDoToAchieve() | ||||
| 			break; | ||||
| 		} | ||||
| 		case EventCondition::STANDARD_WIN: | ||||
| 			return sptr(Conquer()); | ||||
| 			return sptr(Invalid()); | ||||
|  | ||||
| 		// Conditions that likely don't need any implementation | ||||
| 		case EventCondition::DAYS_PASSED: | ||||
| @@ -182,7 +180,7 @@ TSubgoal Win::whatToDoToAchieve() | ||||
| 		case EventCondition::HAVE_BUILDING_0: | ||||
| 		case EventCondition::DESTROY_0: | ||||
| 			//TODO: support new condition format | ||||
| 			return sptr(Conquer()); | ||||
| 			return sptr(Invalid()); | ||||
| 		default: | ||||
| 			assert(0); | ||||
| 		} | ||||
|   | ||||
| @@ -64,7 +64,12 @@ boost::optional<int> MapObjectsEvaluator::getObjectValue(const CGObjectInstance | ||||
| 		auto hero = dynamic_cast<const CGHeroInstance*>(obj); | ||||
| 		return getObjectValue(obj->ID, hero->type->heroClass->id); | ||||
| 	} | ||||
| 	if(obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4) | ||||
| 	else if(obj->ID == Obj::PRISON) | ||||
| 	{ | ||||
| 		//special case: in-game prison subID is captured hero ID, but config has one subID with index 0 for normal prison - use that one | ||||
| 		return getObjectValue(obj->ID, 0); | ||||
| 	} | ||||
| 	else if(obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4) | ||||
| 	{ | ||||
| 		auto dwelling = dynamic_cast<const CGDwelling *>(obj); | ||||
| 		int aiValue = 0; | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|  | ||||
| namespace AIPathfinding | ||||
| { | ||||
| 	Goals::TSubgoal BattleAction::whatToDo(const HeroPtr & hero) const | ||||
| 	Goals::TSubgoal BattleAction::whatToDo(const CGHeroInstance * hero) const | ||||
| 	{ | ||||
| 		return Goals::sptr(Goals::VisitTile(targetTile).sethero(hero)); | ||||
| 	} | ||||
|   | ||||
| @@ -25,6 +25,6 @@ namespace AIPathfinding | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 		virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override; | ||||
| 		virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override; | ||||
| 	}; | ||||
| } | ||||
| @@ -17,7 +17,7 @@ | ||||
|  | ||||
| namespace AIPathfinding | ||||
| { | ||||
| 	Goals::TSubgoal BuildBoatAction::whatToDo(const HeroPtr & hero) const | ||||
| 	Goals::TSubgoal BuildBoatAction::whatToDo(const CGHeroInstance * hero) const | ||||
| 	{ | ||||
| 		return Goals::sptr(Goals::BuildBoat(shipyard)); | ||||
| 	} | ||||
| @@ -27,7 +27,7 @@ namespace AIPathfinding | ||||
| 		return sourceActor->resourceActor; | ||||
| 	} | ||||
|  | ||||
| 	Goals::TSubgoal SummonBoatAction::whatToDo(const HeroPtr & hero) const | ||||
| 	Goals::TSubgoal SummonBoatAction::whatToDo(const CGHeroInstance * hero) const | ||||
| 	{ | ||||
| 		return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT)); | ||||
| 	} | ||||
|   | ||||
| @@ -25,7 +25,7 @@ namespace AIPathfinding | ||||
| 	class SummonBoatAction : public VirtualBoatAction | ||||
| 	{ | ||||
| 	public: | ||||
| 		virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override; | ||||
| 		virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override; | ||||
|  | ||||
| 		virtual void applyOnDestination( | ||||
| 			const CGHeroInstance * hero, | ||||
| @@ -53,7 +53,7 @@ namespace AIPathfinding | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 		virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override; | ||||
| 		virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override; | ||||
|  | ||||
| 		virtual const ChainActor * getActor(const ChainActor * sourceActor) const override; | ||||
| 	}; | ||||
|   | ||||
| @@ -23,7 +23,7 @@ public: | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const = 0; | ||||
| 	virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const = 0; | ||||
|  | ||||
| 	virtual void applyOnDestination( | ||||
| 		const CGHeroInstance * hero, | ||||
|   | ||||
| @@ -16,7 +16,7 @@ | ||||
|  | ||||
| using namespace AIPathfinding; | ||||
|  | ||||
| Goals::TSubgoal TownPortalAction::whatToDo(const HeroPtr & hero) const | ||||
| Goals::TSubgoal TownPortalAction::whatToDo(const CGHeroInstance * hero) const | ||||
| { | ||||
| 	const CGTownInstance * targetTown = target; // const pointer is not allowed in settown | ||||
|  | ||||
|   | ||||
| @@ -28,6 +28,6 @@ namespace AIPathfinding | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 		virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override; | ||||
| 		virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override; | ||||
| 	}; | ||||
| } | ||||
| @@ -32,76 +32,6 @@ void PathfindingManager::setAI(VCAI * AI) | ||||
| 	ai = AI; | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec PathfindingManager::howToVisitTile(const int3 & tile, bool allowGatherArmy) const | ||||
| { | ||||
| 	Goals::TGoalVec result; | ||||
|  | ||||
| 	auto heroes = cb->getHeroesInfo(); | ||||
| 	result.reserve(heroes.size()); | ||||
|  | ||||
| 	for(auto hero : heroes) | ||||
| 	{ | ||||
| 		vstd::concatenate(result, howToVisitTile(hero, tile, allowGatherArmy)); | ||||
| 	} | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec PathfindingManager::howToVisitObj(ObjectIdRef obj, bool allowGatherArmy) const | ||||
| { | ||||
| 	Goals::TGoalVec result; | ||||
|  | ||||
| 	auto heroes = cb->getHeroesInfo(); | ||||
| 	result.reserve(heroes.size()); | ||||
|  | ||||
| 	for(auto hero : heroes) | ||||
| 	{ | ||||
| 		vstd::concatenate(result, howToVisitObj(hero, obj, allowGatherArmy)); | ||||
| 	} | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec PathfindingManager::howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy) const | ||||
| { | ||||
| 	auto result = findPaths(tile, allowGatherArmy, hero, [&](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(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy) const | ||||
| { | ||||
| 	if(!obj) | ||||
| 	{ | ||||
| 		return Goals::TGoalVec(); | ||||
| 	} | ||||
|  | ||||
| 	int3 dest = obj->visitablePos(); | ||||
|  | ||||
| 	auto result = findPaths(dest, allowGatherArmy, hero, [&](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(const HeroPtr & hero, const int3 & tile) const | ||||
| { | ||||
| 	auto paths = pathfinder->getPathInfo(tile); | ||||
| @@ -118,152 +48,6 @@ std::vector<AIPath> PathfindingManager::getPathsToTile(const int3 & tile) const | ||||
| 	return pathfinder->getPathInfo(tile); | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec PathfindingManager::findPaths( | ||||
| 	crint3 dest, | ||||
| 	bool allowGatherArmy, | ||||
| 	HeroPtr hero, | ||||
| 	const std::function<Goals::TSubgoal(int3)> doVisitTile) const | ||||
| { | ||||
| 	Goals::TGoalVec result; | ||||
| 	boost::optional<uint64_t> armyValueRequired; | ||||
| 	uint64_t danger; | ||||
|  | ||||
| 	std::vector<AIPath> chainInfo = pathfinder->getPathInfo(dest); | ||||
|  | ||||
| #ifdef VCMI_TRACE_PATHFINDER | ||||
| 	logAi->trace("Trying to find a way for %s to visit tile %s", hero->name, dest.toString()); | ||||
| #endif | ||||
|  | ||||
| 	for(auto path : chainInfo) | ||||
| 	{ | ||||
| 		if((hero && hero.get() != path.targetHero) || path.nodes.empty()) | ||||
| 			continue; | ||||
|  | ||||
| 		const AIPathNodeInfo & firstNode = path.firstNode(); | ||||
| 		int3 firstTileToGet = firstNode.coord; | ||||
|  | ||||
| #ifdef VCMI_TRACE_PATHFINDER | ||||
| 		std::stringstream str; | ||||
|  | ||||
| 		str << "Path found "; | ||||
|  | ||||
| 		for(auto node : path.nodes) | ||||
| 			str << node.targetHero->name << "->" << node.coord.toString() << "; "; | ||||
|  | ||||
| 		logAi->trace(str.str()); | ||||
| #endif | ||||
| 		if(ai->isTileNotReserved(hero.get(), firstTileToGet)) | ||||
| 		{ | ||||
| 			danger = path.getTotalDanger(); | ||||
|  | ||||
| 			if(isSafeToVisit(hero, path.heroArmy, danger)) | ||||
| 			{ | ||||
| 				Goals::TSubgoal solution; | ||||
|  | ||||
| 				if(path.specialAction) | ||||
| 				{ | ||||
| 					solution = path.specialAction->whatToDo(hero); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					solution = dest == firstTileToGet | ||||
| 						? doVisitTile(firstTileToGet) | ||||
| 						: clearWayTo(firstNode.targetHero, firstTileToGet); | ||||
| 				} | ||||
|  | ||||
| 				if(solution->invalid()) | ||||
| 					continue; | ||||
|  | ||||
| 				if(solution->evaluationContext.danger < danger) | ||||
| 					solution->evaluationContext.danger = danger; | ||||
|  | ||||
| 				solution->evaluationContext.movementCost += path.movementCost(); | ||||
| 				solution->evaluationContext.armyLoss += path.getTotalArmyLoss(); | ||||
| 				solution->evaluationContext.heroStrength = path.getHeroStrength(); | ||||
| #ifdef VCMI_TRACE_PATHFINDER | ||||
| 				logAi->trace("It's safe for %s to visit tile %s with danger %s, loss %s, army strength %s, goal %s",  | ||||
| 					hero->name,  | ||||
| 					dest.toString(),  | ||||
| 					std::to_string(danger), | ||||
| 					std::to_string(path.armyLoss), | ||||
| 					std::to_string(path.heroArmy->getArmyStrength()), | ||||
| 					solution->name()); | ||||
| #endif | ||||
| 				result.push_back(solution); | ||||
|  | ||||
| 				continue; | ||||
| 			} | ||||
|  | ||||
| 			if(!armyValueRequired || armyValueRequired > danger) | ||||
| 			{ | ||||
| 				armyValueRequired = boost::make_optional(danger); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	danger = armyValueRequired.get_value_or(0); | ||||
|  | ||||
| 	if(allowGatherArmy && danger > 0) | ||||
| 	{ | ||||
| 		//we need to get army in order to conquer that place | ||||
| #ifdef VCMI_TRACE_PATHFINDER | ||||
| 		logAi->trace("Gather army for %s, value=%s", hero->name, std::to_string(danger)); | ||||
| #endif | ||||
| 		result.push_back(sptr(Goals::GatherArmy(danger * SAFE_ATTACK_CONSTANT).sethero(hero).setisAbstract(true))); | ||||
| 	} | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet) const | ||||
| { | ||||
| 	if(isBlockedBorderGate(firstTileToGet)) | ||||
| 	{ | ||||
| 		//FIXME: this way we'll not visit gate and activate quest :? | ||||
| 		return sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTile(firstTileToGet)->visitableObjects.back()->subID)); | ||||
| 	} | ||||
|  | ||||
| 	auto topObj = cb->getTopObj(firstTileToGet); | ||||
| 	if(topObj) | ||||
| 	{ | ||||
|  | ||||
| 		if(vstd::contains(ai->reservedObjs, topObj) && !vstd::contains(ai->reservedHeroesMap[hero], topObj)) | ||||
| 		{ | ||||
| 			return sptr(Goals::Invalid()); | ||||
| 		} | ||||
|  | ||||
| 		if(topObj->ID == Obj::HERO && cb->getPlayerRelations(hero->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES) | ||||
| 		{ | ||||
| 			if(topObj != hero.get(true)) //the hero we want to free | ||||
| 			{ | ||||
| 				logAi->warn("%s stands in the way of %s", topObj->getObjectName(), hero->getObjectName()); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if(topObj->ID == Obj::QUEST_GUARD || topObj->ID == Obj::BORDERGUARD) | ||||
| 		{ | ||||
| 			if(shouldVisit(hero, topObj)) | ||||
| 			{ | ||||
| 				//do NOT use VISIT_TILE, as tile with quets guard can't be visited | ||||
| 				return sptr(Goals::VisitObj(topObj->id.getNum()).sethero(hero)); | ||||
| 			} | ||||
|  | ||||
| 			auto questObj = dynamic_cast<const IQuestObject*>(topObj); | ||||
|  | ||||
| 			if(questObj) | ||||
| 			{ | ||||
| 				auto questInfo = QuestInfo(questObj->quest, topObj, topObj->visitablePos()); | ||||
|  | ||||
| 				return sptr(Goals::CompleteQuest(questInfo)); | ||||
| 			} | ||||
|  | ||||
| 			return sptr(Goals::Invalid()); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true)); | ||||
| } | ||||
|  | ||||
| void PathfindingManager::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain) | ||||
| { | ||||
| 	logAi->debug("AIPathfinder has been reseted."); | ||||
|   | ||||
| @@ -21,10 +21,6 @@ public: | ||||
| 	virtual void setAI(VCAI * AI) = 0; | ||||
|  | ||||
| 	virtual void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) = 0; | ||||
| 	virtual Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const = 0; | ||||
| 	virtual Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const = 0; | ||||
| 	virtual Goals::TGoalVec howToVisitTile(const int3 & tile, bool allowGatherArmy = true) const = 0; | ||||
| 	virtual Goals::TGoalVec howToVisitObj(ObjectIdRef obj, bool allowGatherArmy = true) const = 0; | ||||
| 	virtual std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const = 0; | ||||
| 	virtual std::vector<AIPath> getPathsToTile(const int3 & tile) const = 0; | ||||
| }; | ||||
| @@ -42,10 +38,6 @@ public: | ||||
| 	PathfindingManager() = default; | ||||
| 	PathfindingManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only | ||||
|  | ||||
| 	Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const override; | ||||
| 	Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const override; | ||||
| 	Goals::TGoalVec howToVisitTile(const int3 & tile, bool allowGatherArmy = true) const override; | ||||
| 	Goals::TGoalVec howToVisitObj(ObjectIdRef obj, bool allowGatherArmy = true) const override; | ||||
| 	std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override; | ||||
| 	std::vector<AIPath> getPathsToTile(const int3 & tile) const override; | ||||
| 	void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override; | ||||
|   | ||||
| @@ -26,7 +26,7 @@ namespace AIPathfinding | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override | ||||
| 		virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override | ||||
| 		{ | ||||
| 			return Goals::sptr(Goals::Invalid()); | ||||
| 		} | ||||
|   | ||||
| @@ -1,364 +0,0 @@ | ||||
| /* | ||||
| * ResourceManager.cpp, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
| #include "StdInc.h" | ||||
| #include "ResourceManager.h" | ||||
| #include "Goals/Goals.h" | ||||
|  | ||||
| #include "../../CCallback.h" | ||||
| #include "../../lib/mapObjects/MapObjects.h" | ||||
|  | ||||
| #define GOLD_RESERVE (10000); //at least we'll be able to reach capitol | ||||
|  | ||||
| ResourceObjective::ResourceObjective(const TResources & Res, Goals::TSubgoal Goal) | ||||
| 	: resources(Res), goal(Goal) | ||||
| { | ||||
| } | ||||
|  | ||||
| bool ResourceObjective::operator<(const ResourceObjective & ro) const | ||||
| { | ||||
| 	return goal->priority < ro.goal->priority; | ||||
| } | ||||
|  | ||||
| ResourceManager::ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI) | ||||
| 	: ai(AI), cb(CB) | ||||
| { | ||||
| } | ||||
|  | ||||
| void ResourceManager::init(CPlayerSpecificInfoCallback * CB) | ||||
| { | ||||
| 	cb = CB; | ||||
| } | ||||
|  | ||||
| void ResourceManager::setAI(VCAI * AI) | ||||
| { | ||||
| 	ai = AI; | ||||
| } | ||||
|  | ||||
| bool ResourceManager::canAfford(const TResources & cost) const | ||||
| { | ||||
| 	return freeResources().canAfford(cost); | ||||
| } | ||||
|  | ||||
| TResources ResourceManager::estimateIncome() const | ||||
| { | ||||
| 	TResources ret; | ||||
| 	for (const CGTownInstance * t : cb->getTownsInfo()) | ||||
| 	{ | ||||
| 		ret += t->dailyIncome(); | ||||
| 	} | ||||
|  | ||||
| 	for (const CGObjectInstance * obj : ai->getFlaggedObjects()) | ||||
| 	{ | ||||
| 		if (obj->ID == Obj::MINE) | ||||
| 		{ | ||||
| 			switch (obj->subID) | ||||
| 			{ | ||||
| 			case Res::WOOD: | ||||
| 			case Res::ORE: | ||||
| 				ret[obj->subID] += WOOD_ORE_MINE_PRODUCTION; | ||||
| 				break; | ||||
| 			case Res::GOLD: | ||||
| 			case 7: //abandoned mine -> also gold | ||||
| 				ret[Res::GOLD] += GOLD_MINE_PRODUCTION; | ||||
| 				break; | ||||
| 			default: | ||||
| 				ret[obj->subID] += RESOURCE_MINE_PRODUCTION; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| void ResourceManager::reserveResoures(const TResources & res, Goals::TSubgoal goal) | ||||
| { | ||||
| 	if (!goal->invalid()) | ||||
| 		tryPush(ResourceObjective(res, goal)); | ||||
| 	else | ||||
| 		logAi->warn("Attempt to reserve resources for Invalid goal"); | ||||
| } | ||||
|  | ||||
| Goals::TSubgoal ResourceManager::collectResourcesForOurGoal(ResourceObjective &o) const | ||||
| { | ||||
| 	auto allResources = cb->getResourceAmount(); | ||||
| 	auto income = estimateIncome(); | ||||
| 	Res::ERes resourceType = Res::INVALID; | ||||
| 	TResource amountToCollect = 0; | ||||
|  | ||||
| 	typedef std::pair<Res::ERes, TResource> resPair; | ||||
| 	std::map<Res::ERes, TResource> missingResources; | ||||
|  | ||||
| 	//TODO: unit test for complex resource sets | ||||
|  | ||||
| 	//sum missing resources of given type for ALL reserved objectives | ||||
| 	for (auto it = queue.ordered_begin(); it != queue.ordered_end(); it++) | ||||
| 	{ | ||||
| 		//choose specific resources we need for this goal (not 0) | ||||
| 		for (auto r = Res::ResourceSet::nziterator(o.resources); r.valid(); r++) | ||||
| 			missingResources[r->resType] += it->resources[r->resType]; //goal it costs r units of resType | ||||
| 	} | ||||
| 	for (auto it = Res::ResourceSet::nziterator(o.resources); it.valid(); it++) | ||||
| 	{ | ||||
| 		missingResources[it->resType] -= allResources[it->resType]; //missing = (what we need) - (what we have) | ||||
| 		vstd::amax(missingResources[it->resType], 0); // if we have more resources than reserved, we don't need them | ||||
| 	} | ||||
| 	vstd::erase_if(missingResources, [=](const resPair & p) -> bool | ||||
| 	{ | ||||
| 		return !(p.second); //in case evaluated to 0 or less | ||||
| 	}); | ||||
| 	if (missingResources.empty()) //FIXME: should be unit-tested out | ||||
| 	{ | ||||
| 		logAi->error("We don't need to collect resources %s for goal %s", o.resources.toString(), o.goal->name()); | ||||
| 		return o.goal; | ||||
| 	} | ||||
|  | ||||
| 	float goalPriority = 10; //arbitrary, will be divided | ||||
| 	for (const resPair & p : missingResources) | ||||
| 	{ | ||||
| 		if (!income[p.first]) //prioritize resources with 0 income | ||||
| 		{ | ||||
| 			resourceType = p.first; | ||||
| 			amountToCollect = p.second; | ||||
| 			goalPriority /= amountToCollect; //need more resources -> lower priority | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	if (resourceType == Res::INVALID) //no needed resources has 0 income, | ||||
| 	{ | ||||
| 		//find the one which takes longest to collect | ||||
| 		typedef std::pair<Res::ERes, float> timePair; | ||||
| 		std::map<Res::ERes, float> daysToEarn; | ||||
| 		for (auto it : missingResources) | ||||
| 			daysToEarn[it.first] = (float)missingResources[it.first] / income[it.first]; | ||||
| 		auto incomeComparer = [&income](const timePair & lhs, const timePair & rhs) -> bool | ||||
| 		{ | ||||
| 			//theoretically income can be negative, but that falls into this comparison | ||||
| 			return lhs.second < rhs.second; | ||||
| 		}; | ||||
|  | ||||
| 		resourceType = boost::max_element(daysToEarn, incomeComparer)->first; | ||||
| 		amountToCollect = missingResources[resourceType]; | ||||
| 		goalPriority /= daysToEarn[resourceType]; //more days - lower priority | ||||
| 	} | ||||
| 	if (resourceType == Res::GOLD) | ||||
| 		goalPriority *= 1000; | ||||
|  | ||||
| 	//this is abstract goal and might take soem time to complete | ||||
| 	return Goals::sptr(Goals::CollectRes(resourceType, amountToCollect).setisAbstract(true)); | ||||
| } | ||||
|  | ||||
| Goals::TSubgoal ResourceManager::whatToDo() const //suggest any goal | ||||
| { | ||||
| 	if (queue.size()) | ||||
| 	{ | ||||
| 		auto o = queue.top(); | ||||
|  | ||||
| 		auto allResources = cb->getResourceAmount(); //we don't consider savings, it's out top-priority goal | ||||
| 		if (allResources.canAfford(o.resources)) | ||||
| 			return o.goal; | ||||
| 		else //we can't afford even top-priority goal, need to collect resources | ||||
| 			return collectResourcesForOurGoal(o); | ||||
| 	} | ||||
| 	else | ||||
| 		return Goals::sptr(Goals::Invalid()); //nothing else to do | ||||
| } | ||||
|  | ||||
| Goals::TSubgoal ResourceManager::whatToDo(TResources &res, Goals::TSubgoal goal) | ||||
| { | ||||
| 	logAi->trace("ResourceManager: checking goal %s which requires resources %s", goal->name(), res.toString()); | ||||
|  | ||||
| 	TResources accumulatedResources; | ||||
| 	auto allResources = cb->getResourceAmount(); | ||||
|  | ||||
| 	ResourceObjective ro(res, goal); | ||||
| 	tryPush(ro); | ||||
| 	//check if we can afford all the objectives with higher priority first | ||||
| 	for (auto it = queue.ordered_begin(); it != queue.ordered_end(); it++) | ||||
| 	{ | ||||
| 		accumulatedResources += it->resources; | ||||
|  | ||||
| 		logAi->trace( | ||||
| 			"ResourceManager: checking goal %s, accumulatedResources=%s, available=%s", | ||||
| 			it->goal->name(), | ||||
| 			accumulatedResources.toString(), | ||||
| 			allResources.toString()); | ||||
|  | ||||
| 		if(!accumulatedResources.canBeAfforded(allResources)) | ||||
| 		{ | ||||
| 			//can't afford | ||||
| 			break; | ||||
| 		} | ||||
| 		else //can afford all goals up to this point | ||||
| 		{ | ||||
| 			if(it->goal == goal) | ||||
| 			{ | ||||
| 				logAi->debug("ResourceManager: can afford goal %s", goal->name()); | ||||
| 				return goal; //can afford immediately | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	logAi->debug("ResourceManager: can not afford goal %s", goal->name()); | ||||
|  | ||||
| 	return collectResourcesForOurGoal(ro); | ||||
| } | ||||
|  | ||||
| bool ResourceManager::containsObjective(Goals::TSubgoal goal) const | ||||
| { | ||||
| 	logAi->trace("Entering ResourceManager.containsObjective goal=%s", goal->name()); | ||||
| 	dumpToLog(); | ||||
|  | ||||
| 	//TODO: unit tests for once | ||||
| 	for (auto objective : queue) | ||||
| 	{ | ||||
| 		if (objective.goal == goal) | ||||
| 			return true; | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool ResourceManager::notifyGoalCompleted(Goals::TSubgoal goal) | ||||
| { | ||||
| 	logAi->trace("Entering ResourceManager.notifyGoalCompleted goal=%s", goal->name()); | ||||
|  | ||||
| 	if (goal->invalid()) | ||||
| 		logAi->warn("Attempt to complete Invalid goal"); | ||||
|  | ||||
| 	std::function<bool(const Goals::TSubgoal &)> equivalentGoalsCheck = [goal](const Goals::TSubgoal & x) -> bool | ||||
| 	{ | ||||
| 		return x == goal || x->fulfillsMe(goal); | ||||
| 	}; | ||||
|  | ||||
| 	bool removedGoal = removeOutdatedObjectives(equivalentGoalsCheck); | ||||
|  | ||||
| 	dumpToLog(); | ||||
|  | ||||
| 	return removedGoal; | ||||
| } | ||||
|  | ||||
| bool ResourceManager::updateGoal(Goals::TSubgoal goal) | ||||
| { | ||||
| 	//we update priority of goal if it is easier or more difficult to complete | ||||
| 	if (goal->invalid()) | ||||
| 		logAi->warn("Attempt to update Invalid goal"); | ||||
|  | ||||
| 	auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool | ||||
| 	{ | ||||
| 		return ro.goal == goal; | ||||
| 	}); | ||||
| 	if (it != queue.end()) | ||||
| 	{ | ||||
| 		it->goal->setpriority(goal->priority); | ||||
| 		auto handle = queue.s_handle_from_iterator(it); | ||||
| 		queue.update(handle); //restore order | ||||
| 		return true; | ||||
| 	} | ||||
| 	else | ||||
| 		return false; | ||||
| } | ||||
|  | ||||
| void ResourceManager::dumpToLog() const | ||||
| { | ||||
| 	for(auto it = queue.ordered_begin(); it != queue.ordered_end(); it++) | ||||
| 	{ | ||||
| 		logAi->trace("ResourceManager contains goal %s which requires resources %s", it->goal->name(), it->resources.toString()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool ResourceManager::tryPush(const ResourceObjective & o) | ||||
| { | ||||
| 	auto goal = o.goal; | ||||
|  | ||||
| 	logAi->trace("ResourceManager: Trying to add goal %s which requires resources %s", goal->name(), o.resources.toString()); | ||||
| 	dumpToLog(); | ||||
|  | ||||
| 	auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool | ||||
| 	{ | ||||
| 		return ro.goal == goal; | ||||
| 	}); | ||||
| 	if (it != queue.end()) | ||||
| 	{ | ||||
| 		auto handle = queue.s_handle_from_iterator(it); | ||||
| 		vstd::amax(goal->priority, it->goal->priority); //increase priority if case | ||||
| 		//update resources with new value | ||||
| 		queue.update(handle, ResourceObjective(o.resources, goal)); //restore order | ||||
| 		return false; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		queue.push(o); //add new objective | ||||
| 		logAi->debug("Reserved resources (%s) for %s", o.resources.toString(), goal->name()); | ||||
| 		return true; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool ResourceManager::hasTasksLeft() const | ||||
| { | ||||
| 	return !queue.empty(); | ||||
| } | ||||
|  | ||||
| bool ResourceManager::removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) | ||||
| { | ||||
| 	bool removedAnything = false; | ||||
| 	while(true) | ||||
| 	{ //unfortunately we can't use remove_if on heap | ||||
| 		auto it = boost::find_if(queue, [&](const ResourceObjective & ro) -> bool | ||||
| 		{ | ||||
| 			return predicate(ro.goal); | ||||
| 		}); | ||||
|  | ||||
| 		if(it != queue.end()) //removed at least one | ||||
| 		{ | ||||
| 			logAi->debug("Removing goal %s from ResourceManager.", it->goal->name()); | ||||
| 			queue.erase(queue.s_handle_from_iterator(it)); | ||||
| 			removedAnything = true; | ||||
| 		} | ||||
| 		else | ||||
| 		{ //found nothing more to remove | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	return removedAnything; | ||||
| } | ||||
|  | ||||
| TResources ResourceManager::reservedResources() const | ||||
| { | ||||
| 	TResources res; | ||||
| 	for (auto it : queue) //substract the value of reserved goals | ||||
| 		res += it.resources; | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| TResources ResourceManager::freeResources() const | ||||
| { | ||||
| 	TResources myRes = cb->getResourceAmount(); | ||||
| 	myRes -= reservedResources(); //substract the value of reserved goals | ||||
|  | ||||
| 	for (auto & val : myRes) | ||||
| 		vstd::amax(val, 0); //never negative | ||||
|  | ||||
| 	return myRes; | ||||
| } | ||||
|  | ||||
| TResource ResourceManager::freeGold() const | ||||
| { | ||||
| 	return freeResources()[Res::GOLD]; | ||||
| } | ||||
|  | ||||
| TResources ResourceManager::allResources() const | ||||
| { | ||||
| 	return cb->getResourceAmount(); | ||||
| } | ||||
|  | ||||
| TResource ResourceManager::allGold() const | ||||
| { | ||||
| 	return cb->getResourceAmount()[Res::GOLD]; | ||||
| } | ||||
| @@ -1,113 +0,0 @@ | ||||
| /* | ||||
| * ResourceManager.h, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| #include "AIUtility.h" | ||||
| #include "../../lib/GameConstants.h" | ||||
| #include "../../lib/VCMI_Lib.h" | ||||
| #include "VCAI.h" | ||||
| #include <boost/heap/binomial_heap.hpp> | ||||
|  | ||||
| class AIhelper; | ||||
| class IResourceManager; | ||||
|  | ||||
| struct DLL_EXPORT ResourceObjective | ||||
| { | ||||
| 	ResourceObjective() = default; | ||||
| 	ResourceObjective(const TResources &res, Goals::TSubgoal goal); | ||||
| 	bool operator < (const ResourceObjective &ro) const; | ||||
|  | ||||
| 	TResources resources; //how many resoures do we need | ||||
| 	Goals::TSubgoal goal; //what for (build, gather army etc...) | ||||
|  | ||||
| 	 //TODO: register? | ||||
| 	template<typename Handler> void serializeInternal(Handler & h, const int version) | ||||
| 	{ | ||||
| 		h & resources; | ||||
| 		//h & goal; //FIXME: goal serialization is broken | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class DLL_EXPORT IResourceManager //: public: IAbstractManager | ||||
| { | ||||
| public: | ||||
| 	virtual ~IResourceManager() = default; | ||||
| 	virtual void init(CPlayerSpecificInfoCallback * CB) = 0; | ||||
| 	virtual void setAI(VCAI * AI) = 0; | ||||
|  | ||||
| 	virtual TResources reservedResources() const = 0; | ||||
| 	virtual TResources freeResources() const = 0; | ||||
| 	virtual TResource freeGold() const = 0; | ||||
| 	virtual TResources allResources() const = 0; | ||||
| 	virtual TResource allGold() const = 0; | ||||
|  | ||||
| 	virtual Goals::TSubgoal whatToDo() const = 0;//get highest-priority goal | ||||
| 	virtual Goals::TSubgoal whatToDo(TResources &res, Goals::TSubgoal goal) = 0; | ||||
| 	virtual bool containsObjective(Goals::TSubgoal goal) const = 0; | ||||
| 	virtual bool hasTasksLeft() const = 0; | ||||
| 	virtual bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) = 0; //remove ResourceObjectives from queue if ResourceObjective->goal meets specific criteria | ||||
| 	virtual bool notifyGoalCompleted(Goals::TSubgoal goal) = 0; | ||||
| }; | ||||
|  | ||||
| class DLL_EXPORT ResourceManager : public IResourceManager | ||||
| { | ||||
| 	/*Resource Manager is a smart shopping list for AI to help | ||||
| 	Uses priority queue based on CGoal.priority */ | ||||
| 	friend class VCAI; | ||||
| 	friend class AIhelper; | ||||
| 	friend struct SetGlobalState; | ||||
|  | ||||
| 	CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback | ||||
| 	VCAI * ai; | ||||
|  | ||||
| public: | ||||
| 	ResourceManager() = default; | ||||
| 	ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only | ||||
|  | ||||
| 	bool canAfford(const TResources & cost) const; | ||||
| 	TResources reservedResources() const override; | ||||
| 	TResources freeResources() const override; | ||||
| 	TResource freeGold() const override; | ||||
| 	TResources allResources() const override; | ||||
| 	TResource allGold() const override; | ||||
|  | ||||
| 	Goals::TSubgoal whatToDo() const override; //peek highest-priority goal | ||||
| 	Goals::TSubgoal whatToDo(TResources & res, Goals::TSubgoal goal) override; //can we afford this goal or need to CollectRes? | ||||
| 	bool containsObjective(Goals::TSubgoal goal) const override; | ||||
| 	bool hasTasksLeft() const override; | ||||
| 	bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) override; | ||||
| 	bool notifyGoalCompleted(Goals::TSubgoal goal) override; | ||||
|  | ||||
| protected: //not-const actions only for AI | ||||
| 	virtual void reserveResoures(const TResources & res, Goals::TSubgoal goal = Goals::TSubgoal()); | ||||
| 	virtual bool updateGoal(Goals::TSubgoal goal); //new goal must have same properties but different priority | ||||
| 	virtual bool tryPush(const ResourceObjective &o); | ||||
|  | ||||
| 	//inner processing | ||||
| 	virtual TResources estimateIncome() const; | ||||
| 	virtual Goals::TSubgoal collectResourcesForOurGoal(ResourceObjective &o) const; | ||||
|  | ||||
| 	void init(CPlayerSpecificInfoCallback * CB) override; | ||||
| 	void setAI(VCAI * AI) override; | ||||
|  | ||||
| private: | ||||
| 	TResources saving; | ||||
|  | ||||
| 	boost::heap::binomial_heap<ResourceObjective> queue; | ||||
|  | ||||
| 	void dumpToLog() const; | ||||
|  | ||||
| 	//TODO: register? | ||||
| 	template<typename Handler> void serializeInternal(Handler & h, const int version) | ||||
| 	{ | ||||
| 		h & saving; | ||||
| 		h & queue; | ||||
| 	} | ||||
| }; | ||||
| @@ -76,7 +76,7 @@ | ||||
| 			<Add option="-Wno-sign-compare" /> | ||||
| 			<Add option="-Wno-unused-parameter" /> | ||||
| 			<Add option="-Wno-overloaded-virtual" /> | ||||
| 			<Add option="-DBOOST_THREAD_USE_LIB" /> | ||||
| 			<Add option="-DBOOST_ALL_DYN_LINK" /> | ||||
| 			<Add option="-DBOOST_SYSTEM_NO_DEPRECATED" /> | ||||
| 			<Add option="-D_WIN32_WINNT=0x0501" /> | ||||
| 			<Add option="-D_WIN32" /> | ||||
| @@ -93,6 +93,8 @@ | ||||
| 		<Unit filename="AIUtility.h" /> | ||||
| 		<Unit filename="AIhelper.cpp" /> | ||||
| 		<Unit filename="AIhelper.h" /> | ||||
| 		<Unit filename="ArmyManager.cpp" /> | ||||
| 		<Unit filename="ArmyManager.h" /> | ||||
| 		<Unit filename="BuildingManager.cpp" /> | ||||
| 		<Unit filename="BuildingManager.h" /> | ||||
| 		<Unit filename="FuzzyEngines.cpp" /> | ||||
|   | ||||
| @@ -10,8 +10,6 @@ | ||||
| #include "StdInc.h" | ||||
| #include "VCAI.h" | ||||
| #include "FuzzyHelper.h" | ||||
| #include "ResourceManager.h" | ||||
| #include "BuildingManager.h" | ||||
| #include "Goals/Goals.h" | ||||
|  | ||||
| #include "../../lib/UnlockGuard.h" | ||||
| @@ -119,9 +117,6 @@ void VCAI::heroMoved(const TryMoveHero & details) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		//FIXME: teleports are not correctly visited | ||||
| 		unreserveObject(hero, t1); | ||||
| 		unreserveObject(hero, t2); | ||||
| 	} | ||||
| 	else if(details.result == TryMoveHero::EMBARK && hero) | ||||
| 	{ | ||||
| @@ -246,8 +241,6 @@ void VCAI::heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * vi | ||||
| 	if(start && visitedObj) //we can end visit with null object, anyway | ||||
| 	{ | ||||
| 		markObjectVisited(visitedObj); | ||||
| 		unreserveObject(visitor, visitedObj); | ||||
| 		completeGoal(sptr(Goals::VisitObj(visitedObj->id.getNum()).sethero(visitor))); //we don't need to visit it anymore | ||||
| 		//TODO: what if we visited one-time visitable object that was reserved by another hero (shouldn't, but..) | ||||
| 		if (visitedObj->ID == Obj::HERO) | ||||
| 		{ | ||||
| @@ -350,9 +343,6 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q | ||||
| 				transferFrom2to1(secondHero, firstHero); | ||||
| 		} | ||||
|  | ||||
| 		completeGoal(sptr(Goals::VisitHero(firstHero->id.getNum()))); //TODO: what if we were visited by other hero in the meantime? | ||||
| 		completeGoal(sptr(Goals::VisitHero(secondHero->id.getNum()))); | ||||
|  | ||||
| 		answerQuery(query, 0); | ||||
| 	}); | ||||
| } | ||||
| @@ -399,9 +389,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj) | ||||
| 	vstd::erase_if_present(visitableObjs, obj); | ||||
| 	vstd::erase_if_present(alreadyVisited, obj); | ||||
|  | ||||
| 	for(auto h : cb->getHeroesInfo()) | ||||
| 		unreserveObject(h, obj); | ||||
|  | ||||
| 	std::function<bool(const Goals::TSubgoal &)> checkRemovalValidity = [&](const Goals::TSubgoal & x) -> bool | ||||
| 	{ | ||||
| 		if((x->goalType == Goals::VISIT_OBJ) && (x->objid == obj->id.getNum())) | ||||
| @@ -412,27 +399,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj) | ||||
| 			return false; | ||||
| 	}; | ||||
|  | ||||
| 	//clear VCAI / main loop caches | ||||
| 	vstd::erase_if(lockedHeroes, [&](const std::pair<HeroPtr, Goals::TSubgoal> & x) -> bool | ||||
| 	{ | ||||
| 		return checkRemovalValidity(x.second); | ||||
| 	}); | ||||
|  | ||||
| 	vstd::erase_if(ultimateGoalsFromBasic, [&](const std::pair<Goals::TSubgoal, Goals::TGoalVec> & x) -> bool | ||||
| 	{ | ||||
| 		return checkRemovalValidity(x.first); | ||||
| 	}); | ||||
|  | ||||
| 	vstd::erase_if(basicGoals, checkRemovalValidity); | ||||
| 	vstd::erase_if(goalsToAdd, checkRemovalValidity); | ||||
| 	vstd::erase_if(goalsToRemove, checkRemovalValidity); | ||||
|  | ||||
| 	for(auto goal : ultimateGoalsFromBasic) | ||||
| 		vstd::erase_if(goal.second, checkRemovalValidity); | ||||
|  | ||||
| 	//clear resource manager goal cache | ||||
| 	ah->removeOutdatedObjectives(checkRemovalValidity); | ||||
|  | ||||
| 	//TODO: Find better way to handle hero boat removal | ||||
| 	if(auto hero = dynamic_cast<const CGHeroInstance *>(obj)) | ||||
| 	{ | ||||
| @@ -440,9 +406,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj) | ||||
| 		{ | ||||
| 			vstd::erase_if_present(visitableObjs, hero->boat); | ||||
| 			vstd::erase_if_present(alreadyVisited, hero->boat); | ||||
|  | ||||
| 			for(auto h : cb->getHeroesInfo()) | ||||
| 				unreserveObject(h, hero->boat); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -569,9 +532,6 @@ void VCAI::buildChanged(const CGTownInstance * town, BuildingID buildingID, int | ||||
| { | ||||
| 	LOG_TRACE_PARAMS(logAi, "what '%i'", what); | ||||
| 	NET_EVENT_HANDLER; | ||||
|  | ||||
| 	if(town->getOwner() == playerID && what == 1) //built | ||||
| 		completeGoal(sptr(Goals::BuildThis(buildingID, town))); | ||||
| } | ||||
|  | ||||
| void VCAI::heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) | ||||
| @@ -855,22 +815,7 @@ void VCAI::makeTurn() | ||||
|  | ||||
| 	try | ||||
| 	{ | ||||
| 		if(nullkiller) | ||||
| 		{ | ||||
| 			nullkiller->makeTurn(); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			//it looks messy here, but it's better to have armed heroes before attempting realizing goals | ||||
| 			for(const CGTownInstance * t : cb->getTownsInfo()) | ||||
| 				moveCreaturesToHero(t); | ||||
|  | ||||
| 			mainLoop(); | ||||
|  | ||||
| 			/*Below function is also responsible for hero movement via internal wander function. By design it is separate logic for heroes that have nothing to do. | ||||
| 			Heroes that were not picked by striveToGoal(sptr(Goals::Win())); recently (so they do not have new goals and cannot continue/reevaluate previously locked goals) will do logic in wander().*/ | ||||
| 			performTypicalActions(); | ||||
| 		} | ||||
| 		nullkiller->makeTurn(); | ||||
|  | ||||
| 		//for debug purpose | ||||
| 		for (auto h : cb->getHeroesInfo()) | ||||
| @@ -904,194 +849,6 @@ std::vector<HeroPtr> VCAI::getMyHeroes() const | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| void VCAI::mainLoop() | ||||
| { | ||||
| 	std::vector<Goals::TSubgoal> elementarGoals; //no duplicates allowed (operator ==) | ||||
| 	basicGoals.clear(); | ||||
|  | ||||
| 	validateVisitableObjs(); | ||||
|  | ||||
| 	//get all potential and saved goals | ||||
| 	//TODO: not lose | ||||
| 	basicGoals.push_back(sptr(Goals::Win())); | ||||
| 	for (auto goalPair : lockedHeroes) | ||||
| 	{ | ||||
| 		fh->setPriority(goalPair.second);  //re-evaluate, as heroes moved in the meantime | ||||
| 		basicGoals.push_back(goalPair.second); | ||||
| 	} | ||||
| 	if (ah->hasTasksLeft()) | ||||
| 		basicGoals.push_back(ah->whatToDo()); | ||||
| 	for (auto quest : myCb->getMyQuests()) | ||||
| 	{ | ||||
| 		basicGoals.push_back(sptr(Goals::CompleteQuest(quest))); | ||||
| 	} | ||||
| 	basicGoals.push_back(sptr(Goals::Build())); | ||||
|  | ||||
| 	invalidPathHeroes.clear(); | ||||
|  | ||||
| 	while (basicGoals.size()) | ||||
| 	{ | ||||
| 		vstd::removeDuplicates(basicGoals); //TODO: container which does this automagically without has would be nice | ||||
| 		goalsToAdd.clear(); | ||||
| 		goalsToRemove.clear(); | ||||
| 		elementarGoals.clear(); | ||||
| 		ultimateGoalsFromBasic.clear(); | ||||
| 		 | ||||
| 		ah->updatePaths(getMyHeroes()); | ||||
|  | ||||
| 		logAi->debug("Main loop: decomposing %i basic goals", basicGoals.size()); | ||||
|  | ||||
| 		for (auto basicGoal : basicGoals) | ||||
| 		{ | ||||
| 			logAi->debug("Main loop: decomposing basic goal %s", basicGoal->name()); | ||||
|  | ||||
| 			auto goalToDecompose = basicGoal; | ||||
| 			Goals::TSubgoal elementarGoal = sptr(Goals::Invalid()); | ||||
| 			int maxAbstractGoals = 10; | ||||
| 			while (!elementarGoal->isElementar && maxAbstractGoals) | ||||
| 			{ | ||||
| 				try | ||||
| 				{ | ||||
| 					elementarGoal = decomposeGoal(goalToDecompose); | ||||
| 				} | ||||
| 				catch (goalFulfilledException & e) | ||||
| 				{ | ||||
| 					//it is impossible to continue some goals (like exploration, for example) | ||||
| 					//complete abstract goal for now, but maybe main goal finds another path | ||||
| 					logAi->debug("Goal %s decomposition failed: goal was completed as much as possible", e.goal->name()); | ||||
| 					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); | ||||
| 					logAi->debug("Goal %s decomposition failed: %s", basicGoal->name(), e.what()); | ||||
| 					break; | ||||
| 				} | ||||
| 				if (elementarGoal->isAbstract) //we can decompose it further | ||||
| 				{ | ||||
| 					goalsToAdd.push_back(elementarGoal); | ||||
| 					//decompose further now - this is necesssary if we can't add over 10 goals in the pool | ||||
| 					goalToDecompose = elementarGoal; | ||||
| 					//there is a risk of infinite abstract goal loop, though it indicates failed logic | ||||
| 					maxAbstractGoals--; | ||||
| 				} | ||||
| 				else if (elementarGoal->isElementar) //should be | ||||
| 				{ | ||||
| 					logAi->debug("Found elementar goal %s", elementarGoal->name()); | ||||
| 					elementarGoals.push_back(elementarGoal); | ||||
| 					ultimateGoalsFromBasic[elementarGoal].push_back(goalToDecompose); //TODO: how about indirect basicGoal? | ||||
| 					break; | ||||
| 				} | ||||
| 				else //should never be here | ||||
| 					throw cannotFulfillGoalException("Goal %s is neither abstract nor elementar!" + basicGoal->name()); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		logAi->trace("Main loop: selecting best elementar goal"); | ||||
|  | ||||
| 		//now choose one elementar goal to realize | ||||
| 		Goals::TGoalVec possibleGoals(elementarGoals.begin(), elementarGoals.end()); //copy to vector | ||||
| 		Goals::TSubgoal goalToRealize = sptr(Goals::Invalid()); | ||||
| 		while (possibleGoals.size()) | ||||
| 		{ | ||||
| 			//allow assign goals to heroes with 0 movement, but don't realize them | ||||
| 			//maybe there are beter ones left | ||||
|  | ||||
| 			auto bestGoal = fh->chooseSolution(possibleGoals); | ||||
| 			if (bestGoal->hero) //lock this hero to fulfill goal | ||||
| 			{ | ||||
| 				setGoal(bestGoal->hero, bestGoal); | ||||
| 				if (!bestGoal->hero->movement || vstd::contains(invalidPathHeroes, bestGoal->hero)) | ||||
| 				{ | ||||
| 					if (!vstd::erase_if_present(possibleGoals, bestGoal)) | ||||
| 					{ | ||||
| 						logAi->error("erase_if_preset failed? Something very wrong!"); | ||||
| 						break; | ||||
| 					} | ||||
| 					continue; //chose next from the list | ||||
| 				} | ||||
| 			} | ||||
| 			goalToRealize = bestGoal; //we found our goal to execute | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		//realize best goal | ||||
| 		if (!goalToRealize->invalid()) | ||||
| 		{ | ||||
| 			logAi->debug("Trying to realize %s (value %2.3f)", goalToRealize->name(), goalToRealize->priority); | ||||
|  | ||||
| 			try | ||||
| 			{ | ||||
| 				boost::this_thread::interruption_point(); | ||||
| 				goalToRealize->accept(this); //visitor pattern | ||||
| 				boost::this_thread::interruption_point(); | ||||
| 			} | ||||
| 			catch (boost::thread_interrupted & e) | ||||
| 			{ | ||||
| 				logAi->debug("Player %d: Making turn thread received an interruption!", playerID); | ||||
| 				throw; //rethrow, we want to truly end this thread | ||||
| 			} | ||||
| 			catch (goalFulfilledException & e) | ||||
| 			{ | ||||
| 				//the sub-goal was completed successfully | ||||
| 				completeGoal(e.goal); | ||||
| 				//local goal was also completed? | ||||
| 				completeGoal(goalToRealize); | ||||
|  | ||||
| 				// remove abstract visit tile if we completed the elementar one | ||||
| 				vstd::erase_if_present(goalsToAdd, goalToRealize); | ||||
| 			} | ||||
| 			catch (std::exception & e) | ||||
| 			{ | ||||
| 				logAi->debug("Failed to realize subgoal of type %s, I will stop.", goalToRealize->name()); | ||||
| 				logAi->debug("The error message was: %s", e.what()); | ||||
|  | ||||
| 				//erase base goal if we failed to execute decomposed goal | ||||
| 				for (auto basicGoal : ultimateGoalsFromBasic[goalToRealize]) | ||||
| 					goalsToRemove.push_back(basicGoal); | ||||
|  | ||||
| 				// sometimes resource manager contains an elementar goal which is not able to execute anymore and just fails each turn. | ||||
| 				ai->ah->notifyGoalCompleted(goalToRealize); | ||||
|  | ||||
| 				//we failed to realize best goal, but maybe others are still possible? | ||||
| 			} | ||||
|  | ||||
| 			//remove goals we couldn't decompose | ||||
| 			for (auto goal : goalsToRemove) | ||||
| 				vstd::erase_if_present(basicGoals, goal); | ||||
|  | ||||
| 			//add abstract goals | ||||
| 			boost::sort(goalsToAdd, [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool | ||||
| 			{ | ||||
| 				return lhs->priority > rhs->priority; //highest priority at the beginning | ||||
| 			}); | ||||
|  | ||||
| 			//max number of goals = 10 | ||||
| 			int i = 0; | ||||
| 			while (basicGoals.size() < 10 && goalsToAdd.size() > i) | ||||
| 			{ | ||||
| 				if (!vstd::contains(basicGoals, goalsToAdd[i])) //don't add duplicates | ||||
| 					basicGoals.push_back(goalsToAdd[i]); | ||||
| 				i++; | ||||
| 			} | ||||
| 		} | ||||
| 		else //no elementar goals possible | ||||
| 		{ | ||||
| 			logAi->debug("Goal decomposition exhausted"); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h) | ||||
| { | ||||
| 	LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->name % obj->getObjectName() % obj->pos.toString()); | ||||
| @@ -1099,7 +856,6 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h) | ||||
| 	{ | ||||
| 	case Obj::CREATURE_GENERATOR1: | ||||
| 		recruitCreatures(dynamic_cast<const CGDwelling *>(obj), h.get()); | ||||
| 		checkHeroArmy(h); | ||||
| 		break; | ||||
| 	case Obj::TOWN: | ||||
| 		if(h->visitedTown) //we are inside, not just attacking | ||||
| @@ -1116,7 +872,7 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h) | ||||
| 				ah->update(); | ||||
| 			} | ||||
|  | ||||
| 			if(ah->getHeroRole(h) == HeroRole::MAIN && !h->hasSpellbook() && ah->freeGold() >= GameConstants::SPELLBOOK_GOLD_COST) | ||||
| 			if(ah->getHeroRole(h) == HeroRole::MAIN && !h->hasSpellbook() && cb->getResourceAmount(Res::GOLD) >= GameConstants::SPELLBOOK_GOLD_COST) | ||||
| 			{ | ||||
| 				if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1)) | ||||
| 					cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK); | ||||
| @@ -1127,7 +883,6 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h) | ||||
| 		makePossibleUpgrades(h.get()); | ||||
| 		break; | ||||
| 	} | ||||
| 	completeGoal(sptr(Goals::VisitObj(obj->id.getNum()).sethero(h))); | ||||
| } | ||||
|  | ||||
| void VCAI::moveCreaturesToHero(const CGTownInstance * t) | ||||
| @@ -1197,10 +952,6 @@ void VCAI::pickBestCreatures(const CArmedInstance * destinationArmy, const CArme | ||||
| 	} | ||||
|  | ||||
| 	//TODO - having now strongest possible army, we may want to think about arranging stacks | ||||
|  | ||||
| 	auto hero = dynamic_cast<const CGHeroInstance *>(destinationArmy); | ||||
| 	if(hero) | ||||
| 		checkHeroArmy(hero); | ||||
| } | ||||
|  | ||||
| void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other) | ||||
| @@ -1313,7 +1064,7 @@ void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruit | ||||
| 		int count = d->creatures[i].first; | ||||
| 		CreatureID creID = d->creatures[i].second.back(); | ||||
|  | ||||
| 		vstd::amin(count, ah->freeResources() / VLC->creh->creatures[creID]->cost); | ||||
| 		vstd::amin(count, cb->getResourceAmount() / VLC->creh->creatures[creID]->cost); | ||||
| 		if(count > 0) | ||||
| 			cb->recruitCreatures(d, recruiter, creID, count, i); | ||||
| 	} | ||||
| @@ -1402,250 +1153,6 @@ bool VCAI::canRecruitAnyHero(const CGTownInstance * t) const | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void VCAI::wander(HeroPtr h) | ||||
| { | ||||
| 	auto visitTownIfAny = [this](HeroPtr h) -> bool | ||||
| 	{ | ||||
| 		if (h->visitedTown) | ||||
| 		{ | ||||
| 			townVisitsThisWeek[h].insert(h->visitedTown); | ||||
| 			buildArmyIn(h->visitedTown); | ||||
| 			return true; | ||||
| 		} | ||||
| 		return false; | ||||
| 	}; | ||||
|  | ||||
| 	//unclaim objects that are now dangerous for us | ||||
| 	auto reservedObjsSetCopy = reservedHeroesMap[h]; | ||||
| 	for(auto obj : reservedObjsSetCopy) | ||||
| 	{ | ||||
| 		if(!isSafeToVisit(h, obj->visitablePos())) | ||||
| 			unreserveObject(h, obj); | ||||
| 	} | ||||
|  | ||||
| 	TimeCheck tc("looking for wander destination"); | ||||
|  | ||||
| 	while(h->movement) | ||||
| 	{ | ||||
| 		validateVisitableObjs(); | ||||
| 		ah->updatePaths(getMyHeroes()); | ||||
|  | ||||
| 		std::vector<ObjectIdRef> dests; | ||||
|  | ||||
| 		//also visit our reserved objects - but they are not prioritized to avoid running back and forth | ||||
| 		vstd::copy_if(reservedHeroesMap[h], std::back_inserter(dests), [&](ObjectIdRef obj) -> bool | ||||
| 		{ | ||||
| 			return ah->isTileAccessible(h, obj->visitablePos()); | ||||
| 		}); | ||||
|  | ||||
| 		int pass = 0; | ||||
| 		std::vector<boost::optional<float>> distanceLimits = | ||||
| 		{ | ||||
| 			1.0, | ||||
| 			2.0, | ||||
| 			boost::none | ||||
| 		}; | ||||
|  | ||||
| 		while(!dests.size() && pass < distanceLimits.size()) | ||||
| 		{ | ||||
| 			auto & distanceLimit = distanceLimits[pass]; | ||||
|  | ||||
| 			logAi->debug("Looking for wander destination pass=%i, cost limit=%f", pass, distanceLimit.get_value_or(-1.0)); | ||||
|  | ||||
| 			vstd::copy_if(visitableObjs, std::back_inserter(dests), [&](ObjectIdRef obj) -> bool | ||||
| 			{ | ||||
| 				return isGoodForVisit(obj, h, distanceLimit); | ||||
| 			}); | ||||
|  | ||||
| 			pass++; | ||||
| 		} | ||||
|  | ||||
| 		if(!dests.size()) | ||||
| 		{ | ||||
| 			logAi->debug("Looking for town destination"); | ||||
|  | ||||
| 			if(cb->getVisitableObjs(h->visitablePos()).size() > 1) | ||||
| 				moveHeroToTile(h->visitablePos(), h); //just in case we're standing on blocked subterranean gate | ||||
|  | ||||
| 			auto compareReinforcements = [&](const CGTownInstance * lhs, const CGTownInstance * rhs) -> bool | ||||
| 			{ | ||||
| 				const CGHeroInstance * hptr = h.get(); | ||||
| 				auto r1 = ah->howManyReinforcementsCanGet(hptr, lhs), | ||||
| 					r2 = ah->howManyReinforcementsCanGet(hptr, rhs); | ||||
| 				if (r1 != r2) | ||||
| 					return r1 < r2; | ||||
| 				else | ||||
| 					return ah->howManyReinforcementsCanBuy(hptr, lhs) < ah->howManyReinforcementsCanBuy(hptr, rhs); | ||||
| 			}; | ||||
|  | ||||
| 			std::vector<const CGTownInstance *> townsReachable; | ||||
| 			std::vector<const CGTownInstance *> townsNotReachable; | ||||
| 			for(const CGTownInstance * t : cb->getTownsInfo()) | ||||
| 			{ | ||||
| 				if(!t->visitingHero && !vstd::contains(townVisitsThisWeek[h], t)) | ||||
| 				{ | ||||
| 					if(isAccessibleForHero(t->visitablePos(), h)) | ||||
| 						townsReachable.push_back(t); | ||||
| 					else | ||||
| 						townsNotReachable.push_back(t); | ||||
| 				} | ||||
| 			} | ||||
| 			if(townsReachable.size()) //travel to town with largest garrison, or empty - better than nothing | ||||
| 			{ | ||||
| 				dests.push_back(*boost::max_element(townsReachable, compareReinforcements)); | ||||
| 			} | ||||
| 			else if(townsNotReachable.size()) | ||||
| 			{ | ||||
| 				//TODO pick the truly best | ||||
| 				const CGTownInstance * t = *boost::max_element(townsNotReachable, compareReinforcements); | ||||
| 				logAi->debug("%s can't reach any town, we'll try to make our way to %s at %s", h->name, t->name, t->visitablePos().toString()); | ||||
| 				int3 pos1 = h->pos; | ||||
| 				striveToGoal(sptr(Goals::ClearWayTo(t->visitablePos()).sethero(h))); //TODO: drop "strive", add to mainLoop | ||||
| 				//if out hero is stuck, we may need to request another hero to clear the way we see | ||||
|  | ||||
| 				if(pos1 == h->pos && h == primaryHero()) //hero can't move | ||||
| 				{ | ||||
| 					if(canRecruitAnyHero(t)) | ||||
| 						recruitHero(t); | ||||
| 				} | ||||
| 				break; | ||||
| 			} | ||||
| 			else if(cb->getResourceAmount(Res::GOLD) >= GameConstants::HERO_GOLD_COST) | ||||
| 			{ | ||||
| 				std::vector<const CGTownInstance *> towns = cb->getTownsInfo(); | ||||
| 				vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool | ||||
| 				{ | ||||
| 					for(const CGHeroInstance * h : cb->getHeroesInfo()) | ||||
| 					{ | ||||
| 						if(!t->getArmyStrength() || ah->howManyReinforcementsCanGet(h, t)) | ||||
| 							return true; | ||||
| 					} | ||||
| 					return false; | ||||
| 				}); | ||||
| 				if (towns.size()) | ||||
| 				{ | ||||
| 					recruitHero(*boost::max_element(towns, compareArmyStrength)); | ||||
| 				} | ||||
| 				break; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				logAi->debug("Nowhere more to go..."); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		//end of objs empty | ||||
|  | ||||
| 		if(dests.size()) //performance improvement | ||||
| 		{ | ||||
| 			Goals::TGoalVec targetObjectGoals; | ||||
| 			for(auto destination : dests) | ||||
| 			{ | ||||
| 				vstd::concatenate(targetObjectGoals, ah->howToVisitObj(h, destination, false)); | ||||
| 			} | ||||
|  | ||||
| 			if(targetObjectGoals.size()) | ||||
| 			{ | ||||
| 				auto bestObjectGoal = fh->chooseSolution(targetObjectGoals); | ||||
|  | ||||
| 				//wander should not cause heroes to be reserved - they are always considered free | ||||
| 				if(bestObjectGoal->goalType == Goals::VISIT_OBJ) | ||||
| 				{ | ||||
| 					auto chosenObject = cb->getObjInstance(ObjectInstanceID(bestObjectGoal->objid)); | ||||
| 					if(chosenObject != nullptr) | ||||
| 						logAi->debug("Of all %d destinations, object %s at pos=%s seems nice", dests.size(), chosenObject->getObjectName(), chosenObject->pos.toString()); | ||||
| 				} | ||||
| 				else | ||||
| 					logAi->debug("Trying to realize goal of type %s as part of wandering.", bestObjectGoal->name()); | ||||
|  | ||||
| 				try | ||||
| 				{ | ||||
| 					decomposeGoal(bestObjectGoal)->accept(this); | ||||
| 				} | ||||
| 				catch(const goalFulfilledException & e) | ||||
| 				{ | ||||
| 					if(e.goal->goalType == Goals::EGoals::VISIT_TILE || e.goal->goalType == Goals::EGoals::VISIT_OBJ) | ||||
| 						continue; | ||||
|  | ||||
| 					throw e; | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				logAi->debug("Nowhere more to go..."); | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			visitTownIfAny(h); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	visitTownIfAny(h); //in case hero is just sitting in town | ||||
| } | ||||
|  | ||||
| void VCAI::setGoal(HeroPtr h, Goals::TSubgoal goal) | ||||
| { | ||||
| 	if(goal->invalid()) | ||||
| 	{ | ||||
| 		vstd::erase_if_present(lockedHeroes, h); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		lockedHeroes[h] = goal; | ||||
| 		goal->setisElementar(false); //Force always evaluate goals before realizing | ||||
| 	} | ||||
| } | ||||
| void VCAI::evaluateGoal(HeroPtr h) | ||||
| { | ||||
| 	if(vstd::contains(lockedHeroes, h)) | ||||
| 		fh->setPriority(lockedHeroes[h]); | ||||
| } | ||||
|  | ||||
| void VCAI::completeGoal(Goals::TSubgoal goal) | ||||
| { | ||||
| 	if (goal->goalType == Goals::WIN) //we can never complete this goal - unless we already won | ||||
| 		return; | ||||
|  | ||||
| 	logAi->debug("Completing goal: %s", goal->name()); | ||||
|  | ||||
| 	//notify Managers | ||||
| 	ah->notifyGoalCompleted(goal); | ||||
| 	//notify mainLoop() | ||||
| 	goalsToRemove.push_back(goal); //will be removed from mainLoop() goals | ||||
| 	for (auto basicGoal : basicGoals) //we could luckily fulfill any of our goals | ||||
| 	{ | ||||
| 		if (basicGoal->fulfillsMe(goal)) | ||||
| 			goalsToRemove.push_back(basicGoal); | ||||
| 	} | ||||
|  | ||||
| 	//unreserve heroes | ||||
| 	if(const CGHeroInstance * h = goal->hero.get(true)) | ||||
| 	{ | ||||
| 		auto it = lockedHeroes.find(h); | ||||
| 		if(it != lockedHeroes.end()) | ||||
| 		{ | ||||
| 			if(it->second == goal || it->second->fulfillsMe(goal)) //FIXME this is overspecified, fulfillsMe shoudl be complete | ||||
| 			{ | ||||
| 				logAi->debug(goal->completeMessage()); | ||||
| 				lockedHeroes.erase(it); //goal fulfilled, free hero | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else //complete goal for all heroes maybe? | ||||
| 	{ | ||||
| 		vstd::erase_if(lockedHeroes, [goal](std::pair<HeroPtr, Goals::TSubgoal> p) | ||||
| 		{ | ||||
| 			if(p.second == goal || p.second->fulfillsMe(goal)) //we could have fulfilled goals of other heroes by chance | ||||
| 			{ | ||||
| 				logAi->debug(p.second->completeMessage()); | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| void VCAI::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side) | ||||
| { | ||||
| 	NET_EVENT_HANDLER; | ||||
| @@ -1686,19 +1193,6 @@ void VCAI::markObjectVisited(const CGObjectInstance * obj) | ||||
| 	alreadyVisited.insert(obj); | ||||
| } | ||||
|  | ||||
| void VCAI::reserveObject(HeroPtr h, const CGObjectInstance * obj) | ||||
| { | ||||
| 	reservedObjs.insert(obj); | ||||
| 	reservedHeroesMap[h].insert(obj); | ||||
| 	logAi->debug("reserved object id=%d; address=%p; name=%s", obj->id, obj, obj->getObjectName()); | ||||
| } | ||||
|  | ||||
| void VCAI::unreserveObject(HeroPtr h, const CGObjectInstance * obj) | ||||
| { | ||||
| 	vstd::erase_if_present(reservedObjs, obj); //unreserve objects | ||||
| 	vstd::erase_if_present(reservedHeroesMap[h], obj); | ||||
| } | ||||
|  | ||||
| void VCAI::markHeroUnableToExplore(HeroPtr h) | ||||
| { | ||||
| 	heroesUnableToExplore.insert(h); | ||||
| @@ -2053,25 +1547,15 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) | ||||
| 	} | ||||
| 	if(h) //we could have lost hero after last move | ||||
| 	{ | ||||
| 		completeGoal(sptr(Goals::VisitTile(dst).sethero(h))); //we stepped on some tile, anyway | ||||
| 		completeGoal(sptr(Goals::ClearWayTo(dst).sethero(h))); | ||||
|  | ||||
| 		ret = ret || (dst == h->visitablePos()); | ||||
|  | ||||
| 		if(!ret) //reserve object we are heading towards | ||||
| 		{ | ||||
| 			auto obj = vstd::frontOrNull(cb->getVisitableObjs(dst)); | ||||
| 			if(obj && obj != *h) | ||||
| 				reserveObject(h, obj); | ||||
| 		} | ||||
|  | ||||
| 		if(startHpos == h->visitablePos() && !ret) //we didn't move and didn't reach the target | ||||
| 		{ | ||||
| 			vstd::erase_if_present(lockedHeroes, h); //hero seemingly is confused or has only 95mp which is not enough to move | ||||
| 			invalidPathHeroes.insert(h); | ||||
| 			throw cannotFulfillGoalException("Invalid path found!"); | ||||
| 		} | ||||
| 		evaluateGoal(h); //new hero position means new game situation | ||||
|  | ||||
| 		logAi->debug("Hero %s moved from %s to %s. Returning %d.", h->name, startHpos.toString(), h->visitablePos().toString(), ret); | ||||
| 	} | ||||
| 	return ret; | ||||
| @@ -2181,7 +1665,6 @@ void VCAI::tryRealize(Goals::DigAtTile & g) | ||||
| 	if(g.hero->diggingStatus() == EDiggingStatus::CAN_DIG) | ||||
| 	{ | ||||
| 		cb->dig(g.hero.get()); | ||||
| 		completeGoal(sptr(g)); // finished digging | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| @@ -2192,7 +1675,7 @@ void VCAI::tryRealize(Goals::DigAtTile & g) | ||||
|  | ||||
| void VCAI::tryRealize(Goals::Trade & g) //trade | ||||
| { | ||||
| 	if(ah->freeResources()[g.resID] >= g.value) //goal is already fulfilled. Why we need this check, anyway? | ||||
| 	if(cb->getResourceAmount((Res::ERes)g.resID) >= g.value) //goal is already fulfilled. Why we need this check, anyway? | ||||
| 		throw goalFulfilledException(sptr(g)); | ||||
|  | ||||
| 	int accquiredResources = 0; | ||||
| @@ -2200,7 +1683,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade | ||||
| 	{ | ||||
| 		if(const IMarket * m = IMarket::castFrom(obj, false)) | ||||
| 		{ | ||||
| 			auto freeRes = ah->freeResources(); //trade only resources which are not reserved | ||||
| 			auto freeRes = cb->getResourceAmount(); //trade only resources which are not reserved | ||||
| 			for(auto it = Res::ResourceSet::nziterator(freeRes); it.valid(); it++) | ||||
| 			{ | ||||
| 				auto res = it->resType; | ||||
| @@ -2217,7 +1700,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade | ||||
| 					accquiredResources = toGet * (it->resVal / toGive); | ||||
| 					logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, accquiredResources, g.resID, obj->getObjectName()); | ||||
| 				} | ||||
| 				if (ah->freeResources()[g.resID] >= g.value) | ||||
| 				if (cb->getResourceAmount((Res::ERes)g.resID) >= g.value) | ||||
| 					throw goalFulfilledException(sptr(g)); //we traded all we needed | ||||
| 			} | ||||
|  | ||||
| @@ -2255,7 +1738,7 @@ void VCAI::tryRealize(Goals::BuyArmy & g) | ||||
|  | ||||
| 	for (int i = 0; valueBought < g.value && i < armyToBuy.size(); i++) | ||||
| 	{ | ||||
| 		auto res = ah->allResources(); | ||||
| 		auto res = cb->getResourceAmount(); | ||||
| 		auto & ci = armyToBuy[i]; | ||||
|  | ||||
| 		if(g.objid != -1 && ci.creID != g.objid) | ||||
| @@ -2365,140 +1848,6 @@ void VCAI::endTurn() | ||||
| 	logGlobal->info("Player %d (%s) ended turn", playerID, playerID.getStr()); | ||||
| } | ||||
|  | ||||
| void VCAI::striveToGoal(Goals::TSubgoal basicGoal) | ||||
| { | ||||
| 	//TODO: this function is deprecated and should be dropped altogether | ||||
|  | ||||
| 	auto goalToDecompose = basicGoal; | ||||
| 	Goals::TSubgoal elementarGoal = sptr(Goals::Invalid()); | ||||
| 	int maxAbstractGoals = 10; | ||||
| 	while (!elementarGoal->isElementar && maxAbstractGoals) | ||||
| 	{ | ||||
| 		try | ||||
| 		{ | ||||
| 			elementarGoal = decomposeGoal(goalToDecompose); | ||||
| 		} | ||||
| 		catch (goalFulfilledException & e) | ||||
| 		{ | ||||
| 			//it is impossible to continue some goals (like exploration, for example) | ||||
| 			completeGoal(e.goal); //put in goalsToRemove | ||||
| 			logAi->debug("Goal %s decomposition failed: goal was completed as much as possible", e.goal->name()); | ||||
| 			return; | ||||
| 		} | ||||
| 		catch (std::exception & e) | ||||
| 		{ | ||||
| 			goalsToRemove.push_back(basicGoal); | ||||
| 			logAi->debug("Goal %s decomposition failed: %s", basicGoal->name(), e.what()); | ||||
| 			return; | ||||
| 		} | ||||
| 		if (elementarGoal->isAbstract) //we can decompose it further | ||||
| 		{ | ||||
| 			goalsToAdd.push_back(elementarGoal); | ||||
| 			//decompose further now - this is necesssary if we can't add over 10 goals in the pool | ||||
| 			goalToDecompose = elementarGoal; | ||||
| 			//there is a risk of infinite abstract goal loop, though it indicates failed logic | ||||
| 			maxAbstractGoals--; | ||||
| 		} | ||||
| 		else if (elementarGoal->isElementar) //should be | ||||
| 		{ | ||||
| 			logAi->debug("Found elementar goal %s", elementarGoal->name()); | ||||
| 			ultimateGoalsFromBasic[elementarGoal].push_back(goalToDecompose); //TODO: how about indirect basicGoal? | ||||
| 			break; | ||||
| 		} | ||||
| 		else //should never be here | ||||
| 			throw cannotFulfillGoalException("Goal %s is neither abstract nor elementar!" + basicGoal->name()); | ||||
| 	} | ||||
|  | ||||
| 	//realize best goal | ||||
| 	if (!elementarGoal->invalid()) | ||||
| 	{ | ||||
| 		logAi->debug("Trying to realize %s (value %2.3f)", elementarGoal->name(), elementarGoal->priority); | ||||
|  | ||||
| 		try | ||||
| 		{ | ||||
| 			boost::this_thread::interruption_point(); | ||||
| 			elementarGoal->accept(this); //visitor pattern | ||||
| 			boost::this_thread::interruption_point(); | ||||
| 		} | ||||
| 		catch (boost::thread_interrupted & e) | ||||
| 		{ | ||||
| 			logAi->debug("Player %d: Making turn thread received an interruption!", playerID); | ||||
| 			throw; //rethrow, we want to truly end this thread | ||||
| 		} | ||||
| 		catch (goalFulfilledException & e) | ||||
| 		{ | ||||
| 			//the sub-goal was completed successfully | ||||
| 			completeGoal(e.goal); | ||||
| 			//local goal was also completed | ||||
| 			completeGoal(elementarGoal); | ||||
| 		} | ||||
| 		catch (std::exception & e) | ||||
| 		{ | ||||
| 			logAi->debug("Failed to realize subgoal of type %s, I will stop.", elementarGoal->name()); | ||||
| 			logAi->debug("The error message was: %s", e.what()); | ||||
|  | ||||
| 			//erase base goal if we failed to execute decomposed goal | ||||
| 			for (auto basicGoalToRemove : ultimateGoalsFromBasic[elementarGoal]) | ||||
| 				goalsToRemove.push_back(basicGoalToRemove); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| Goals::TSubgoal VCAI::decomposeGoal(Goals::TSubgoal ultimateGoal) | ||||
| { | ||||
| 	if(ultimateGoal->isElementar) | ||||
| 	{ | ||||
| 		logAi->warn("Trying to decompose elementar goal %s", ultimateGoal->name()); | ||||
|  | ||||
| 		return ultimateGoal; | ||||
| 	} | ||||
|  | ||||
| 	const int searchDepth = 30; | ||||
|  | ||||
| 	Goals::TSubgoal goal = ultimateGoal; | ||||
| 	logAi->debug("Decomposing goal %s", ultimateGoal->name()); | ||||
| 	int maxGoals = searchDepth; //preventing deadlock for mutually dependent goals | ||||
| 	while (maxGoals) | ||||
| 	{ | ||||
| 		boost::this_thread::interruption_point(); | ||||
|  | ||||
| 		goal = goal->whatToDoToAchieve(); //may throw if decomposition fails | ||||
| 		--maxGoals; | ||||
| 		if (goal == ultimateGoal) //compare objects by value | ||||
| 			if (goal->isElementar == ultimateGoal->isElementar) | ||||
| 				throw cannotFulfillGoalException((boost::format("Goal dependency loop detected for %s!") | ||||
| 												% ultimateGoal->name()).str()); | ||||
| 		if (goal->isAbstract || goal->isElementar) | ||||
| 			return goal; | ||||
| 		else | ||||
| 			logAi->debug("Considering: %s", goal->name()); | ||||
| 	} | ||||
|  | ||||
| 	throw cannotFulfillGoalException("Too many subgoals, don't know what to do"); | ||||
| } | ||||
|  | ||||
| void VCAI::performTypicalActions() | ||||
| { | ||||
| 	for(auto h : getUnblockedHeroes()) | ||||
| 	{ | ||||
| 		if(!h) //hero might be lost. getUnblockedHeroes() called once on start of turn | ||||
| 			continue; | ||||
|  | ||||
| 		logAi->debug("Hero %s started wandering, MP=%d", h->name.c_str(), h->movement); | ||||
| 		makePossibleUpgrades(*h); | ||||
| 		pickBestArtifacts(*h); | ||||
| 		try | ||||
| 		{ | ||||
| 			wander(h); | ||||
| 		} | ||||
| 		catch(std::exception & e) | ||||
| 		{ | ||||
| 			logAi->debug("Cannot use this hero anymore, received exception: %s", e.what()); | ||||
| 			continue; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void VCAI::buildArmyIn(const CGTownInstance * t) | ||||
| { | ||||
| 	makePossibleUpgrades(t->visitingHero); | ||||
| @@ -2507,16 +1856,6 @@ void VCAI::buildArmyIn(const CGTownInstance * t) | ||||
| 	moveCreaturesToHero(t); | ||||
| } | ||||
|  | ||||
| void VCAI::checkHeroArmy(HeroPtr h) | ||||
| { | ||||
| 	auto it = lockedHeroes.find(h); | ||||
| 	if(it != lockedHeroes.end()) | ||||
| 	{ | ||||
| 		if(it->second->goalType == Goals::GATHER_ARMY && it->second->value <= h->getArmyStrength()) | ||||
| 			completeGoal(sptr(Goals::GatherArmy(it->second->value).sethero(h))); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void VCAI::recruitHero(const CGTownInstance * t, bool throwing) | ||||
| { | ||||
| 	logAi->debug("Trying to recruit a hero in %s at %s", t->name, t->visitablePos().toString()); | ||||
| @@ -2547,6 +1886,8 @@ void VCAI::recruitHero(const CGTownInstance * t, bool throwing) | ||||
|  | ||||
| void VCAI::finish() | ||||
| { | ||||
| 	//we want to lock to avoid multiple threads from calling makingTurn->join() at same time | ||||
| 	boost::lock_guard<boost::mutex> multipleCleanupGuard(turnInterruptionMutex); | ||||
| 	if(makingTurn) | ||||
| 	{ | ||||
| 		makingTurn->interrupt(); | ||||
| @@ -2570,41 +1911,11 @@ void VCAI::lostHero(HeroPtr h) | ||||
| { | ||||
| 	logAi->debug("I lost my hero %s. It's best to forget and move on.", h.name); | ||||
|  | ||||
| 	vstd::erase_if_present(lockedHeroes, h); | ||||
| 	for(auto obj : reservedHeroesMap[h]) | ||||
| 	{ | ||||
| 		vstd::erase_if_present(reservedObjs, obj); //unreserve all objects for that hero | ||||
| 	} | ||||
| 	vstd::erase_if_present(reservedHeroesMap, h); | ||||
| 	vstd::erase_if_present(visitedHeroes, h); | ||||
| 	for (auto heroVec : visitedHeroes) | ||||
| 	{ | ||||
| 		vstd::erase_if_present(heroVec.second, h); | ||||
| 	} | ||||
|  | ||||
| 	//remove goals with removed hero assigned from main loop | ||||
| 	vstd::erase_if(ultimateGoalsFromBasic, [&](const std::pair<Goals::TSubgoal, Goals::TGoalVec> & x) -> bool | ||||
| 	{ | ||||
| 		if(x.first->hero == h) | ||||
| 			return true; | ||||
| 		else | ||||
| 			return false; | ||||
| 	}); | ||||
|  | ||||
| 	auto removedHeroGoalPredicate = [&](const Goals::TSubgoal & x) ->bool | ||||
| 	{ | ||||
| 		if(x->hero == h) | ||||
| 			return true; | ||||
| 		else | ||||
| 			return false; | ||||
| 	}; | ||||
|  | ||||
| 	vstd::erase_if(basicGoals, removedHeroGoalPredicate); | ||||
| 	vstd::erase_if(goalsToAdd, removedHeroGoalPredicate); | ||||
| 	vstd::erase_if(goalsToRemove, removedHeroGoalPredicate); | ||||
|  | ||||
| 	for(auto goal : ultimateGoalsFromBasic) | ||||
| 		vstd::erase_if(goal.second, removedHeroGoalPredicate); | ||||
| } | ||||
|  | ||||
| void VCAI::answerQuery(QueryID queryID, int selection) | ||||
| @@ -2914,7 +2225,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj) | ||||
| 	case Obj::SCHOOL_OF_MAGIC: | ||||
| 	case Obj::SCHOOL_OF_WAR: | ||||
| 	{ | ||||
| 		if (ai->ah->freeGold() < 1000) | ||||
| 		if (cb->getResourceAmount(Res::GOLD) < 1000) | ||||
| 			return false; | ||||
| 		break; | ||||
| 	} | ||||
| @@ -2924,7 +2235,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj) | ||||
| 		break; | ||||
| 	case Obj::TREE_OF_KNOWLEDGE: | ||||
| 	{ | ||||
| 		TResources myRes = ai->ah->freeResources(); | ||||
| 		TResources myRes = cb->getResourceAmount(); | ||||
| 		if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10) | ||||
| 			return false; | ||||
| 		break; | ||||
| @@ -2939,7 +2250,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj) | ||||
| 		//TODO: only on request | ||||
| 		if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER) | ||||
| 			return false; | ||||
| 		else if(ai->ah->freeGold() < GameConstants::HERO_GOLD_COST) | ||||
| 		else if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST) | ||||
| 			return false; | ||||
| 		break; | ||||
| 	} | ||||
|   | ||||
| @@ -13,9 +13,7 @@ | ||||
| #include "Goals/AbstractGoal.h" | ||||
| #include "../../lib/AI_Base.h" | ||||
| #include "../../CCallback.h" | ||||
|  | ||||
| #include "../../lib/CThreadHelper.h" | ||||
|  | ||||
| #include "../../lib/GameConstants.h" | ||||
| #include "../../lib/VCMI_Lib.h" | ||||
| #include "../../lib/CBuildingHandler.h" | ||||
| @@ -90,12 +88,6 @@ public: | ||||
| 	//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs | ||||
| 	std::map<HeroPtr, std::set<const CGTownInstance *>> townVisitsThisWeek; | ||||
|  | ||||
| 	//part of mainLoop, but accessible from outisde | ||||
| 	std::vector<Goals::TSubgoal> basicGoals; | ||||
| 	Goals::TGoalVec goalsToRemove; | ||||
| 	Goals::TGoalVec goalsToAdd; | ||||
| 	std::map<Goals::TSubgoal, Goals::TGoalVec> ultimateGoalsFromBasic; //theoreticlaly same goal can fulfill multiple basic goals | ||||
|  | ||||
| 	std::set<HeroPtr> invalidPathHeroes; //FIXME, just a workaround | ||||
| 	std::map<HeroPtr, Goals::TSubgoal> lockedHeroes; //TODO: allow non-elementar objectives | ||||
| 	std::map<HeroPtr, std::set<const CGObjectInstance *>> reservedHeroesMap; //objects reserved by specific heroes | ||||
| @@ -113,6 +105,9 @@ public: | ||||
| 	std::shared_ptr<CCallback> myCb; | ||||
|  | ||||
| 	std::unique_ptr<boost::thread> makingTurn; | ||||
| private: | ||||
| 	boost::mutex turnInterruptionMutex; | ||||
| public: | ||||
| 	ObjectInstanceID selectedObject; | ||||
|  | ||||
| 	AIhelper * ah; | ||||
| @@ -199,17 +194,9 @@ public: | ||||
| 	void battleEnd(const BattleResult * br) override; | ||||
|  | ||||
| 	void makeTurn(); | ||||
| 	void mainLoop(); | ||||
| 	void performTypicalActions(); | ||||
|  | ||||
| 	void buildArmyIn(const CGTownInstance * t); | ||||
| 	void striveToGoal(Goals::TSubgoal ultimateGoal); | ||||
| 	Goals::TSubgoal decomposeGoal(Goals::TSubgoal ultimateGoal); | ||||
| 	void endTurn(); | ||||
| 	void wander(HeroPtr h); | ||||
| 	void setGoal(HeroPtr h, Goals::TSubgoal goal); | ||||
| 	void evaluateGoal(HeroPtr h); //evaluates goal assigned to hero, if any | ||||
| 	void completeGoal(Goals::TSubgoal goal); //safely removes goal from reserved hero | ||||
|  | ||||
| 	void recruitHero(const CGTownInstance * t, bool throwing = false); | ||||
| 	bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit = boost::none); | ||||
| @@ -230,8 +217,6 @@ public: | ||||
|  | ||||
| 	void addVisitableObj(const CGObjectInstance * obj); | ||||
| 	void markObjectVisited(const CGObjectInstance * obj); | ||||
| 	void reserveObject(HeroPtr h, const CGObjectInstance * obj); //TODO: reserve all objects that heroes attempt to visit | ||||
| 	void unreserveObject(HeroPtr h, const CGObjectInstance * obj); | ||||
|  | ||||
| 	void markHeroUnableToExplore(HeroPtr h); | ||||
| 	void markHeroAbleToExplore(HeroPtr h); | ||||
| @@ -261,7 +246,6 @@ public: | ||||
| 	std::vector<HeroPtr> getUnblockedHeroes() const; | ||||
| 	std::vector<HeroPtr> getMyHeroes() const; | ||||
| 	HeroPtr primaryHero() const; | ||||
| 	void checkHeroArmy(HeroPtr h); | ||||
|  | ||||
| 	void requestSent(const CPackForServer * pack, int requestID) override; | ||||
| 	void answerQuery(QueryID queryID, int selection); | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
|   <PropertyGroup Label="Globals"> | ||||
|     <ProjectGuid>{276C3DB0-7A6B-4417-8E5C-322B08633AAC}</ProjectGuid> | ||||
|     <RootNamespace>StupidAI</RootNamespace> | ||||
|     <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion> | ||||
|     <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> | ||||
|   </PropertyGroup> | ||||
|   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> | ||||
| @@ -41,7 +41,7 @@ | ||||
|     <UseDebugLibraries>false</UseDebugLibraries> | ||||
|     <WholeProgramOptimization>true</WholeProgramOptimization> | ||||
|     <CharacterSet>MultiByte</CharacterSet> | ||||
|     <PlatformToolset>v141</PlatformToolset> | ||||
|     <PlatformToolset>v142</PlatformToolset> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="Configuration"> | ||||
|     <ConfigurationType>DynamicLibrary</ConfigurationType> | ||||
| @@ -117,7 +117,7 @@ | ||||
|       <PrecompiledHeader>Use</PrecompiledHeader> | ||||
|       <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile> | ||||
|       <MultiProcessorCompilation>true</MultiProcessorCompilation> | ||||
|       <Optimization>MaxSpeed</Optimization> | ||||
|       <Optimization>Disabled</Optimization> | ||||
|     </ClCompile> | ||||
|     <Link> | ||||
|       <AdditionalDependencies>VCMI_lib.lib;FuzzyLite.lib;%(AdditionalDependencies)</AdditionalDependencies> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user