mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	AI: fix hero exchange logic, allow splitting weakest-fastest creature, refactoring
This commit is contained in:
		| @@ -297,73 +297,6 @@ creInfo infoFromDC(const dwellingContent & dc) | ||||
| 	return ci; | ||||
| } | ||||
|  | ||||
| ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t) | ||||
| { | ||||
| 	ui64 aivalue = 0; | ||||
| 	TResources availableRes = cb->getResourceAmount(); | ||||
| 	int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount(); | ||||
|  | ||||
| 	for(auto const dc : t->creatures) | ||||
| 	{ | ||||
| 		creInfo ci = infoFromDC(dc); | ||||
|  | ||||
| 		if(!ci.count || ci.creID == -1) | ||||
| 			continue; | ||||
|  | ||||
| 		vstd::amin(ci.count, availableRes / ci.cre->cost); //max count we can afford | ||||
|  | ||||
| 		if(ci.count && ci.creID != -1) //valid creature at this level | ||||
| 		{ | ||||
| 			//can be merged with another stack? | ||||
| 			SlotID dst = h->getSlotFor(ci.creID); | ||||
| 			if(!h->hasStackAtSlot(dst)) //need another new slot for this stack | ||||
| 			{ | ||||
| 				if(!freeHeroSlots) //no more place for stacks | ||||
| 					continue; | ||||
| 				else | ||||
| 					freeHeroSlots--; //new slot will be occupied | ||||
| 			} | ||||
|  | ||||
| 			//we found matching occupied or free slot | ||||
| 			aivalue += ci.count * ci.cre->AIValue; | ||||
| 			availableRes -= ci.cre->cost * ci.count; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return aivalue; | ||||
| } | ||||
|  | ||||
| ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t) | ||||
| { | ||||
| 	ui64 ret = 0; | ||||
| 	int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount(); | ||||
| 	std::vector<const CStackInstance *> toMove; | ||||
| 	for(auto const slot : t->Slots()) | ||||
| 	{ | ||||
| 		//can be merged woth another stack? | ||||
| 		SlotID dst = h->getSlotFor(slot.second->getCreatureID()); | ||||
| 		if(h->hasStackAtSlot(dst)) | ||||
| 			ret += t->getPower(slot.first); | ||||
| 		else | ||||
| 			toMove.push_back(slot.second); | ||||
| 	} | ||||
| 	boost::sort(toMove, [](const CStackInstance * lhs, const CStackInstance * rhs) | ||||
| 	{ | ||||
| 		return lhs->getPower() < rhs->getPower(); | ||||
| 	}); | ||||
| 	for(auto & stack : boost::adaptors::reverse(toMove)) | ||||
| 	{ | ||||
| 		if(freeHeroSlots) | ||||
| 		{ | ||||
| 			ret += stack->getPower(); | ||||
| 			freeHeroSlots--; | ||||
| 		} | ||||
| 		else | ||||
| 			break; | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| bool compareHeroStrength(HeroPtr h1, HeroPtr h2) | ||||
| { | ||||
| 	return h1->getTotalStrength() < h2->getTotalStrength(); | ||||
|   | ||||
| @@ -173,8 +173,6 @@ bool isSafeToVisit(HeroPtr h, crint3 tile); | ||||
| bool compareHeroStrength(HeroPtr h1, HeroPtr h2); | ||||
| bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2); | ||||
| bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2); | ||||
| ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t); | ||||
| ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t); | ||||
|  | ||||
| class CDistanceSorter | ||||
| { | ||||
|   | ||||
| @@ -10,14 +10,13 @@ | ||||
| #include "StdInc.h" | ||||
|  | ||||
| #include "AIhelper.h" | ||||
| #include "ResourceManager.h" | ||||
| #include "BuildingManager.h" | ||||
|  | ||||
| AIhelper::AIhelper() | ||||
| { | ||||
| 	resourceManager.reset(new ResourceManager()); | ||||
| 	buildingManager.reset(new BuildingManager()); | ||||
| 	pathfindingManager.reset(new PathfindingManager()); | ||||
| 	armyManager.reset(new ArmyManager()); | ||||
| } | ||||
|  | ||||
| AIhelper::~AIhelper() | ||||
| @@ -34,6 +33,7 @@ void AIhelper::init(CPlayerSpecificInfoCallback * CB) | ||||
| 	resourceManager->init(CB); | ||||
| 	buildingManager->init(CB); | ||||
| 	pathfindingManager->init(CB); | ||||
| 	armyManager->init(CB); | ||||
| } | ||||
|  | ||||
| void AIhelper::setAI(VCAI * AI) | ||||
| @@ -41,6 +41,7 @@ void AIhelper::setAI(VCAI * AI) | ||||
| 	resourceManager->setAI(AI); | ||||
| 	buildingManager->setAI(AI); | ||||
| 	pathfindingManager->setAI(AI); | ||||
| 	armyManager->setAI(AI); | ||||
| } | ||||
|  | ||||
| bool AIhelper::getBuildingOptions(const CGTownInstance * t) | ||||
| @@ -152,3 +153,33 @@ void AIhelper::updatePaths(std::vector<HeroPtr> heroes) | ||||
| { | ||||
| 	pathfindingManager->updatePaths(heroes); | ||||
| } | ||||
|  | ||||
| bool AIhelper::canGetArmy(const CArmedInstance * army, const CArmedInstance * source) const | ||||
| { | ||||
| 	return armyManager->canGetArmy(army, source); | ||||
| } | ||||
|  | ||||
| ui64 AIhelper::howManyReinforcementsCanBuy(const CCreatureSet * h, const CGDwelling * t) const | ||||
| { | ||||
| 	return armyManager->howManyReinforcementsCanBuy(h, t); | ||||
| } | ||||
|  | ||||
| ui64 AIhelper::howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const | ||||
| { | ||||
| 	return armyManager->howManyReinforcementsCanGet(target, source); | ||||
| } | ||||
|  | ||||
| std::vector<SlotInfo> AIhelper::getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const | ||||
| { | ||||
| 	return armyManager->getBestArmy(target, source); | ||||
| } | ||||
|  | ||||
| std::vector<SlotInfo>::iterator AIhelper::getWeakestCreature(std::vector<SlotInfo> & army) const | ||||
| { | ||||
| 	return armyManager->getWeakestCreature(army); | ||||
| } | ||||
|  | ||||
| std::vector<SlotInfo> AIhelper::getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const | ||||
| { | ||||
| 	return armyManager->getSortedSlots(target, source); | ||||
| } | ||||
| @@ -16,6 +16,7 @@ | ||||
|  | ||||
| #include "ResourceManager.h" | ||||
| #include "BuildingManager.h" | ||||
| #include "ArmyManager.h" | ||||
| #include "Pathfinding/PathfindingManager.h" | ||||
|  | ||||
| class ResourceManager; | ||||
| @@ -23,7 +24,7 @@ class BuildingManager; | ||||
|  | ||||
|  | ||||
| //indirection interface for various modules | ||||
| class DLL_EXPORT AIhelper : public IResourceManager, public IBuildingManager, public IPathfindingManager | ||||
| class DLL_EXPORT AIhelper : public IResourceManager, public IBuildingManager, public IPathfindingManager, public IArmyManager | ||||
| { | ||||
| 	friend class VCAI; | ||||
| 	friend struct SetGlobalState; //mess? | ||||
| @@ -31,6 +32,7 @@ class DLL_EXPORT AIhelper : public IResourceManager, public IBuildingManager, pu | ||||
| 	std::shared_ptr<ResourceManager> resourceManager; | ||||
| 	std::shared_ptr<BuildingManager> buildingManager; | ||||
| 	std::shared_ptr<PathfindingManager> pathfindingManager; | ||||
| 	std::shared_ptr<ArmyManager> armyManager; | ||||
| 	//TODO: vector<IAbstractManager> | ||||
| public: | ||||
| 	AIhelper(); | ||||
| @@ -68,6 +70,13 @@ public: | ||||
| 		return pathfindingManager->isTileAccessible(hero, tile); | ||||
| 	} | ||||
|  | ||||
| 	bool canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const override; | ||||
| 	ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source) const override; | ||||
| 	ui64 howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const override; | ||||
| 	std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const override; | ||||
| 	std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const override; | ||||
| 	std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override; | ||||
|  | ||||
| private: | ||||
| 	bool notifyGoalCompleted(Goals::TSubgoal goal) override; | ||||
|  | ||||
|   | ||||
							
								
								
									
										158
									
								
								AI/VCAI/ArmyManager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								AI/VCAI/ArmyManager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,158 @@ | ||||
| /* | ||||
| * 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 "ArmyManager.h" | ||||
|  | ||||
| #include "../../CCallback.h" | ||||
| #include "../../lib/mapObjects/MapObjects.h" | ||||
|  | ||||
| void ArmyManager::init(CPlayerSpecificInfoCallback * CB) | ||||
| { | ||||
| 	cb = CB; | ||||
| } | ||||
|  | ||||
| void ArmyManager::setAI(VCAI * AI) | ||||
| { | ||||
| 	ai = AI; | ||||
| } | ||||
|  | ||||
| std::vector<SlotInfo> ArmyManager::getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const | ||||
| { | ||||
| 	const CCreatureSet * armies[] = { target, source }; | ||||
|  | ||||
| 	//we calculate total strength for each creature type available in armies | ||||
| 	std::map<const CCreature *, SlotInfo> creToPower; | ||||
| 	std::vector<SlotInfo> resultingArmy; | ||||
|  | ||||
| 	for(auto armyPtr : armies) | ||||
| 	{ | ||||
| 		for(auto & i : armyPtr->Slots()) | ||||
| 		{ | ||||
| 			auto & slotInfp = creToPower[i.second->type]; | ||||
|  | ||||
| 			slotInfp.creature = i.second->type; | ||||
| 			slotInfp.power += i.second->getPower(); | ||||
| 			slotInfp.count += i.second->count; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for(auto pair : creToPower) | ||||
| 		resultingArmy.push_back(pair.second); | ||||
|  | ||||
| 	boost::sort(resultingArmy, [](SlotInfo & left, SlotInfo & right) -> bool | ||||
| 	{ | ||||
| 		return left.power > right.power; | ||||
| 	}); | ||||
|  | ||||
| 	return resultingArmy; | ||||
| } | ||||
|  | ||||
| std::vector<SlotInfo>::iterator ArmyManager::getWeakestCreature(std::vector<SlotInfo> & army) const | ||||
| { | ||||
| 	auto weakest = boost::min_element(army, [](SlotInfo & left, SlotInfo & right) -> bool | ||||
| 	{ | ||||
| 		if(left.creature->level != right.creature->level) | ||||
| 			return left.creature->level < right.creature->level; | ||||
| 		 | ||||
| 		return left.creature->Speed() > right.creature->Speed(); | ||||
| 	}); | ||||
|  | ||||
| 	return weakest; | ||||
| } | ||||
|  | ||||
| std::vector<SlotInfo> ArmyManager::getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const | ||||
| { | ||||
| 	auto resultingArmy = getSortedSlots(target, source); | ||||
|  | ||||
| 	if(resultingArmy.size() > GameConstants::ARMY_SIZE) | ||||
| 	{ | ||||
| 		resultingArmy.resize(GameConstants::ARMY_SIZE); | ||||
| 	} | ||||
| 	else if(source->needsLastStack()) | ||||
| 	{ | ||||
| 		auto weakest = getWeakestCreature(resultingArmy); | ||||
|  | ||||
| 		if(weakest->count == 1) | ||||
| 		{ | ||||
| 			resultingArmy.erase(weakest); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			weakest->power -= weakest->power / weakest->count; | ||||
| 			weakest->count--; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return resultingArmy; | ||||
| } | ||||
|  | ||||
| bool ArmyManager::canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const | ||||
| { | ||||
| 	//TODO: merge with pickBestCreatures | ||||
| 	//if (ai->primaryHero().h == source) | ||||
| 	if(target->tempOwner != source->tempOwner) | ||||
| 	{ | ||||
| 		logAi->error("Why are we even considering exchange between heroes from different players?"); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	return 0 < howManyReinforcementsCanGet(target, source); | ||||
| } | ||||
|  | ||||
| ui64 ArmyManager::howManyReinforcementsCanBuy(const CCreatureSet * h, const CGDwelling * t) const | ||||
| { | ||||
| 	ui64 aivalue = 0; | ||||
| 	TResources availableRes = cb->getResourceAmount(); | ||||
| 	int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount(); | ||||
|  | ||||
| 	for(auto const dc : t->creatures) | ||||
| 	{ | ||||
| 		creInfo ci = infoFromDC(dc); | ||||
|  | ||||
| 		if(!ci.count || ci.creID == -1) | ||||
| 			continue; | ||||
|  | ||||
| 		vstd::amin(ci.count, availableRes / ci.cre->cost); //max count we can afford | ||||
|  | ||||
| 		if(ci.count && ci.creID != -1) //valid creature at this level | ||||
| 		{ | ||||
| 			//can be merged with another stack? | ||||
| 			SlotID dst = h->getSlotFor(ci.creID); | ||||
| 			if(!h->hasStackAtSlot(dst)) //need another new slot for this stack | ||||
| 			{ | ||||
| 				if(!freeHeroSlots) //no more place for stacks | ||||
| 					continue; | ||||
| 				else | ||||
| 					freeHeroSlots--; //new slot will be occupied | ||||
| 			} | ||||
|  | ||||
| 			//we found matching occupied or free slot | ||||
| 			aivalue += ci.count * ci.cre->AIValue; | ||||
| 			availableRes -= ci.cre->cost * ci.count; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return aivalue; | ||||
| } | ||||
|  | ||||
| ui64 ArmyManager::howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const | ||||
| { | ||||
| 	auto bestArmy = getBestArmy(target, source); | ||||
| 	uint64_t newArmy = 0; | ||||
| 	uint64_t oldArmy = target->getArmyStrength(); | ||||
|  | ||||
| 	for(auto & slot : bestArmy) | ||||
| 	{ | ||||
| 		newArmy += slot.power; | ||||
| 	} | ||||
|  | ||||
| 	return newArmy > oldArmy ? newArmy - oldArmy : 0; | ||||
| } | ||||
							
								
								
									
										57
									
								
								AI/VCAI/ArmyManager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								AI/VCAI/ArmyManager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| /* | ||||
| * ArmyManager.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 SlotInfo | ||||
| { | ||||
| 	const CCreature * creature; | ||||
| 	int count; | ||||
| 	uint64_t power; | ||||
| }; | ||||
|  | ||||
| class DLL_EXPORT IArmyManager //: public: IAbstractManager | ||||
| { | ||||
| public: | ||||
| 	virtual void init(CPlayerSpecificInfoCallback * CB) = 0; | ||||
| 	virtual void setAI(VCAI * AI) = 0; | ||||
| 	virtual bool canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const = 0; | ||||
| 	virtual ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source) const = 0; | ||||
| 	virtual ui64 howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const = 0; | ||||
| 	virtual std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const = 0; | ||||
| 	virtual std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const = 0; | ||||
| 	virtual std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const = 0; | ||||
| }; | ||||
|  | ||||
| class DLL_EXPORT ArmyManager : public IArmyManager | ||||
| { | ||||
| private: | ||||
| 	CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback | ||||
| 	VCAI * ai; | ||||
|  | ||||
| public: | ||||
| 	void init(CPlayerSpecificInfoCallback * CB) override; | ||||
| 	void setAI(VCAI * AI) override; | ||||
|  | ||||
| 	bool canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const override; | ||||
| 	ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source) const override; | ||||
| 	ui64 howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const override; | ||||
| 	std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const override; | ||||
| 	std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const override; | ||||
| 	std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override; | ||||
| }; | ||||
| @@ -21,6 +21,7 @@ set(VCAI_SRCS | ||||
| 		Pathfinding/Rules/AIPreviousNodeRule.cpp | ||||
| 		AIUtility.cpp | ||||
| 		AIhelper.cpp | ||||
| 		ArmyManager.cpp | ||||
| 		ResourceManager.cpp | ||||
| 		BuildingManager.cpp | ||||
| 		SectorMap.cpp | ||||
| @@ -71,6 +72,7 @@ set(VCAI_HEADERS | ||||
| 		Pathfinding/Rules/AIPreviousNodeRule.h | ||||
| 		AIUtility.h | ||||
| 		AIhelper.h | ||||
| 		ArmyManager.h | ||||
| 		ResourceManager.h | ||||
| 		BuildingManager.h | ||||
| 		SectorMap.h | ||||
|   | ||||
| @@ -62,7 +62,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals() | ||||
| 		if(waysToVisit.size()) | ||||
| 		{ | ||||
| 			//grab army from town | ||||
| 			if(!t->visitingHero && howManyReinforcementsCanGet(hero.get(), t)) | ||||
| 			if(!t->visitingHero && ai->ah->howManyReinforcementsCanGet(hero.get(), t)) | ||||
| 			{ | ||||
| 				if(!vstd::contains(ai->townVisitsThisWeek[hero], t)) | ||||
| 					vstd::concatenate(ret, waysToVisit); | ||||
| @@ -73,8 +73,8 @@ TGoalVec GatherArmy::getAllPossibleSubgoals() | ||||
| 			{ | ||||
| 				std::vector<int> values = { | ||||
| 					value, | ||||
| 					(int)howManyReinforcementsCanBuy(t->getUpperArmy(), t), | ||||
| 					(int)howManyReinforcementsCanBuy(hero.get(), t) }; | ||||
| 					(int)ai->ah->howManyReinforcementsCanBuy(t->getUpperArmy(), t), | ||||
| 					(int)ai->ah->howManyReinforcementsCanBuy(hero.get(), t) }; | ||||
|  | ||||
| 				int val = *std::min_element(values.begin(), values.end()); | ||||
|  | ||||
| @@ -113,7 +113,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals() | ||||
| 			return true; | ||||
| 		else if(!ai->isAccessibleForHero(heroDummy->visitablePos(), h, true)) | ||||
| 			return true; | ||||
| 		else if(!ai->canGetArmy(heroDummy.h, h)) //TODO: return actual aiValue | ||||
| 		else if(!ai->ah->canGetArmy(heroDummy.h, h)) //TODO: return actual aiValue | ||||
| 			return true; | ||||
| 		else if(ai->getGoal(h)->goalType == GATHER_ARMY) | ||||
| 			return true; | ||||
| @@ -148,7 +148,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals() | ||||
| 			{ | ||||
| 				auto dwelling = dynamic_cast<const CGDwelling *>(obj); | ||||
|  | ||||
| 				ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero.get(), dwelling)); | ||||
| 				ui32 val = std::min<ui32>(value, ai->ah->howManyReinforcementsCanBuy(hero.get(), dwelling)); | ||||
|  | ||||
| 				if(val) | ||||
| 				{ | ||||
|   | ||||
							
								
								
									
										165
									
								
								AI/VCAI/VCAI.cpp
									
									
									
									
									
								
							
							
						
						
									
										165
									
								
								AI/VCAI/VCAI.cpp
									
									
									
									
									
								
							| @@ -332,9 +332,9 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q | ||||
| 		} | ||||
| 		else //regular criteria | ||||
| 		{ | ||||
| 			if(firstHero->getFightingStrength() > secondHero->getFightingStrength() && canGetArmy(firstHero, secondHero)) | ||||
| 			if(firstHero->getFightingStrength() > secondHero->getFightingStrength() && ah->canGetArmy(firstHero, secondHero)) | ||||
| 				transferFrom2to1(firstHero, secondHero); | ||||
| 			else if(canGetArmy(secondHero, firstHero)) | ||||
| 			else if(ah->canGetArmy(secondHero, firstHero)) | ||||
| 				transferFrom2to1(secondHero, firstHero); | ||||
| 		} | ||||
|  | ||||
| @@ -1056,132 +1056,59 @@ void VCAI::moveCreaturesToHero(const CGTownInstance * t) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool VCAI::canGetArmy(const CGHeroInstance * army, const CGHeroInstance * source) | ||||
| { | ||||
| 	//TODO: merge with pickBestCreatures | ||||
| 	//if (ai->primaryHero().h == source) | ||||
| 	if(army->tempOwner != source->tempOwner) | ||||
| 	{ | ||||
| 		logAi->error("Why are we even considering exchange between heroes from different players?"); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	const CArmedInstance * armies[] = {army, source}; | ||||
|  | ||||
| 	//we calculate total strength for each creature type available in armies | ||||
| 	std::map<const CCreature *, int> creToPower; | ||||
| 	for(auto armyPtr : armies) | ||||
| 	{ | ||||
| 		for(auto & i : armyPtr->Slots()) | ||||
| 		{ | ||||
| 			creToPower[i.second->type] += i.second->getPower(); | ||||
| 		} | ||||
| 	} | ||||
| 	//TODO - consider more than just power (ie morale penalty, hero specialty in certain stacks, etc) | ||||
| 	int armySize = creToPower.size(); | ||||
| 	armySize = std::min(armySize, GameConstants::ARMY_SIZE); | ||||
| 	std::vector<const CCreature *> bestArmy; //types that'll be in final dst army | ||||
| 	for(int i = 0; i < armySize; i++) //pick the creatures from which we can get most power, as many as dest can fit | ||||
| 	{ | ||||
| 		typedef const std::pair<const CCreature *, int> & CrePowerPair; | ||||
| 		auto creIt = boost::max_element(creToPower, [](CrePowerPair lhs, CrePowerPair rhs) | ||||
| 		{ | ||||
| 			return lhs.second < rhs.second; | ||||
| 		}); | ||||
| 		bestArmy.push_back(creIt->first); | ||||
| 		creToPower.erase(creIt); | ||||
| 		if(creToPower.empty()) | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 	//foreach best type -> iterate over slots in both armies and if it's the appropriate type, send it to the slot where it belongs | ||||
| 	for(int i = 0; i < bestArmy.size(); i++) //i-th strongest creature type will go to i-th slot | ||||
| 	{ | ||||
| 		for(auto armyPtr : armies) | ||||
| 		{ | ||||
| 			for(int j = 0; j < GameConstants::ARMY_SIZE; j++) | ||||
| 			{ | ||||
| 				if(armyPtr->getCreature(SlotID(j)) == bestArmy[i] && armyPtr != army) //it's a searched creature not in dst ARMY | ||||
| 				{ | ||||
| 					if(!(armyPtr->needsLastStack() && (armyPtr->stacksCount() == 1) && armyPtr->getStackCount(SlotID(j)) < 2)) //can't take away or split last creature | ||||
| 						return true; //at least one exchange will be performed | ||||
| 					else | ||||
| 						return false; //no further exchange possible | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| void VCAI::pickBestCreatures(const CArmedInstance * destinationArmy, const CArmedInstance * source) | ||||
| { | ||||
| 	const CArmedInstance * armies[] = {destinationArmy, source}; | ||||
|  | ||||
| 	//we calculate total strength for each creature type available in armies | ||||
| 	std::map<const CCreature *, int> creToPower; | ||||
| 	for(auto armyPtr : armies) | ||||
| 	{ | ||||
| 		for(auto & i : armyPtr->Slots()) | ||||
| 		{ | ||||
| 			creToPower[i.second->type] += i.second->getPower(); | ||||
| 		} | ||||
| 	} | ||||
| 	//TODO - consider more than just power (ie morale penalty, hero specialty in certain stacks, etc) | ||||
| 	int armySize = creToPower.size(); | ||||
|  | ||||
| 	armySize = std::min(armySize, GameConstants::ARMY_SIZE); | ||||
| 	std::vector<const CCreature *> bestArmy; //types that'll be in final dst army | ||||
| 	for(int i = 0; i < armySize; i++) //pick the creatures from which we can get most power, as many as dest can fit | ||||
| 	{ | ||||
| 		typedef const std::pair<const CCreature *, int> & CrePowerPair; | ||||
| 		auto creIt = boost::max_element(creToPower, [](CrePowerPair lhs, CrePowerPair rhs) | ||||
| 		{ | ||||
| 			return lhs.second < rhs.second; | ||||
| 		}); | ||||
| 		bestArmy.push_back(creIt->first); | ||||
| 		creToPower.erase(creIt); | ||||
| 		if(creToPower.empty()) | ||||
| 			break; | ||||
| 	} | ||||
| 	auto bestArmy = ah->getSortedSlots(destinationArmy, source); | ||||
|  | ||||
| 	//foreach best type -> iterate over slots in both armies and if it's the appropriate type, send it to the slot where it belongs | ||||
| 	for(int i = 0; i < bestArmy.size(); i++) //i-th strongest creature type will go to i-th slot | ||||
| 	for(SlotID i = SlotID(0); i.getNum() < bestArmy.size() && i.validSlot(); i.advance(1)) //i-th strongest creature type will go to i-th slot | ||||
| 	{ | ||||
| 		const CCreature * targetCreature = bestArmy[i.getNum()].creature; | ||||
|  | ||||
| 		for(auto armyPtr : armies) | ||||
| 		{ | ||||
| 			for(int j = 0; j < GameConstants::ARMY_SIZE; j++) | ||||
| 			for(SlotID j = SlotID(0); j.validSlot(); j.advance(1)) | ||||
| 			{ | ||||
| 				if(armyPtr->getCreature(SlotID(j)) == bestArmy[i] && (i != j || armyPtr != destinationArmy)) //it's a searched creature not in dst SLOT | ||||
| 				if(armyPtr->getCreature(j) == targetCreature && (i != j || armyPtr != destinationArmy)) //it's a searched creature not in dst SLOT | ||||
| 				{ | ||||
| 					if(!(armyPtr->needsLastStack() && armyPtr->stacksCount() == 1)) //can't take away last creature without split | ||||
| 					//can't take away last creature without split. generate a new stack with 1 creature which is weak but fast | ||||
| 					if(armyPtr == source | ||||
| 						&& source->needsLastStack() | ||||
| 						&& source->stacksCount() == 1 | ||||
| 						&& (!destinationArmy->hasStackAtSlot(i) || destinationArmy->getCreature(i) == targetCreature)) | ||||
| 					{ | ||||
| 						cb->mergeOrSwapStacks(armyPtr, destinationArmy, SlotID(j), SlotID(i)); | ||||
| 					} | ||||
| 					else | ||||
| 					{	 | ||||
| 						//TODO: Improve logic by splitting weakest creature, instead of creature that becomes last stack | ||||
| 						SlotID sourceSlot = SlotID(j); | ||||
| 						auto lastStackCount = armyPtr->getStackCount(sourceSlot); | ||||
| 						auto weakest = ah->getWeakestCreature(bestArmy); | ||||
| 						 | ||||
| 						if(weakest->creature == targetCreature) | ||||
| 						{ | ||||
| 							if(1 == source->getStackCount(j)) | ||||
| 								break; | ||||
|  | ||||
| 						if(lastStackCount > 1) //we can perform exchange if we need creature and split is possible | ||||
| 						{	 | ||||
| 							SlotID destinationSlot = SlotID(i); | ||||
| 							//check if there are some creatures of same type in destination army slots - add to them instead of first available empty slot if possible | ||||
| 							for(int candidateSlot = 0; candidateSlot < GameConstants::ARMY_SIZE; candidateSlot++) | ||||
| 							{ | ||||
| 								auto creatureInSlot = destinationArmy->getCreature(SlotID(candidateSlot)); | ||||
| 								if(creatureInSlot && (creatureInSlot->idNumber == armyPtr->getCreature(SlotID(j))->idNumber)) | ||||
| 								{ | ||||
| 									destinationSlot = SlotID(candidateSlot); | ||||
| 									break; | ||||
| 								} | ||||
| 							} | ||||
| 							//last cb->splitStack argument is total amount of creatures expected after exchange so if slot is not empty we need to add to existing creatures | ||||
| 							auto destinationSlotCreatureCount = destinationArmy->getStackCount(destinationSlot); | ||||
| 							cb->splitStack(armyPtr, destinationArmy, sourceSlot, destinationSlot, lastStackCount + destinationSlotCreatureCount - 1); | ||||
| 							// move all except 1 of weakest creature from source to destination | ||||
| 							cb->splitStack( | ||||
| 								source, | ||||
| 								destinationArmy, | ||||
| 								j, | ||||
| 								destinationArmy->getSlotFor(targetCreature), | ||||
| 								destinationArmy->getStackCount(i) + source->getStackCount(j) - 1); | ||||
|  | ||||
| 							break; | ||||
| 						} | ||||
| 						else | ||||
| 						{ | ||||
| 							// Source last stack is not weakest. Move 1 of weakest creature from destination to source | ||||
| 							cb->splitStack( | ||||
| 								destinationArmy, | ||||
| 								source, | ||||
| 								destinationArmy->getSlotFor(weakest->creature), | ||||
| 								source->getFreeSlot(), | ||||
| 								1); | ||||
| 						} | ||||
| 					} | ||||
|  | ||||
| 					cb->mergeOrSwapStacks(armyPtr, destinationArmy, j, i); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @@ -1461,15 +1388,15 @@ void VCAI::wander(HeroPtr h) | ||||
| 			if(cb->getVisitableObjs(h->visitablePos()).size() > 1) | ||||
| 				moveHeroToTile(h->visitablePos(), h); //just in case we're standing on blocked subterranean gate | ||||
|  | ||||
| 			auto compareReinforcements = [h](const CGTownInstance * lhs, const CGTownInstance * rhs) -> bool | ||||
| 			auto compareReinforcements = [&](const CGTownInstance * lhs, const CGTownInstance * rhs) -> bool | ||||
| 			{ | ||||
| 				const CGHeroInstance * hptr = h.get(); | ||||
| 				auto r1 = howManyReinforcementsCanGet(hptr, lhs), | ||||
| 					r2 = howManyReinforcementsCanGet(hptr, rhs); | ||||
| 				auto r1 = ah->howManyReinforcementsCanGet(hptr, lhs), | ||||
| 					r2 = ah->howManyReinforcementsCanGet(hptr, rhs); | ||||
| 				if (r1 != r2) | ||||
| 					return r1 < r2; | ||||
| 				else | ||||
| 					return howManyReinforcementsCanBuy(hptr, lhs) < howManyReinforcementsCanBuy(hptr, rhs); | ||||
| 					return ah->howManyReinforcementsCanBuy(hptr, lhs) < ah->howManyReinforcementsCanBuy(hptr, rhs); | ||||
| 			}; | ||||
|  | ||||
| 			std::vector<const CGTownInstance *> townsReachable; | ||||
| @@ -1507,11 +1434,11 @@ void VCAI::wander(HeroPtr h) | ||||
| 			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 | ||||
| 				vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool | ||||
| 				{ | ||||
| 					for(const CGHeroInstance * h : cb->getHeroesInfo()) | ||||
| 					{ | ||||
| 						if(!t->getArmyStrength() || howManyReinforcementsCanGet(h, t)) | ||||
| 						if(!t->getArmyStrength() || ah->howManyReinforcementsCanGet(h, t)) | ||||
| 							return true; | ||||
| 					} | ||||
| 					return false; | ||||
|   | ||||
| @@ -214,7 +214,6 @@ public: | ||||
| 	bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) const; | ||||
| 	//void recruitCreatures(const CGTownInstance * t); | ||||
| 	void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter); | ||||
| 	bool canGetArmy(const CGHeroInstance * h, const CGHeroInstance * source); //can we get any better stacks from other hero? | ||||
| 	void pickBestCreatures(const CArmedInstance * army, const CArmedInstance * source); //called when we can't find a slot for new stack | ||||
| 	void pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other = nullptr); | ||||
| 	void moveCreaturesToHero(const CGTownInstance * t); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user