浏览代码

Nullkiller: defence behavior

Andrii Danylchenko 4 年之前
父节点
当前提交
a2ac19e4ec

+ 177 - 0
AI/Nullkiller/Behaviors/DefenceBehavior.cpp

@@ -0,0 +1,177 @@
+/*
+* DefenceBehavior.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 "DefenceBehavior.h"
+#include "../VCAI.h"
+#include "../Engine/Nullkiller.h"
+#include "../AIhelper.h"
+#include "../AIUtility.h"
+#include "../Goals/BuyArmy.h"
+#include "../Goals/VisitTile.h"
+#include "../Goals/ExecuteHeroChain.h"
+#include "../Goals/ExchangeSwapTownHeroes.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;
+
+std::string DefenceBehavior::toString() const
+{
+	return "Defend towns";
+}
+
+Goals::TGoalVec DefenceBehavior::getTasks()
+{
+	Goals::TGoalVec tasks;
+		
+	auto heroes = cb->getHeroesInfo();
+
+	if(heroes.size())
+	{
+		auto mainArmy = *vstd::maxElementByFun(heroes, [](const CGHeroInstance * hero) -> uint64_t
+		{
+			return hero->getTotalStrength();
+		});
+
+		for(auto town : cb->getTownsInfo())
+		{
+			evaluateDefence(tasks, town);
+		}
+	}
+
+	return tasks;
+}
+
+void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town)
+{
+	logAi->debug("Evaluating defence for %s", town->name);
+
+	auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
+	auto treats = { treatNode.fastestDanger, treatNode.maximumDanger };
+
+	if(town->garrisonHero)
+	{
+		if(ai->nullkiller->isActive(town->garrisonHero.get()))
+		{
+			tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, nullptr).setpriority(5)));
+			return;
+		}
+
+		logAi->debug(
+			"Hero %s in garrison of town %s is suposed to defend the town",
+			town->name,
+			town->garrisonHero->name);
+
+		return;
+	}
+
+	if(town->visitingHero && isSafeToVisit(town->visitingHero.get(), treatNode.maximumDanger.danger))
+	{
+		logAi->debug(
+			"Town %s has visiting hero %s who is strong enough to defend the town", 
+			town->name, 
+			town->visitingHero->name);
+
+		return;
+	}
+
+	uint64_t reinforcement = ai->ah->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
+
+	if(reinforcement)
+	{
+		logAi->debug("Town %s can buy defence army %lld", town->name, reinforcement);
+		tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f)));
+	}
+
+	auto paths = ai->ah->getPathsToTile(town->visitablePos());
+
+	if(paths.empty())
+	{
+		logAi->debug("No ways to defend town %s", town->name);
+
+		return;
+	}
+
+	for(AIPath & path : paths)
+	{
+		for(auto & treat : treats)
+		{
+			if(path.turn() < treat.turn && isSafeToVisit(path.targetHero, path.heroArmy, treat.danger))
+			{
+				logAi->debug("Town %s is not in danger.", town->name);
+
+				return;
+			}
+		}
+	}
+
+	for(auto & treat : treats)
+	{
+		logAi->debug(
+			"Town %s has treat %lld in %s turns, hero: %s", 
+			town->name,
+			treat.danger,
+			std::to_string(treat.turn),
+			treat.hero->name);
+
+		for(AIPath & path : paths)
+		{
+#if AI_TRACE_LEVEL >= 1
+			logAi->trace(
+				"Hero %s can defend town with force %lld in %s turns, path: %s",
+				path.targetHero->name,
+				path.getHeroStrength(),
+				std::to_string(path.turn()),
+				path.toString());
+#endif
+
+			float priority = 0.6f + (float)path.getHeroStrength() / treat.danger / (treat.turn + 1);
+
+			if(path.targetHero == town->visitingHero && path.exchangeCount == 1)
+			{
+#if AI_TRACE_LEVEL >= 1
+				logAi->trace("Put %s to garrison of town %s with priority %f", 
+					path.targetHero->name,
+					town->name,
+					priority);
+#endif
+
+				tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, town->visitingHero.get()).setpriority(priority)));
+			}
+			else if(path.getHeroStrength() * SAFE_ATTACK_CONSTANT >= treat.danger)
+			{
+#if AI_TRACE_LEVEL >= 1
+				logAi->trace("Move %s to defend town %s with priority %f",
+					path.targetHero->name,
+					town->name,
+					priority);
+#endif
+
+				tasks.push_back(Goals::sptr(Goals::ExecuteHeroChain(path, town).setpriority(priority)));
+			}
+		}
+	}
+
+	logAi->debug("Found %d tasks", tasks.size());
+
+	/*for(auto & treat : treats)
+	{
+		auto paths = ai->ah->getPathsToTile(treat.hero->visitablePos());
+
+		for(AIPath & path : paths)
+		{
+			tasks.push_back(Goals::sptr(Goals::ExecuteHeroChain(path)));
+		}
+	}*/
+}

+ 29 - 0
AI/Nullkiller/Behaviors/DefenceBehavior.h

@@ -0,0 +1,29 @@
+/*
+* BuyArmyBehavior.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 DefenceBehavior : public Behavior
+{
+public:
+	DefenceBehavior()
+	{
+	}
+
+	virtual Goals::TGoalVec getTasks() override;
+	virtual std::string toString() const override;
+
+private:
+	void evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town);
+};
+

+ 2 - 0
AI/Nullkiller/CMakeLists.txt

@@ -55,6 +55,7 @@ set(VCAI_SRCS
 		Behaviors/CaptureObjectsBehavior.cpp
 		Behaviors/RecruitHeroBehavior.cpp
 		Behaviors/BuyArmyBehavior.cpp
+		Behaviors/DefenceBehavior.cpp
 		Behaviors/StartupBehavior.cpp
 		main.cpp
 		VCAI.cpp
@@ -121,6 +122,7 @@ set(VCAI_HEADERS
 		Behaviors/CaptureObjectsBehavior.h
 		Behaviors/RecruitHeroBehavior.h
 		Behaviors/BuyArmyBehavior.h
+		Behaviors/DefenceBehavior.h
 		Behaviors/StartupBehavior.h
 		VCAI.h
 )

+ 13 - 1
AI/Nullkiller/Engine/DangerHitMapAnalyzer.cpp

@@ -9,6 +9,7 @@
 */
 #include "StdInc.h"
 #include "DangerHitMapAnalyzer.h"
+#include "lib/mapping/CMap.h" //for victory conditions
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
@@ -38,7 +39,8 @@ void DangerHitMapAnalyzer::updateHitMap()
 	{
 		ai->ah->updatePaths(pair.second, false);
 
-		foreach_tile_pos([&](const int3 & pos){
+		foreach_tile_pos([&](const int3 & pos)
+		{
 			for(AIPath & path : ai->ah->getPathsToTile(pos))
 			{
 				auto tileDanger = path.getHeroStrength();
@@ -50,6 +52,7 @@ void DangerHitMapAnalyzer::updateHitMap()
 				{
 					node.maximumDanger.danger = tileDanger;
 					node.maximumDanger.turn = turn;
+					node.maximumDanger.hero = path.targetHero;
 				}
 
 				if(turn < node.fastestDanger.turn
@@ -57,6 +60,7 @@ void DangerHitMapAnalyzer::updateHitMap()
 				{
 					node.fastestDanger.danger = tileDanger;
 					node.fastestDanger.turn = turn;
+					node.fastestDanger.hero = path.targetHero;
 				}
 			}
 		});
@@ -72,3 +76,11 @@ uint64_t DangerHitMapAnalyzer::enemyCanKillOurHeroesAlongThePath(const AIPath &
 	return info.fastestDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.fastestDanger.danger)
 		|| info.maximumDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.maximumDanger.danger);
 }
+
+const HitMapNode & DangerHitMapAnalyzer::getObjectTreat(const CGObjectInstance * town) const
+{
+	auto tile = town->visitablePos();
+	const HitMapNode & info = hitMap[tile.x][tile.y][tile.z];
+
+	return info;
+}

+ 4 - 0
AI/Nullkiller/Engine/DangerHitMapAnalyzer.h

@@ -16,11 +16,13 @@ struct HitMapInfo
 {
 	uint64_t danger;
 	uint8_t turn;
+	const CGHeroInstance * hero;
 
 	void reset()
 	{
 		danger = 0;
 		turn = 255;
+		hero = nullptr;
 	}
 };
 
@@ -40,8 +42,10 @@ class DangerHitMapAnalyzer
 {
 private:
 	boost::multi_array<HitMapNode, 3> hitMap;
+	std::map<const CGHeroInstance *, int> enemyHeroTreatMAp;
 
 public:
 	void updateHitMap();
 	uint64_t enemyCanKillOurHeroesAlongThePath(const AIPath & path) const;
+	const HitMapNode & getObjectTreat(const CGObjectInstance * town) const;
 };

+ 3 - 1
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -15,6 +15,7 @@
 #include "../Behaviors/RecruitHeroBehavior.h"
 #include "../Behaviors/BuyArmyBehavior.h"
 #include "../Behaviors/StartupBehavior.h"
+#include "../Behaviors/DefenceBehavior.h"
 #include "../Goals/Invalid.h"
 
 extern boost::thread_specific_ptr<CCallback> cb;
@@ -95,7 +96,8 @@ void Nullkiller::makeTurn()
 		Goals::TGoalVec bestTasks = {
 			choseBestTask(std::make_shared<BuyArmyBehavior>()),
 			choseBestTask(std::make_shared<CaptureObjectsBehavior>()),
-			choseBestTask(std::make_shared<RecruitHeroBehavior>())
+			choseBestTask(std::make_shared<RecruitHeroBehavior>()),
+			choseBestTask(std::make_shared<DefenceBehavior>())
 		};
 
 		if(cb->getDate(Date::DAY) == 1)

+ 9 - 1
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -484,9 +484,17 @@ void AINodeStorage::setHeroes(std::vector<HeroPtr> heroes, const VCAI * _ai)
 	for(auto & hero : heroes)
 	{
 		uint64_t mask = 1 << actors.size();
+		auto actor = std::make_shared<HeroActor>(hero.get(), mask, ai);
+
+		if(hero->tempOwner != ai->playerID)
+		{
+			bool onLand = !actor->hero->boat;
+			actor->initialMovement = actor->hero->maxMovePoints(onLand);
+		}
 
 		playerID = hero->tempOwner;
-		actors.push_back(std::make_shared<HeroActor>(hero.get(), mask, ai));
+
+		actors.push_back(actor);
 	}
 }
 

+ 1 - 0
AI/Nullkiller/Pathfinding/AINodeStorage.h

@@ -11,6 +11,7 @@
 #pragma once
 
 #define VCMI_TRACE_PATHFINDER 1
+#define AI_TRACE_LEVEL 1
 
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/mapObjects/CGHeroInstance.h"