mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Nullkiller AI: new prioritization engine stabilization
This commit is contained in:
		
				
					committed by
					
						 Andrii Danylchenko
						Andrii Danylchenko
					
				
			
			
				
	
			
			
			
						parent
						
							b261734905
						
					
				
				
					commit
					6bebb766a6
				
			| @@ -192,6 +192,11 @@ std::vector<SlotInfo> AIhelper::getSortedSlots(const CCreatureSet * target, cons | ||||
| 	return armyManager->getSortedSlots(target, source); | ||||
| } | ||||
|  | ||||
| std::vector<creInfo> AIhelper::getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const | ||||
| { | ||||
| 	return armyManager->getArmyAvailableToBuy(hero, dwelling); | ||||
| } | ||||
|  | ||||
| int AIhelper::selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const | ||||
| { | ||||
| 	return heroManager->selectBestSkill(hero, skills); | ||||
|   | ||||
| @@ -79,6 +79,7 @@ public: | ||||
| 	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; | ||||
| 	std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const override; | ||||
|  | ||||
| 	const std::map<HeroPtr, HeroRole> & getHeroRoles() const override; | ||||
| 	HeroRole getHeroRole(const HeroPtr & hero) const override; | ||||
|   | ||||
| @@ -110,37 +110,49 @@ bool ArmyManager::canGetArmy(const CArmedInstance * target, const CArmedInstance | ||||
| ui64 ArmyManager::howManyReinforcementsCanBuy(const CCreatureSet * h, const CGDwelling * t) const | ||||
| { | ||||
| 	ui64 aivalue = 0; | ||||
| 	TResources availableRes = cb->getResourceAmount(); | ||||
| 	int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount(); | ||||
| 	auto army = getArmyAvailableToBuy(h, t); | ||||
|  | ||||
| 	for(auto const dc : t->creatures) | ||||
| 	for(const creInfo & ci : army) | ||||
| 	{ | ||||
| 		creInfo ci = infoFromDC(dc); | ||||
| 		aivalue += ci.count * ci.cre->AIValue; | ||||
| 	} | ||||
|  | ||||
| 	return aivalue; | ||||
| } | ||||
|  | ||||
| std::vector<creInfo> ArmyManager::getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const | ||||
| { | ||||
| 	auto availableRes = cb->getResourceAmount(); | ||||
| 	std::vector<creInfo> creaturesInDwellings; | ||||
| 	int freeHeroSlots = GameConstants::ARMY_SIZE - hero->stacksCount(); | ||||
|  | ||||
| 	for(int i = dwelling->creatures.size() - 1; i >= 0; i--) | ||||
| 	{ | ||||
| 		auto ci = infoFromDC(dwelling->creatures[i]); | ||||
|  | ||||
| 		if(!ci.count || ci.creID == -1) | ||||
| 			continue; | ||||
|  | ||||
| 		SlotID dst = hero->getSlotFor(ci.creID); | ||||
| 		if(!hero->hasStackAtSlot(dst)) //need another new slot for this stack | ||||
| 		{ | ||||
| 			if(!freeHeroSlots) //no more place for stacks | ||||
| 				continue; | ||||
| 			else | ||||
| 				freeHeroSlots--; //new slot will be occupied | ||||
| 		} | ||||
|  | ||||
| 		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 | ||||
| 			} | ||||
| 		if(!ci.count) | ||||
| 			continue; | ||||
|  | ||||
| 			//we found matching occupied or free slot | ||||
| 			aivalue += ci.count * ci.cre->AIValue; | ||||
| 			availableRes -= ci.cre->cost * ci.count; | ||||
| 		} | ||||
| 		ci.level = i; //this is important for Dungeon Summoning Portal | ||||
| 		creaturesInDwellings.push_back(ci); | ||||
| 		availableRes -= ci.cre->cost * ci.count; | ||||
| 	} | ||||
|  | ||||
| 	return aivalue; | ||||
| 	return creaturesInDwellings; | ||||
| } | ||||
|  | ||||
| ui64 ArmyManager::howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const | ||||
|   | ||||
| @@ -36,6 +36,7 @@ public: | ||||
| 	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; | ||||
| 	virtual std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const = 0; | ||||
| }; | ||||
|  | ||||
| class DLL_EXPORT ArmyManager : public IArmyManager | ||||
| @@ -54,4 +55,5 @@ public: | ||||
| 	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; | ||||
| 	std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const override; | ||||
| }; | ||||
|   | ||||
| @@ -103,36 +103,38 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 					if(!targetNode->accessible || targetNode->turns != 0) | ||||
| 					{ | ||||
| 						logAi->error( | ||||
| 							"Enable to complete chain. Expected hero %s to arive to %s but he in 0 turns but he can not do this", | ||||
| 							"Enable to complete chain. Expected hero %s to arive to %s in 0 turns but he can not do this", | ||||
| 							hero.name, | ||||
| 							node.coord.toString(), | ||||
| 							hero->visitablePos().toString()); | ||||
| 							node.coord.toString()); | ||||
|  | ||||
| 						return; | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				try | ||||
| 				if(hero->movement) | ||||
| 				{ | ||||
| 					Goals::VisitTile(node.coord).sethero(hero).accept(ai); | ||||
| 				} | ||||
| 				catch(cannotFulfillGoalException) | ||||
| 				{ | ||||
| 					if(hero->movement > 0) | ||||
| 					try | ||||
| 					{ | ||||
| 						CGPath path; | ||||
| 						bool isOk = cb->getPathsInfo(hero.get())->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()); | ||||
|  | ||||
| 							ai->nullkiller->lockHero(hero.get()); | ||||
| 							return; | ||||
| 						} | ||||
| 						Goals::VisitTile(node.coord).sethero(hero).accept(ai); | ||||
| 					} | ||||
| 					catch(cannotFulfillGoalException) | ||||
| 					{ | ||||
| 						if(hero->movement > 0) | ||||
| 						{ | ||||
| 							CGPath path; | ||||
| 							bool isOk = cb->getPathsInfo(hero.get())->getPath(path, node.coord); | ||||
|  | ||||
| 					throw; | ||||
| 							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()); | ||||
|  | ||||
| 								ai->nullkiller->lockHero(hero.get()); | ||||
| 								return; | ||||
| 							} | ||||
| 						} | ||||
|  | ||||
| 						throw; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
|   | ||||
| @@ -201,11 +201,6 @@ void AINodeStorage::commit( | ||||
| 	int movementLeft,  | ||||
| 	float cost) const | ||||
| { | ||||
| 	if(destination->actor->chainMask == 195 && turn == 0) | ||||
| 	{ | ||||
| 		throw std::exception(); | ||||
| 	} | ||||
|  | ||||
| 	destination->action = action; | ||||
| 	destination->cost = cost; | ||||
| 	destination->moveRemains = movementLeft; | ||||
|   | ||||
| @@ -1068,7 +1068,7 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h) | ||||
| 		{ | ||||
| 			makePossibleUpgrades(h.get()); | ||||
|  | ||||
| 			if(!h->visitedTown->garrisonHero) | ||||
| 			if(!nullkiller || !h->visitedTown->garrisonHero || !nullkiller->isHeroLocked(h->visitedTown->garrisonHero)) | ||||
| 				moveCreaturesToHero(h->visitedTown); | ||||
|  | ||||
| 			townVisitsThisWeek[h].insert(h->visitedTown); | ||||
| @@ -2192,46 +2192,33 @@ void VCAI::tryRealize(Goals::BuyArmy & g) | ||||
|  | ||||
| 	makePossibleUpgrades(t); | ||||
|  | ||||
| 	while (valueBought < g.value) | ||||
| 	auto armyToBuy = ah->getArmyAvailableToBuy(t->getUpperArmy(), t); | ||||
|  | ||||
| 	if(armyToBuy.empty()) | ||||
| 	{ | ||||
| 		throw cannotFulfillGoalException("No creatures to buy."); | ||||
| 	} | ||||
|  | ||||
| 	for (int i = 0; valueBought < g.value && i < armyToBuy.size(); i++) | ||||
| 	{ | ||||
| 		auto res = ah->allResources(); | ||||
| 		std::vector<creInfo> creaturesInDwellings; | ||||
| 		auto & ci = armyToBuy[i]; | ||||
|  | ||||
| 		for (int i = t->creatures.size() - 1; i >= 0; i--) | ||||
| 		if(g.objid != -1 && ci.creID != g.objid) | ||||
| 			continue; | ||||
|  | ||||
| 		vstd::amin(ci.count, res / ci.cre->cost); | ||||
|  | ||||
| 		if(ci.count) | ||||
| 		{ | ||||
| 			auto ci = infoFromDC(t->creatures[i]); | ||||
|  | ||||
| 			if(!ci.count | ||||
| 				|| ci.creID == -1 | ||||
| 				|| (g.objid != -1 && ci.creID != g.objid) | ||||
| 				|| t->getUpperArmy()->getSlotFor(ci.creID) == SlotID()) | ||||
| 				continue; | ||||
|  | ||||
| 			vstd::amin(ci.count, res / ci.cre->cost); //max count we can afford | ||||
|  | ||||
| 			if(!ci.count) | ||||
| 				continue; | ||||
|  | ||||
| 			ci.level = i; //this is important for Dungeon Summoning Portal | ||||
| 			creaturesInDwellings.push_back(ci); | ||||
| 			cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level); | ||||
| 			valueBought += ci.count * ci.cre->AIValue; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 		if (creaturesInDwellings.empty()) | ||||
| 			throw cannotFulfillGoalException("Can't buy any more creatures!"); | ||||
|  | ||||
| 		creInfo ci = | ||||
| 			*boost::max_element(creaturesInDwellings, [](const creInfo & lhs, const creInfo & rhs) | ||||
| 		{ | ||||
| 			//max value of creatures we can buy with our res | ||||
| 			int value1 = lhs.cre->AIValue * lhs.count, | ||||
| 				value2 = rhs.cre->AIValue * rhs.count; | ||||
|  | ||||
| 			return value1 < value2; | ||||
| 		}); | ||||
|  | ||||
|  | ||||
| 		cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level); | ||||
| 		valueBought += ci.count * ci.cre->AIValue; | ||||
| 	if(!valueBought) | ||||
| 	{ | ||||
| 		throw cannotFulfillGoalException("No creatures to buy."); | ||||
| 	} | ||||
|  | ||||
| 	if(t->visitingHero) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user