mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Nullkiller: stabilisation fixes
This commit is contained in:
		
				
					committed by
					
						 Andrii Danylchenko
						Andrii Danylchenko
					
				
			
			
				
	
			
			
			
						parent
						
							17a960e850
						
					
				
				
					commit
					b7b615ec70
				
			
							
								
								
									
										162
									
								
								AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | ||||
| /* | ||||
| * GatherArmyBehavior.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 "../VCAI.h" | ||||
| #include "../Engine/Nullkiller.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../Goals/ExecuteHeroChain.h" | ||||
| #include "GatherArmyBehavior.h" | ||||
| #include "../AIUtility.h" | ||||
| #include "lib/mapping/CMap.h" //for victory conditions | ||||
| #include "lib/CPathfinder.h" | ||||
|  | ||||
| extern boost::thread_specific_ptr<CCallback> cb; | ||||
| extern boost::thread_specific_ptr<VCAI> ai; | ||||
| extern FuzzyHelper * fh; | ||||
|  | ||||
| using namespace Goals; | ||||
|  | ||||
| #define AI_TRACE_LEVEL 2 | ||||
|  | ||||
| std::string GatherArmyBehavior::toString() const | ||||
| { | ||||
| 	return "Gather army"; | ||||
| } | ||||
|  | ||||
| Goals::TGoalVec GatherArmyBehavior::getTasks() | ||||
| { | ||||
| 	Goals::TGoalVec tasks; | ||||
|  | ||||
| 	auto heroes = cb->getHeroesInfo(); | ||||
|  | ||||
| 	if(heroes.empty()) | ||||
| 	{ | ||||
| 		return tasks; | ||||
| 	} | ||||
| 	 | ||||
| 	for(const CGHeroInstance * hero : heroes) | ||||
| 	{ | ||||
| 		if(ai->ah->getHeroRole(hero) != HeroRole::MAIN | ||||
| 			|| hero->getArmyStrength() < 300) | ||||
| 		{ | ||||
| #ifdef AI_TRACE_LEVEL >= 1 | ||||
| 			logAi->trace("Skipping hero %s", hero->name); | ||||
| #endif | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		const int3 pos = hero->visitablePos(); | ||||
|  | ||||
| #ifdef AI_TRACE_LEVEL >= 1 | ||||
| 		logAi->trace("Checking ways to gaher army for hero %s, %s", hero->getObjectName(), pos.toString()); | ||||
| #endif | ||||
| 		if(ai->nullkiller->isHeroLocked(hero)) | ||||
| 		{ | ||||
| #ifdef AI_TRACE_LEVEL >= 1 | ||||
| 			logAi->trace("Skipping locked hero %s, %s", hero->getObjectName(), pos.toString()); | ||||
| #endif | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		auto paths = ai->ah->getPathsToTile(pos); | ||||
| 		std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj; | ||||
|  | ||||
| #ifdef AI_TRACE_LEVEL >= 1 | ||||
| 		logAi->trace("Found %d paths", paths.size()); | ||||
| #endif | ||||
|  | ||||
| 		for(auto & path : paths) | ||||
| 		{ | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| 			logAi->trace("Path found %s", path.toString()); | ||||
| #endif | ||||
| 			bool skip = path.targetHero == hero; | ||||
|  | ||||
| 			for(auto node : path.nodes) | ||||
| 			{ | ||||
| 				skip |= (node.targetHero == hero); | ||||
| 			} | ||||
|  | ||||
| 			if(skip) continue; | ||||
|  | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| 			logAi->trace("Path found %s", path.toString()); | ||||
| #endif | ||||
|  | ||||
| 			if(path.getFirstBlockedAction()) | ||||
| 			{ | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| 				// TODO: decomposition? | ||||
| 				logAi->trace("Ignore path. Action is blocked."); | ||||
| #endif | ||||
| 				continue; | ||||
| 			} | ||||
|  | ||||
| 			if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path)) | ||||
| 			{ | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| 				logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength()); | ||||
| #endif | ||||
| 				continue; | ||||
| 			} | ||||
|  | ||||
| 			float armyValue = (float)ai->ah->howManyReinforcementsCanGet(hero, path.heroArmy) / hero->getArmyStrength(); | ||||
|  | ||||
| 			// avoid transferring very small amount of army | ||||
| 			if(armyValue < 0.1f) | ||||
| 				continue; | ||||
|  | ||||
| 			// avoid trying to move bigger army to the weaker one. | ||||
| 			if(armyValue > 0.5f) | ||||
| 				continue; | ||||
|  | ||||
| 			auto danger = path.getTotalDanger(); | ||||
|  | ||||
| 			auto isSafe = isSafeToVisit(hero, path.heroArmy, danger); | ||||
|  | ||||
| #ifdef AI_TRACE_LEVEL >= 2 | ||||
| 			logAi->trace( | ||||
| 				"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld", | ||||
| 				isSafe ? "safe" : "not safe", | ||||
| 				hero->name, | ||||
| 				path.targetHero->name, | ||||
| 				path.getHeroStrength(), | ||||
| 				danger, | ||||
| 				path.getTotalArmyLoss()); | ||||
| #endif | ||||
|  | ||||
| 			if(isSafe) | ||||
| 			{ | ||||
| 				auto newWay = std::make_shared<ExecuteHeroChain>(path, hero); | ||||
|  | ||||
| 				newWay->evaluationContext.strategicalValue = armyValue; | ||||
| 				waysToVisitObj.push_back(newWay); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if(waysToVisitObj.empty()) | ||||
| 			continue; | ||||
|  | ||||
| 		for(auto way : waysToVisitObj) | ||||
| 		{ | ||||
| 			if(ai->nullkiller->arePathHeroesLocked(way->getPath())) | ||||
| 				continue; | ||||
|  | ||||
| 			if(ai->nullkiller->getHeroLockedReason(way->hero.get()) == HeroLockedReason::STARTUP) | ||||
| 				continue; | ||||
|  | ||||
| 			way->evaluationContext.closestWayRatio = 1; | ||||
|  | ||||
| 			tasks.push_back(sptr(*way)); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return tasks; | ||||
| } | ||||
							
								
								
									
										32
									
								
								AI/Nullkiller/Behaviors/GatherArmyBehavior.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								AI/Nullkiller/Behaviors/GatherArmyBehavior.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| /* | ||||
| * GatherArmyBehavior.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 "lib/VCMI_Lib.h" | ||||
| #include "Behavior.h" | ||||
| #include "../AIUtility.h" | ||||
|  | ||||
| class GatherArmyBehavior : public Behavior { | ||||
| private: | ||||
| 	std::vector<int> objectTypes; | ||||
| 	std::vector<int> objectSubTypes; | ||||
| 	std::vector<const CGObjectInstance *> objectsToCapture; | ||||
| 	bool specificObjects; | ||||
| public: | ||||
| 	GatherArmyBehavior() | ||||
| 	{ | ||||
| 		objectTypes = std::vector<int>(); | ||||
| 		specificObjects = false; | ||||
| 	} | ||||
|  | ||||
| 	virtual Goals::TGoalVec getTasks() override; | ||||
| 	virtual std::string toString() const override; | ||||
| }; | ||||
|  | ||||
| @@ -60,6 +60,7 @@ set(VCAI_SRCS | ||||
| 		Behaviors/DefenceBehavior.cpp | ||||
| 		Behaviors/StartupBehavior.cpp | ||||
| 		Behaviors/BuildingBehavior.cpp | ||||
| 		Behaviors/GatherArmyBehavior.cpp | ||||
| 		main.cpp | ||||
| 		VCAI.cpp | ||||
| ) | ||||
| @@ -130,6 +131,7 @@ set(VCAI_HEADERS | ||||
| 		Behaviors/DefenceBehavior.h | ||||
| 		Behaviors/StartupBehavior.h | ||||
| 		Behaviors/BuildingBehavior.h | ||||
| 		Behaviors/GatherArmyBehavior.h | ||||
| 		VCAI.h | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,7 @@ | ||||
| #include "../Behaviors/StartupBehavior.h" | ||||
| #include "../Behaviors/DefenceBehavior.h" | ||||
| #include "../Behaviors/BuildingBehavior.h" | ||||
| #include "../Behaviors/GatherArmyBehavior.h" | ||||
| #include "../Goals/Invalid.h" | ||||
|  | ||||
| extern boost::thread_specific_ptr<CCallback> cb; | ||||
| @@ -117,7 +118,8 @@ void Nullkiller::makeTurn() | ||||
| 			choseBestTask(std::make_shared<CaptureObjectsBehavior>()), | ||||
| 			choseBestTask(std::make_shared<RecruitHeroBehavior>()), | ||||
| 			choseBestTask(std::make_shared<DefenceBehavior>()), | ||||
| 			choseBestTask(std::make_shared<BuildingBehavior>()) | ||||
| 			choseBestTask(std::make_shared<BuildingBehavior>()), | ||||
| 			choseBestTask(std::make_shared<GatherArmyBehavior>()) | ||||
| 		}; | ||||
|  | ||||
| 		if(cb->getDate(Date::DAY) == 1) | ||||
|   | ||||
| @@ -447,7 +447,7 @@ public: | ||||
| 		evaluationContext.goldReward = getGoldReward(target, hero); | ||||
| 		evaluationContext.armyReward = getArmyReward(target, hero, army, checkGold); | ||||
| 		evaluationContext.skillReward = getSkillReward(target, hero, evaluationContext.heroRole); | ||||
| 		evaluationContext.strategicalValue = getStrategicalValue(target); | ||||
| 		evaluationContext.strategicalValue += getStrategicalValue(target); | ||||
| 		evaluationContext.goldCost = getGoldCost(target, hero, army); | ||||
| 		evaluationContext.turn = chain.getPath().turn(); | ||||
|  | ||||
|   | ||||
| @@ -186,3 +186,22 @@ float AbstractGoal::accept(FuzzyHelper * f) | ||||
| { | ||||
| 	return f->evaluate(*this); | ||||
| } | ||||
|  | ||||
| EvaluationContext::EvaluationContext() | ||||
| 	: movementCost(0.0), | ||||
| 	manaCost(0), | ||||
| 	danger(0), | ||||
| 	closestWayRatio(1), | ||||
| 	armyLoss(0), | ||||
| 	heroStrength(0), | ||||
| 	movementCostByRole(), | ||||
| 	skillReward(0), | ||||
| 	goldReward(0), | ||||
| 	goldCost(0), | ||||
| 	armyReward(0), | ||||
| 	armyLossPersentage(0), | ||||
| 	heroRole(HeroRole::SCOUT), | ||||
| 	turn(0), | ||||
| 	strategicalValue(0) | ||||
| { | ||||
| } | ||||
| @@ -110,23 +110,7 @@ namespace Goals | ||||
| 		HeroRole heroRole; | ||||
| 		uint8_t turn; | ||||
|  | ||||
| 		EvaluationContext() | ||||
| 			: movementCost(0.0), | ||||
| 			manaCost(0), | ||||
| 			danger(0), | ||||
| 			closestWayRatio(1), | ||||
| 			armyLoss(0), | ||||
| 			heroStrength(0), | ||||
| 			movementCostByRole(), | ||||
| 			skillReward(0), | ||||
| 			goldReward(0), | ||||
| 			goldCost(0), | ||||
| 			armyReward(0), | ||||
| 			armyLossPersentage(0), | ||||
| 			heroRole(HeroRole::SCOUT), | ||||
| 			turn(0) | ||||
| 		{ | ||||
| 		} | ||||
| 		EvaluationContext(); | ||||
| 	}; | ||||
|  | ||||
| 	class DLL_EXPORT AbstractGoal | ||||
|   | ||||
| @@ -96,7 +96,16 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 					{ | ||||
| 						auto specialGoal = node.specialAction->whatToDo(hero); | ||||
|  | ||||
| 						specialGoal->accept(ai); | ||||
| 						try | ||||
| 						{ | ||||
| 							specialGoal->accept(ai); | ||||
| 						} | ||||
| 						catch(cannotFulfillGoalException e) | ||||
| 						{ | ||||
| 							logAi->warn("Can not complete %s because of an exception: %s", specialGoal->name(), e.what()); | ||||
|  | ||||
| 							throw; | ||||
| 						} | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| @@ -104,7 +113,7 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				if(node.turns == 0) | ||||
| 				if(node.turns == 0 && node.coord != hero->visitablePos()) | ||||
| 				{ | ||||
| 					auto targetNode = cb->getPathsInfo(hero.get())->getPathInfo(node.coord); | ||||
|  | ||||
| @@ -146,6 +155,9 @@ void ExecuteHeroChain::accept(VCAI * ai) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if(node.coord == hero->visitablePos()) | ||||
| 				continue; | ||||
|  | ||||
| 			if(node.turns == 0) | ||||
| 			{ | ||||
| 				logAi->error( | ||||
|   | ||||
		Reference in New Issue
	
	Block a user