Skip to content

Commit

Permalink
Merge branch 'beta' into 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
IvanSavenko committed Jan 31, 2025
2 parents bbaa9c3 + 6d2e536 commit d3131ea
Show file tree
Hide file tree
Showing 408 changed files with 18,976 additions and 7,178 deletions.
34 changes: 18 additions & 16 deletions AI/BattleAI/AttackPossibility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void DamageCache::buildObstacleDamageCache(std::shared_ptr<HypotheticBattle> hb,

auto damageDealt = stack->getAvailableHealth() - updated->getAvailableHealth();

for(auto hex : affectedHexes)
for(const auto & hex : affectedHexes)
{
obstacleDamage[hex][stack->unitId()] = damageDealt;
}
Expand All @@ -92,8 +92,8 @@ void DamageCache::buildDamageCache(std::shared_ptr<HypotheticBattle> hb, BattleS
return u->isValidTarget();
});

std::vector<const battle::Unit *> ourUnits;
std::vector<const battle::Unit *> enemyUnits;
battle::Units ourUnits;
battle::Units enemyUnits;

for(auto stack : stacks)
{
Expand Down Expand Up @@ -129,7 +129,7 @@ int64_t DamageCache::getDamage(const battle::Unit * attacker, const battle::Unit
return damageCache[attacker->unitId()][defender->unitId()] * attacker->getCount();
}

int64_t DamageCache::getObstacleDamage(BattleHex hex, const battle::Unit * defender)
int64_t DamageCache::getObstacleDamage(const BattleHex & hex, const battle::Unit * defender)
{
if(parent)
return parent->getObstacleDamage(hex, defender);
Expand Down Expand Up @@ -166,7 +166,7 @@ int64_t DamageCache::getOriginalDamage(const battle::Unit * attacker, const batt
return getDamage(attacker, defender, hb);
}

AttackPossibility::AttackPossibility(BattleHex from, BattleHex dest, const BattleAttackInfo & attack)
AttackPossibility::AttackPossibility(const BattleHex & from, const BattleHex & dest, const BattleAttackInfo & attack)
: from(from), dest(dest), attack(attack)
{
this->attack.attackerPos = from;
Expand Down Expand Up @@ -280,8 +280,8 @@ int64_t AttackPossibility::evaluateBlockedShootersDmg(
std::set<uint32_t> checkedUnits;

auto attacker = attackInfo.attacker;
auto hexes = attacker->getSurroundingHexes(hex);
for(BattleHex tile : hexes)
const auto & hexes = attacker->getSurroundingHexes(hex);
for(const BattleHex & tile : hexes)
{
auto st = state->battleGetUnitByPos(tile, true);
if(!st || !state->battleMatchOwner(st, attacker))
Expand Down Expand Up @@ -326,13 +326,13 @@ AttackPossibility AttackPossibility::evaluate(

AttackPossibility bestAp(hex, BattleHex::INVALID, attackInfo);

std::vector<BattleHex> defenderHex;
BattleHexArray defenderHex;
if(attackInfo.shooting)
defenderHex.push_back(defender->getPosition());
defenderHex.insert(defender->getPosition());
else
defenderHex = CStack::meleeAttackHexes(attacker, defender, hex);

for(BattleHex defHex : defenderHex)
for(const BattleHex & defHex : defenderHex)
{
if(defHex == hex) // should be impossible but check anyway
continue;
Expand All @@ -346,9 +346,9 @@ AttackPossibility AttackPossibility::evaluate(
if (!attackInfo.shooting)
ap.attackerState->setPosition(hex);

std::vector<const battle::Unit *> defenderUnits;
std::vector<const battle::Unit *> retaliatedUnits = {attacker};
std::vector<const battle::Unit *> affectedUnits;
battle::Units defenderUnits;
battle::Units retaliatedUnits = {attacker};
battle::Units affectedUnits;

if (attackInfo.shooting)
defenderUnits = state->getAttackedBattleUnits(attacker, defender, defHex, true, hex, defender->getPosition());
Expand Down Expand Up @@ -384,7 +384,9 @@ AttackPossibility AttackPossibility::evaluate(
affectedUnits = defenderUnits;
vstd::concatenate(affectedUnits, retaliatedUnits);

logAi->trace("Attacked battle units count %d, %d->%d", affectedUnits.size(), hex.hex, defHex.hex);
#if BATTLE_TRACE_LEVEL>=1
logAi->trace("Attacked battle units count %d, %d->%d", affectedUnits.size(), hex, defHex);
#endif

std::map<uint32_t, std::shared_ptr<battle::CUnitState>> defenderStates;

Expand Down Expand Up @@ -487,7 +489,7 @@ AttackPossibility AttackPossibility::evaluate(
logAi->trace("BattleAI AP: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld",
attackInfo.attacker->unitType()->getJsonKey(),
attackInfo.defender->unitType()->getJsonKey(),
(int)ap.dest, (int)ap.from, (int)ap.affectedUnits.size(),
ap.dest.toInt(), ap.from.toInt(), (int)ap.affectedUnits.size(),
ap.defenderDamageReduce, ap.attackerDamageReduce, ap.collateralDamageReduce, ap.shootersBlockedDmg);
#endif

Expand All @@ -502,7 +504,7 @@ AttackPossibility AttackPossibility::evaluate(
logAi->trace("BattleAI best AP: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld",
attackInfo.attacker->unitType()->getJsonKey(),
attackInfo.defender->unitType()->getJsonKey(),
(int)bestAp.dest, (int)bestAp.from, (int)bestAp.affectedUnits.size(),
bestAp.dest.toInt(), bestAp.from.toInt(), (int)bestAp.affectedUnits.size(),
bestAp.defenderDamageReduce, bestAp.attackerDamageReduce, bestAp.collateralDamageReduce, bestAp.shootersBlockedDmg);
#endif

Expand Down
4 changes: 2 additions & 2 deletions AI/BattleAI/AttackPossibility.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class DamageCache

void cacheDamage(const battle::Unit * attacker, const battle::Unit * defender, std::shared_ptr<CBattleInfoCallback> hb);
int64_t getDamage(const battle::Unit * attacker, const battle::Unit * defender, std::shared_ptr<CBattleInfoCallback> hb);
int64_t getObstacleDamage(BattleHex hex, const battle::Unit * defender);
int64_t getObstacleDamage(const BattleHex & hex, const battle::Unit * defender);
int64_t getOriginalDamage(const battle::Unit * attacker, const battle::Unit * defender, std::shared_ptr<CBattleInfoCallback> hb);
void buildDamageCache(std::shared_ptr<HypotheticBattle> hb, BattleSide side);
};
Expand All @@ -55,7 +55,7 @@ class AttackPossibility
int64_t shootersBlockedDmg = 0;
bool defenderDead = false;

AttackPossibility(BattleHex from, BattleHex dest, const BattleAttackInfo & attack_);
AttackPossibility(const BattleHex & from, const BattleHex & dest, const BattleAttackInfo & attack_);

float damageDiff() const;
float attackValue() const;
Expand Down
4 changes: 2 additions & 2 deletions AI/BattleAI/BattleAI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ void logHexNumbers()
#if BATTLE_TRACE_LEVEL >= 1
logVisual->updateWithLock("hexes", [](IVisualLogBuilder & b)
{
for(BattleHex hex = BattleHex(0); hex < GameConstants::BFIELD_SIZE; hex = BattleHex(hex + 1))
b.addText(hex, std::to_string(hex.hex));
for(BattleHex hex = BattleHex(0); hex < GameConstants::BFIELD_SIZE; ++hex)
b.addText(hex, std::to_string(hex.toInt()));
});
#endif
}
Expand Down
2 changes: 1 addition & 1 deletion AI/BattleAI/BattleAI.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class CBattleAI : public CBattleGameInterface
//void battleResultsApplied() override; //called when all effects of last battle are applied
//void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied;
//void battleNewRound(int round) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
//void battleStackMoved(const CStack * stack, std::vector<BattleHex> dest, int distance) override;
//void battleStackMoved(const CStack * stack, BattleHexArray dest, int distance) override;
//void battleSpellCast(const BattleSpellCast *sc) override;
//void battleStacksEffectsSet(const SetStackEffect & sse) override;//called when a specific effect is set to stacks
//void battleTriggerEffect(const BattleTriggerEffect & bte) override;
Expand Down
75 changes: 36 additions & 39 deletions AI/BattleAI/BattleEvaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
bestAttack.attackerState->unitType()->getJsonKey(),
bestAttack.affectedUnits[0]->unitType()->getJsonKey(),
bestAttack.affectedUnits[0]->getCount(),
(int)bestAttack.from,
(int)bestAttack.attack.attacker->getPosition().hex,
bestAttack.from.toInt(),
bestAttack.attack.attacker->getPosition().toInt(),
bestAttack.attack.chargeDistance,
bestAttack.attack.attacker->getMovementRange(0),
bestAttack.defenderDamageReduce,
Expand Down Expand Up @@ -252,7 +252,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)

if(siegeDefense)
{
logAi->trace("Evaluating exchange at %d self-defense", stack->getPosition().hex);
logAi->trace("Evaluating exchange at %d self-defense", stack->getPosition());

BattleAttackInfo bai(stack, stack, 0, false);
AttackPossibility apDefend(stack->getPosition(), stack->getPosition(), bai);
Expand All @@ -278,15 +278,15 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
score = moveTarget.score;
cachedAttack.ap = moveTarget.cachedAttack;
cachedAttack.score = score;
cachedAttack.turn = moveTarget.turnsToRich;
cachedAttack.turn = moveTarget.turnsToReach;

if(stack->waited())
{
logAi->debug(
"Moving %s towards hex %s[%d], score: %2f",
stack->getDescription(),
moveTarget.cachedAttack->attack.defender->getDescription(),
moveTarget.cachedAttack->attack.defender->getPosition().hex,
moveTarget.cachedAttack->attack.defender->getPosition(),
moveTarget.score);

return goTowardsNearest(stack, moveTarget.positions, *targets);
Expand Down Expand Up @@ -320,14 +320,14 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
return stack->waited() ? BattleAction::makeDefend(stack) : BattleAction::makeWait(stack);
}

uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock> start)
uint64_t timeElapsed(std::chrono::time_point<std::chrono::steady_clock> start)
{
auto end = std::chrono::high_resolution_clock::now();
auto end = std::chrono::steady_clock::now();

return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
}

BattleAction BattleEvaluator::moveOrAttack(const CStack * stack, BattleHex hex, const PotentialTargets & targets)
BattleAction BattleEvaluator::moveOrAttack(const CStack * stack, const BattleHex & hex, const PotentialTargets & targets)
{
auto additionalScore = 0;
std::optional<AttackPossibility> attackOnTheWay;
Expand Down Expand Up @@ -355,7 +355,7 @@ BattleAction BattleEvaluator::moveOrAttack(const CStack * stack, BattleHex hex,
}
}

BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector<BattleHex> hexes, const PotentialTargets & targets)
BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, const BattleHexArray & hexes, const PotentialTargets & targets)
{
auto reachability = cb->getBattle(battleID)->getReachability(stack);
auto avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(reachability, stack, false);
Expand All @@ -371,37 +371,36 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector

if (siegeDefense)
{
vstd::erase_if(avHexes, [&](const BattleHex& hex) {
avHexes.eraseIf([&](const BattleHex & hex)
{
return !cb->getBattle(battleID)->battleIsInsideWalls(hex);
});
}

if(!avHexes.size() || !hexes.size()) //we are blocked or dest is blocked
if(avHexes.empty() || hexes.empty()) //we are blocked or dest is blocked
{
return BattleAction::makeDefend(stack);
}

std::vector<BattleHex> targetHexes = hexes;

vstd::erase_if(targetHexes, [](const BattleHex & hex) { return !hex.isValid(); });
BattleHexArray targetHexes = hexes;

std::sort(targetHexes.begin(), targetHexes.end(), [&](BattleHex h1, BattleHex h2) -> bool
targetHexes.sort([&](const BattleHex & h1, const BattleHex & h2) -> bool
{
return reachability.distances[h1] < reachability.distances[h2];
return reachability.distances[h1.toInt()] < reachability.distances[h2.toInt()];
});

BattleHex bestNeighbor = targetHexes.front();
BattleHex bestNeighbour = targetHexes.front();

if(reachability.distances[bestNeighbor] > GameConstants::BFIELD_SIZE)
if(reachability.distances[bestNeighbour.toInt()] > GameConstants::BFIELD_SIZE)
{
logAi->trace("No richable hexes.");
logAi->trace("No reachable hexes.");
return BattleAction::makeDefend(stack);
}

// this turn
for(auto hex : targetHexes)
for(const auto & hex : targetHexes)
{
if(vstd::contains(avHexes, hex))
if(avHexes.contains(hex))
{
return moveOrAttack(stack, hex, targets);
}
Expand All @@ -419,36 +418,31 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector

if(stack->hasBonusOfType(BonusType::FLYING))
{
std::set<BattleHex> obstacleHexes;

auto insertAffected = [](const CObstacleInstance & spellObst, std::set<BattleHex> & obstacleHexes) {
auto affectedHexes = spellObst.getAffectedTiles();
obstacleHexes.insert(affectedHexes.cbegin(), affectedHexes.cend());
};
BattleHexArray obstacleHexes;

const auto & obstacles = hb->battleGetAllObstacles();

for (const auto & obst: obstacles) {

for (const auto & obst : obstacles)
{
if(obst->triggersEffects())
{
auto triggerAbility = VLC->spells()->getById(obst->getTrigger());
auto triggerIsNegative = triggerAbility->isNegative() || triggerAbility->isDamage();

if(triggerIsNegative)
insertAffected(*obst, obstacleHexes);
obstacleHexes.insert(obst->getAffectedTiles());
}
}
// Flying stack doesn't go hex by hex, so we can't backtrack using predecessors.
// We just check all available hexes and pick the one closest to the target.
auto nearestAvailableHex = vstd::minElementByFun(avHexes, [&](BattleHex hex) -> int
auto nearestAvailableHex = vstd::minElementByFun(avHexes, [&](const BattleHex & hex) -> int
{
const int NEGATIVE_OBSTACLE_PENALTY = 100; // avoid landing on negative obstacle (moat, fire wall, etc)
const int BLOCKED_STACK_PENALTY = 100; // avoid landing on moat

auto distance = BattleHex::getDistance(bestNeighbor, hex);
auto distance = BattleHex::getDistance(bestNeighbour, hex);

if(vstd::contains(obstacleHexes, hex))
if(obstacleHexes.contains(hex))
distance += NEGATIVE_OBSTACLE_PENALTY;

return scoreEvaluator.checkPositionBlocksOurStacks(*hb, stack, hex) ? BLOCKED_STACK_PENALTY + distance : distance;
Expand All @@ -458,21 +452,22 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
}
else
{
BattleHex currentDest = bestNeighbor;
BattleHex currentDest = bestNeighbour;

while(true)
{
if(!currentDest.isValid())
{
return BattleAction::makeDefend(stack);
}

if(vstd::contains(avHexes, currentDest)
if(avHexes.contains(currentDest)
&& !scoreEvaluator.checkPositionBlocksOurStacks(*hb, stack, currentDest))
{
return moveOrAttack(stack, currentDest, targets);
}

currentDest = reachability.predecessors[currentDest];
currentDest = reachability.predecessors[currentDest.toInt()];
}
}

Expand Down Expand Up @@ -691,7 +686,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
else
{
auto psFirst = ps.dest.front();
auto strWhere = psFirst.unitValue ? psFirst.unitValue->getDescription() : std::to_string(psFirst.hexValue.hex);
auto strWhere = psFirst.unitValue ? psFirst.unitValue->getDescription() : std::to_string(psFirst.hexValue.toInt());

logAi->trace("Evaluating %s at %s", ps.spell->getNameTranslated(), strWhere);
}
Expand Down Expand Up @@ -760,7 +755,9 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)

auto updatedAttack = AttackPossibility::evaluate(updatedBai, cachedAttack.ap->from, innerCache, state);

stackActionScore = scoreEvaluator.evaluateExchange(updatedAttack, cachedAttack.turn, *targets, innerCache, state);
BattleExchangeEvaluator innerEvaluator(scoreEvaluator);

stackActionScore = innerEvaluator.evaluateExchange(updatedAttack, cachedAttack.turn, *targets, innerCache, state);
}
for(const auto & unit : allUnits)
{
Expand Down Expand Up @@ -807,7 +804,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
logAi->trace(
"Spell %s to %d affects %s (%d), dps: %2f oldHealth: %d newHealth: %d",
ps.spell->getNameTranslated(),
ps.dest.at(0).hexValue.hex, // Safe to access .at(0) now
ps.dest.at(0).hexValue.toInt(), // Safe to access .at(0) now
unit->creatureId().toCreature()->getNameSingularTranslated(),
unit->getCount(),
dpsReduce,
Expand Down
4 changes: 2 additions & 2 deletions AI/BattleAI/BattleEvaluator.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ class BattleEvaluator
bool attemptCastingSpell(const CStack * stack);
bool canCastSpell();
std::optional<PossibleSpellcast> findBestCreatureSpell(const CStack * stack);
BattleAction goTowardsNearest(const CStack * stack, std::vector<BattleHex> hexes, const PotentialTargets & targets);
BattleAction goTowardsNearest(const CStack * stack, const BattleHexArray & hexes, const PotentialTargets & targets);
std::vector<BattleHex> getBrokenWallMoatHexes() const;
bool hasWorkingTowers() const;
void evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcast & ps); //for offensive damaging spells only
void print(const std::string & text) const;
BattleAction moveOrAttack(const CStack * stack, BattleHex hex, const PotentialTargets & targets);
BattleAction moveOrAttack(const CStack * stack, const BattleHex & hex, const PotentialTargets & targets);

BattleEvaluator(
std::shared_ptr<Environment> env,
Expand Down
Loading

0 comments on commit d3131ea

Please sign in to comment.