mirror of
https://github.com/vcmi/vcmi.git
synced 2026-05-22 09:55:17 +02:00
Battle AI: fix firewall, fix haste spellcast evaluation for waits and movements, allow location spells
This commit is contained in:
@@ -66,7 +66,6 @@ BattleEvaluator::BattleEvaluator(
|
||||
damageCache.buildDamageCache(hb, side);
|
||||
|
||||
targets = std::make_unique<PotentialTargets>(activeStack, damageCache, hb);
|
||||
cachedScore = EvaluationResult::INEFFECTIVE_SCORE;
|
||||
}
|
||||
|
||||
BattleEvaluator::BattleEvaluator(
|
||||
@@ -85,7 +84,6 @@ BattleEvaluator::BattleEvaluator(
|
||||
damageCache(damageCache), strengthRatio(strengthRatio), battleID(battleID), simulationTurnsCount(simulationTurnsCount)
|
||||
{
|
||||
targets = std::make_unique<PotentialTargets>(activeStack, damageCache, hb);
|
||||
cachedScore = EvaluationResult::INEFFECTIVE_SCORE;
|
||||
}
|
||||
|
||||
std::vector<BattleHex> BattleEvaluator::getBrokenWallMoatHexes() const
|
||||
@@ -178,8 +176,10 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
|
||||
auto evaluationResult = scoreEvaluator.findBestTarget(stack, *targets, damageCache, hb);
|
||||
auto & bestAttack = evaluationResult.bestAttack;
|
||||
|
||||
cachedAttack = bestAttack;
|
||||
cachedScore = evaluationResult.score;
|
||||
cachedAttack.ap = bestAttack;
|
||||
cachedAttack.score = evaluationResult.score;
|
||||
cachedAttack.turn = 0;
|
||||
cachedAttack.waited = evaluationResult.wait;
|
||||
|
||||
//TODO: consider more complex spellcast evaluation, f.e. because "re-retaliation" during enemy move in same turn for melee attack etc.
|
||||
if(bestSpellcast.has_value() && bestSpellcast->value > bestAttack.damageDiff())
|
||||
@@ -239,8 +239,9 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
|
||||
if(moveTarget.score > score)
|
||||
{
|
||||
score = moveTarget.score;
|
||||
cachedAttack = moveTarget.cachedAttack;
|
||||
cachedScore = score;
|
||||
cachedAttack.ap = moveTarget.cachedAttack;
|
||||
cachedAttack.score = score;
|
||||
cachedAttack.turn = moveTarget.turnsToRich;
|
||||
|
||||
if(stack->waited())
|
||||
{
|
||||
@@ -255,6 +256,8 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
|
||||
}
|
||||
else
|
||||
{
|
||||
cachedAttack.waited = true;
|
||||
|
||||
return BattleAction::makeWait(stack);
|
||||
}
|
||||
}
|
||||
@@ -448,7 +451,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
|
||||
|
||||
vstd::erase_if(possibleSpells, [](const CSpell *s)
|
||||
{
|
||||
return spellType(s) != SpellTypes::BATTLE || s->getTargetType() == spells::AimType::LOCATION;
|
||||
return spellType(s) != SpellTypes::BATTLE;
|
||||
});
|
||||
|
||||
LOGFL("I know how %d of them works.", possibleSpells.size());
|
||||
@@ -459,9 +462,6 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
|
||||
{
|
||||
spells::BattleCast temp(cb->getBattle(battleID).get(), hero, spells::Mode::HERO, spell);
|
||||
|
||||
if(spell->getTargetType() == spells::AimType::LOCATION)
|
||||
continue;
|
||||
|
||||
const bool FAST = true;
|
||||
|
||||
for(auto & target : temp.findPotentialTargets(FAST))
|
||||
@@ -648,9 +648,15 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
|
||||
|
||||
DamageCache safeCopy = damageCache;
|
||||
DamageCache innerCache(&safeCopy);
|
||||
|
||||
innerCache.buildDamageCache(state, side);
|
||||
|
||||
if(needFullEval || !cachedAttack)
|
||||
if(cachedAttack.ap && cachedAttack.waited)
|
||||
{
|
||||
state->makeWait(activeStack);
|
||||
}
|
||||
|
||||
if(needFullEval || !cachedAttack.ap)
|
||||
{
|
||||
#if BATTLE_TRACE_LEVEL >= 1
|
||||
logAi->trace("Full evaluation is started due to stack speed affected.");
|
||||
@@ -659,29 +665,31 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
|
||||
PotentialTargets innerTargets(activeStack, innerCache, state);
|
||||
BattleExchangeEvaluator innerEvaluator(state, env, strengthRatio, simulationTurnsCount);
|
||||
|
||||
innerEvaluator.updateReachabilityMap(state);
|
||||
|
||||
auto moveTarget = scoreEvaluator.findMoveTowardsUnreachable(activeStack, innerTargets, innerCache, state);
|
||||
|
||||
if(!innerTargets.possibleAttacks.empty())
|
||||
{
|
||||
innerEvaluator.updateReachabilityMap(state);
|
||||
|
||||
auto newStackAction = innerEvaluator.findBestTarget(activeStack, innerTargets, innerCache, state);
|
||||
|
||||
ps.value = newStackAction.score;
|
||||
ps.value = std::max(moveTarget.score, newStackAction.score);
|
||||
}
|
||||
else
|
||||
{
|
||||
ps.value = 0;
|
||||
ps.value = moveTarget.score;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ps.value = scoreEvaluator.evaluateExchange(*cachedAttack, 0, *targets, innerCache, state);
|
||||
ps.value = scoreEvaluator.evaluateExchange(*cachedAttack.ap, cachedAttack.turn, *targets, innerCache, state);
|
||||
}
|
||||
|
||||
for(const auto & unit : allUnits)
|
||||
{
|
||||
if (!unit->isValidTarget())
|
||||
if(!unit->isValidTarget())
|
||||
continue;
|
||||
|
||||
|
||||
auto newHealth = unit->getAvailableHealth();
|
||||
auto oldHealth = vstd::find_or(healthOfStack, unit->unitId(), 0); // old health value may not exist for newly summoned units
|
||||
|
||||
@@ -692,7 +700,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
|
||||
|
||||
auto dpsReduce = AttackPossibility::calculateDamageReduce(
|
||||
nullptr,
|
||||
originalDefender && originalDefender->alive() ? originalDefender : unit,
|
||||
originalDefender && originalDefender->alive() ? originalDefender : unit,
|
||||
damage,
|
||||
innerCache,
|
||||
state);
|
||||
@@ -719,6 +727,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if BATTLE_TRACE_LEVEL >= 1
|
||||
logAi->trace("Total score: %2f", ps.value);
|
||||
#endif
|
||||
@@ -729,13 +738,12 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
|
||||
|
||||
LOGFL("Evaluation took %d ms", timer.getDiff());
|
||||
|
||||
auto pscValue = [](const PossibleSpellcast &ps) -> float
|
||||
{
|
||||
return ps.value;
|
||||
};
|
||||
auto castToPerform = *vstd::maxElementByFun(possibleCasts, pscValue);
|
||||
auto castToPerform = *vstd::maxElementByFun(possibleCasts, [](const PossibleSpellcast & ps) -> float
|
||||
{
|
||||
return ps.value;
|
||||
});
|
||||
|
||||
if(castToPerform.value > cachedScore)
|
||||
if(castToPerform.value > cachedAttack.score && !vstd::isAlmostEqual(castToPerform.value, cachedAttack.score))
|
||||
{
|
||||
LOGFL("Best spell is %s (value %d). Will cast.", castToPerform.spell->getNameTranslated() % castToPerform.value);
|
||||
BattleAction spellcast;
|
||||
|
||||
Reference in New Issue
Block a user