소스 검색

TBB for battle AI spellcast an fixes

Andrii Danylchenko 2 년 전
부모
커밋
03395a3d8a
5개의 변경된 파일82개의 추가작업 그리고 79개의 파일을 삭제
  1. 6 1
      AI/BattleAI/AttackPossibility.cpp
  2. 51 76
      AI/BattleAI/BattleAI.cpp
  3. 5 1
      AI/BattleAI/CMakeLists.txt
  4. 18 1
      AI/BattleAI/StackWithBonuses.cpp
  5. 2 0
      AI/BattleAI/StackWithBonuses.h

+ 6 - 1
AI/BattleAI/AttackPossibility.cpp

@@ -132,7 +132,12 @@ int64_t AttackPossibility::calculateDamageReduce(
 	{
 		auto ourUnits = state->battleGetUnitsIf([&](const battle::Unit * u) -> bool
 			{
-				return u->unitSide() != defender->unitSide() && !u->isTurret();
+				return u->unitSide() != defender->unitSide()
+					&& !u->isTurret()
+					&& u->creatureId() != CreatureID::CATAPULT
+					&& u->creatureId() != CreatureID::BALLISTA
+					&& u->creatureId() != CreatureID::FIRST_AID_TENT
+					&& u->getCount();
 			});
 
 		if(ourUnits.empty())

+ 51 - 76
AI/BattleAI/BattleAI.cpp

@@ -13,6 +13,7 @@
 
 #include "StackWithBonuses.h"
 #include "EnemyInfo.h"
+#include "tbb/parallel_for.h"
 #include "../../lib/CStopWatch.h"
 #include "../../lib/CThreadHelper.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
@@ -704,96 +705,70 @@ void BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
 		}
 	}
 
-	struct ScriptsCache
-	{
-		//todo: re-implement scripts context cache
-	};
-
-	auto evaluateSpellcast = [&] (PossibleSpellcast * ps, std::shared_ptr<ScriptsCache>)
-	{
-		auto state = std::make_shared<HypotheticBattle>(env.get(), cb);
-
-		spells::BattleCast cast(state.get(), hero, spells::Mode::HERO, ps->spell);
-		cast.castEval(state->getServerCallback(), ps->dest);
-
-		auto allUnits = state->battleGetUnitsIf([](const battle::Unit * u) -> bool{ return true; });
-
-		auto needFullEval = vstd::contains_if(allUnits, [&](const battle::Unit * u) -> bool
-			{
-				auto original = cb->battleGetUnitByID(u->unitId());
-				return  !original || u->speed() != original->speed();
-			});
-
-		DamageCache innerCache(&damageCache);
-		innerCache.buildDamageCache(state, side);
+	CStopWatch timer;
 
-		if(needFullEval || !cachedAttack)
+	tbb::parallel_for(tbb::blocked_range<size_t>(0, possibleCasts.size()), [&](const tbb::blocked_range<size_t> & r)
 		{
-			PotentialTargets innerTargets(activeStack, damageCache, state);
-			BattleExchangeEvaluator innerEvaluator(state, env);
-
-			if(!innerTargets.possibleAttacks.empty())
+			for(auto i = r.begin(); i != r.end(); i++)
 			{
-				innerEvaluator.updateReachabilityMap(state);
+				auto & ps = possibleCasts[i];
+				auto state = std::make_shared<HypotheticBattle>(env.get(), cb);
 
-				auto newStackAction = innerEvaluator.findBestTarget(activeStack, innerTargets, innerCache, state);
+				spells::BattleCast cast(state.get(), hero, spells::Mode::HERO, ps.spell);
+				cast.castEval(state->getServerCallback(), ps.dest);
 
-				ps->value = newStackAction.score;
-			}
-			else
-			{
-				ps->value = 0;
-			}
-		}
-		else
-		{
-			ps->value = scoreEvaluator.calculateExchange(*cachedAttack, *targets, innerCache, state);
-		}
-
-		for(auto unit : allUnits)
-		{
-			auto newHealth = unit->getAvailableHealth();
-			auto oldHealth = healthOfStack[unit->unitId()];
+				auto allUnits = state->battleGetUnitsIf([](const battle::Unit * u) -> bool { return true; });
 
-			if(oldHealth != newHealth)
-			{
-				auto damage = std::abs(oldHealth - newHealth);
-				auto originalDefender = cb->battleGetUnitByID(unit->unitId());
-				auto dpsReduce = AttackPossibility::calculateDamageReduce(nullptr, originalDefender ? originalDefender : unit, damage, innerCache, state);
-				auto ourUnit = unit->unitSide() == side ? 1 : -1;
-				auto goodEffect = newHealth > oldHealth ? 1 : -1;
-
-				ps->value += ourUnit * goodEffect * dpsReduce;
-			}
-		}
-	};
-
-	using EvalRunner = ThreadPool<ScriptsCache>;
+				auto needFullEval = vstd::contains_if(allUnits, [&](const battle::Unit * u) -> bool
+					{
+						auto original = cb->battleGetUnitByID(u->unitId());
+						return  !original || u->speed() != original->speed();
+					});
 
-	EvalRunner::Tasks tasks;
+				DamageCache innerCache(&damageCache);
+				innerCache.buildDamageCache(state, side);
 
-	for(PossibleSpellcast & psc : possibleCasts)
-		tasks.push_back(std::bind(evaluateSpellcast, &psc, _1));
+				if(needFullEval || !cachedAttack)
+				{
+					PotentialTargets innerTargets(activeStack, damageCache, state);
+					BattleExchangeEvaluator innerEvaluator(state, env);
 
-	uint32_t threadCount = boost::thread::hardware_concurrency();
+					if(!innerTargets.possibleAttacks.empty())
+					{
+						innerEvaluator.updateReachabilityMap(state);
 
-	if(threadCount == 0)
-	{
-		logGlobal->warn("No information of CPU cores available");
-		threadCount = 1;
-	}
+						auto newStackAction = innerEvaluator.findBestTarget(activeStack, innerTargets, innerCache, state);
 
-	CStopWatch timer;
+						ps.value = newStackAction.score;
+					}
+					else
+					{
+						ps.value = 0;
+					}
+				}
+				else
+				{
+					ps.value = scoreEvaluator.calculateExchange(*cachedAttack, *targets, innerCache, state);
+				}
 
-	std::vector<std::shared_ptr<ScriptsCache>> scriptsPool;
+				for(auto unit : allUnits)
+				{
+					auto newHealth = unit->getAvailableHealth();
+					auto oldHealth = healthOfStack[unit->unitId()];
 
-	for(uint32_t idx = 0; idx < threadCount; idx++)
-	{
-		scriptsPool.emplace_back();
-	}
+					if(oldHealth != newHealth)
+					{
+						auto damage = std::abs(oldHealth - newHealth);
+						auto originalDefender = cb->battleGetUnitByID(unit->unitId());
+						auto dpsReduce = AttackPossibility::calculateDamageReduce(nullptr, originalDefender ? originalDefender : unit, damage, innerCache, state);
+						auto ourUnit = unit->unitSide() == side ? 1 : -1;
+						auto goodEffect = newHealth > oldHealth ? 1 : -1;
 
-	EvalRunner runner(&tasks, scriptsPool);
-	runner.run();
+						ps.value += ourUnit * goodEffect * dpsReduce;
+					}
+				}
+			}
+		});
 
 	LOGFL("Evaluation took %d ms", timer.getDiff());
 

+ 5 - 1
AI/BattleAI/CMakeLists.txt

@@ -37,7 +37,11 @@ else()
 endif()
 
 target_include_directories(BattleAI PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-target_link_libraries(BattleAI PRIVATE ${VCMI_LIB_TARGET})
+target_link_libraries(BattleAI PRIVATE ${VCMI_LIB_TARGET} TBB::tbb)
 
 vcmi_set_output_dir(BattleAI "AI")
 enable_pch(BattleAI)
+
+if(APPLE_IOS AND NOT USING_CONAN)
+	install(IMPORTED_RUNTIME_ARTIFACTS TBB::tbb LIBRARY DESTINATION ${LIB_DIR}) # CMake 3.21+
+endif()

+ 18 - 1
AI/BattleAI/StackWithBonuses.cpp

@@ -52,6 +52,23 @@ StackWithBonuses::StackWithBonuses(const HypotheticBattle * Owner, const battle:
 	battle::CUnitState::operator=(*Stack);
 }
 
+StackWithBonuses::StackWithBonuses(const HypotheticBattle * Owner, const battle::Unit * Stack)
+	: battle::CUnitState(),
+	origBearer(Stack->getBonusBearer()),
+	owner(Owner),
+	type(Stack->unitType()),
+	baseAmount(Stack->unitBaseAmount()),
+	id(Stack->unitId()),
+	side(Stack->unitSide()),
+	player(Stack->unitOwner()),
+	slot(Stack->unitSlot())
+{
+	localInit(Owner);
+
+	auto state = Stack->acquireState();
+	battle::CUnitState::operator=(*state);
+}
+
 StackWithBonuses::StackWithBonuses(const HypotheticBattle * Owner, const battle::UnitInfo & info)
 	: battle::CUnitState(),
 	origBearer(nullptr),
@@ -265,7 +282,7 @@ std::shared_ptr<StackWithBonuses> HypotheticBattle::getForUpdate(uint32_t id)
 
 	if(iter == stackStates.end())
 	{
-		const CStack * s = subject->battleGetStackByID(id, false);
+		const battle::Unit * s = subject->battleGetUnitByID(id);
 
 		auto ret = std::make_shared<StackWithBonuses>(this, s);
 		stackStates[id] = ret;

+ 2 - 0
AI/BattleAI/StackWithBonuses.h

@@ -51,6 +51,8 @@ public:
 
 	StackWithBonuses(const HypotheticBattle * Owner, const battle::CUnitState * Stack);
 
+	StackWithBonuses(const HypotheticBattle * Owner, const battle::Unit * Stack);
+
 	StackWithBonuses(const HypotheticBattle * Owner, const battle::UnitInfo & info);
 
 	virtual ~StackWithBonuses();