1
0
mirror of https://github.com/vcmi/vcmi.git synced 2026-05-22 09:55:17 +02:00

Merge remote-tracking branch 'upstream/develop' into develop

This commit is contained in:
Xilmi
2024-08-24 12:25:03 +02:00
62 changed files with 1594 additions and 355 deletions
+101 -44
View File
@@ -49,6 +49,45 @@ SpellTypes spellType(const CSpell * spell)
return SpellTypes::OTHER;
}
BattleEvaluator::BattleEvaluator(
std::shared_ptr<Environment> env,
std::shared_ptr<CBattleCallback> cb,
const battle::Unit * activeStack,
PlayerColor playerID,
BattleID battleID,
BattleSide side,
float strengthRatio,
int simulationTurnsCount)
:scoreEvaluator(cb->getBattle(battleID), env, strengthRatio, simulationTurnsCount),
cachedAttack(), playerID(playerID), side(side), env(env),
cb(cb), strengthRatio(strengthRatio), battleID(battleID), simulationTurnsCount(simulationTurnsCount)
{
hb = std::make_shared<HypotheticBattle>(env.get(), cb->getBattle(battleID));
damageCache.buildDamageCache(hb, side);
targets = std::make_unique<PotentialTargets>(activeStack, damageCache, hb);
cachedScore = EvaluationResult::INEFFECTIVE_SCORE;
}
BattleEvaluator::BattleEvaluator(
std::shared_ptr<Environment> env,
std::shared_ptr<CBattleCallback> cb,
std::shared_ptr<HypotheticBattle> hb,
DamageCache & damageCache,
const battle::Unit * activeStack,
PlayerColor playerID,
BattleID battleID,
BattleSide side,
float strengthRatio,
int simulationTurnsCount)
:scoreEvaluator(cb->getBattle(battleID), env, strengthRatio, simulationTurnsCount),
cachedAttack(), playerID(playerID), side(side), env(env), cb(cb), hb(hb),
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
{
std::vector<BattleHex> result;
@@ -167,7 +206,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
score
);
if (moveTarget.scorePerTurn <= score)
if (moveTarget.score <= score)
{
if(evaluationResult.wait)
{
@@ -197,7 +236,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
}
//ThreatMap threatsToUs(stack); // These lines may be useful but they are't used in the code.
if(moveTarget.scorePerTurn > score)
if(moveTarget.score > score)
{
score = moveTarget.score;
cachedAttack = moveTarget.cachedAttack;
@@ -206,14 +245,13 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
if(stack->waited())
{
logAi->debug(
"Moving %s towards hex %s[%d], score: %2f/%2f",
"Moving %s towards hex %s[%d], score: %2f",
stack->getDescription(),
moveTarget.cachedAttack->attack.defender->getDescription(),
moveTarget.cachedAttack->attack.defender->getPosition().hex,
moveTarget.score,
moveTarget.scorePerTurn);
moveTarget.score);
return goTowardsNearest(stack, moveTarget.positions);
return goTowardsNearest(stack, moveTarget.positions, *targets);
}
else
{
@@ -235,7 +273,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
if(stack->doubleWide() && vstd::contains(brokenWallMoat, stack->getPosition()))
return BattleAction::makeMove(stack, stack->getPosition().cloneInDirection(BattleHex::RIGHT));
else
return goTowardsNearest(stack, brokenWallMoat);
return goTowardsNearest(stack, brokenWallMoat, *targets);
}
}
@@ -249,7 +287,32 @@ uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock>
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
}
BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector<BattleHex> hexes)
BattleAction BattleEvaluator::moveOrAttack(const CStack * stack, BattleHex hex, const PotentialTargets & targets)
{
auto additionalScore = 0;
std::optional<AttackPossibility> attackOnTheWay;
for(auto & target : targets.possibleAttacks)
{
if(!target.attack.shooting && target.from == hex && target.attackValue() > additionalScore)
{
additionalScore = target.attackValue();
attackOnTheWay = target;
}
}
if(attackOnTheWay)
{
activeActionMade = true;
return BattleAction::makeMeleeAttack(stack, attackOnTheWay->attack.defender->getPosition(), attackOnTheWay->from);
}
else
{
return BattleAction::makeMove(stack, hex);
}
}
BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector<BattleHex> hexes, const PotentialTargets & targets)
{
auto reachability = cb->getBattle(battleID)->getReachability(stack);
auto avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(reachability, stack, false);
@@ -261,49 +324,38 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
std::vector<BattleHex> targetHexes = hexes;
for(int i = 0; i < 5; i++)
{
std::sort(targetHexes.begin(), targetHexes.end(), [&](BattleHex h1, BattleHex h2) -> bool
{
return reachability.distances[h1] < reachability.distances[h2];
});
vstd::erase_if(targetHexes, [](const BattleHex & hex) { return !hex.isValid(); });
for(auto hex : targetHexes)
std::sort(targetHexes.begin(), targetHexes.end(), [&](BattleHex h1, BattleHex h2) -> bool
{
if(vstd::contains(avHexes, hex))
{
return BattleAction::makeMove(stack, hex);
}
if(stack->coversPos(hex))
{
logAi->warn("Warning: already standing on neighbouring tile!");
//We shouldn't even be here...
return BattleAction::makeDefend(stack);
}
}
if(reachability.distances[targetHexes.front()] <= GameConstants::BFIELD_SIZE)
{
break;
}
std::vector<BattleHex> copy = targetHexes;
for(auto hex : copy)
vstd::concatenate(targetHexes, hex.allNeighbouringTiles());
vstd::erase_if(targetHexes, [](const BattleHex & hex) {return !hex.isValid();});
vstd::removeDuplicates(targetHexes);
}
return reachability.distances[h1] < reachability.distances[h2];
});
BattleHex bestNeighbor = targetHexes.front();
if(reachability.distances[bestNeighbor] > GameConstants::BFIELD_SIZE)
{
logAi->trace("No richable hexes.");
return BattleAction::makeDefend(stack);
}
// this turn
for(auto hex : targetHexes)
{
if(vstd::contains(avHexes, hex))
{
return moveOrAttack(stack, hex, targets);
}
if(stack->coversPos(hex))
{
logAi->warn("Warning: already standing on neighbouring hex!");
//We shouldn't even be here...
return BattleAction::makeDefend(stack);
}
}
// not this turn
scoreEvaluator.updateReachabilityMap(hb);
if(stack->hasBonusOfType(BonusType::FLYING))
@@ -343,7 +395,7 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
return scoreEvaluator.checkPositionBlocksOurStacks(*hb, stack, hex) ? BLOCKED_STACK_PENALTY + distance : distance;
});
return BattleAction::makeMove(stack, *nearestAvailableHex);
return moveOrAttack(stack, *nearestAvailableHex, targets);
}
else
{
@@ -357,11 +409,16 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
if(vstd::contains(avHexes, currentDest)
&& !scoreEvaluator.checkPositionBlocksOurStacks(*hb, stack, currentDest))
return BattleAction::makeMove(stack, currentDest);
{
return moveOrAttack(stack, currentDest, targets);
}
currentDest = reachability.predecessors[currentDest];
}
}
logAi->error("We should either detect that hexes are unreachable or make a move!");
return BattleAction::makeDefend(stack);
}
bool BattleEvaluator::canCastSpell()
@@ -597,7 +654,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
#endif
PotentialTargets innerTargets(activeStack, innerCache, state);
BattleExchangeEvaluator innerEvaluator(state, env, strengthRatio);
BattleExchangeEvaluator innerEvaluator(state, env, strengthRatio, simulationTurnsCount);
if(!innerTargets.possibleAttacks.empty())
{