1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

TBB for battle AI spellcast an fixes

This commit is contained in:
Andrii Danylchenko 2023-08-13 13:56:04 +03:00
parent 274bf739b8
commit 03395a3d8a
5 changed files with 92 additions and 89 deletions

View File

@ -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())

View File

@ -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);
if(needFullEval || !cachedAttack)
{
PotentialTargets innerTargets(activeStack, damageCache, state);
BattleExchangeEvaluator innerEvaluator(state, env);
if(!innerTargets.possibleAttacks.empty())
{
innerEvaluator.updateReachabilityMap(state);
auto newStackAction = innerEvaluator.findBestTarget(activeStack, innerTargets, innerCache, state);
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()];
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>;
EvalRunner::Tasks tasks;
for(PossibleSpellcast & psc : possibleCasts)
tasks.push_back(std::bind(evaluateSpellcast, &psc, _1));
uint32_t threadCount = boost::thread::hardware_concurrency();
if(threadCount == 0)
{
logGlobal->warn("No information of CPU cores available");
threadCount = 1;
}
CStopWatch timer;
std::vector<std::shared_ptr<ScriptsCache>> scriptsPool;
tbb::parallel_for(tbb::blocked_range<size_t>(0, possibleCasts.size()), [&](const tbb::blocked_range<size_t> & r)
{
for(auto i = r.begin(); i != r.end(); i++)
{
auto & ps = possibleCasts[i];
auto state = std::make_shared<HypotheticBattle>(env.get(), cb);
for(uint32_t idx = 0; idx < threadCount; idx++)
{
scriptsPool.emplace_back();
}
spells::BattleCast cast(state.get(), hero, spells::Mode::HERO, ps.spell);
cast.castEval(state->getServerCallback(), ps.dest);
EvalRunner runner(&tasks, scriptsPool);
runner.run();
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);
if(needFullEval || !cachedAttack)
{
PotentialTargets innerTargets(activeStack, damageCache, state);
BattleExchangeEvaluator innerEvaluator(state, env);
if(!innerTargets.possibleAttacks.empty())
{
innerEvaluator.updateReachabilityMap(state);
auto newStackAction = innerEvaluator.findBestTarget(activeStack, innerTargets, innerCache, state);
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()];
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;
}
}
}
});
LOGFL("Evaluation took %d ms", timer.getDiff());

View File

@ -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()

View File

@ -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;

View File

@ -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();