diff --git a/AI/BattleAI/AttackPossibility.cpp b/AI/BattleAI/AttackPossibility.cpp index 4a4de7e46..496f73414 100644 --- a/AI/BattleAI/AttackPossibility.cpp +++ b/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()) diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 8da28e145..d5e59b6d7 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/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) - { - auto state = std::make_shared(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; - - 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> scriptsPool; + tbb::parallel_for(tbb::blocked_range(0, possibleCasts.size()), [&](const tbb::blocked_range & r) + { + for(auto i = r.begin(); i != r.end(); i++) + { + auto & ps = possibleCasts[i]; + auto state = std::make_shared(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()); diff --git a/AI/BattleAI/CMakeLists.txt b/AI/BattleAI/CMakeLists.txt index 7feed93e2..1850e24f1 100644 --- a/AI/BattleAI/CMakeLists.txt +++ b/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() diff --git a/AI/BattleAI/StackWithBonuses.cpp b/AI/BattleAI/StackWithBonuses.cpp index 3311e8f90..c77e2b4fc 100644 --- a/AI/BattleAI/StackWithBonuses.cpp +++ b/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 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(this, s); stackStates[id] = ret; diff --git a/AI/BattleAI/StackWithBonuses.h b/AI/BattleAI/StackWithBonuses.h index 024cb4d76..c7692434e 100644 --- a/AI/BattleAI/StackWithBonuses.h +++ b/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();