diff --git a/AI/BattleAI/AttackPossibility.cpp b/AI/BattleAI/AttackPossibility.cpp index ce1803c34..91cc7f62f 100644 --- a/AI/BattleAI/AttackPossibility.cpp +++ b/AI/BattleAI/AttackPossibility.cpp @@ -22,7 +22,7 @@ void DamageCache::cacheDamage(const battle::Unit * attacker, const battle::Unit { auto damage = averageDmg(hb->battleEstimateDamage(attacker, defender, 0).damage); - damageCache[attacker->unitId()][defender->unitId()] = damage / attacker->getCount(); + damageCache[attacker->unitId()][defender->unitId()] = static_cast(damage) / attacker->getCount(); } @@ -98,18 +98,18 @@ AttackPossibility::AttackPossibility(BattleHex from, BattleHex dest, const Battl { } -int64_t AttackPossibility::damageDiff() const +float AttackPossibility::damageDiff() const { return defenderDamageReduce - attackerDamageReduce - collateralDamageReduce + shootersBlockedDmg; } -int64_t AttackPossibility::damageDiff(float positiveEffectMultiplier, float negativeEffectMultiplier) const +float AttackPossibility::damageDiff(float positiveEffectMultiplier, float negativeEffectMultiplier) const { return positiveEffectMultiplier * (defenderDamageReduce + shootersBlockedDmg) - negativeEffectMultiplier * (attackerDamageReduce + collateralDamageReduce); } -int64_t AttackPossibility::attackValue() const +float AttackPossibility::attackValue() const { return damageDiff(); } @@ -119,7 +119,7 @@ int64_t AttackPossibility::attackValue() const /// Half bounty for kill, half for making damage equal to enemy health /// Bounty - the killed creature average damage calculated against attacker /// -int64_t AttackPossibility::calculateDamageReduce( +float AttackPossibility::calculateDamageReduce( const battle::Unit * attacker, const battle::Unit * defender, uint64_t damageDealt, @@ -163,7 +163,7 @@ int64_t AttackPossibility::calculateDamageReduce( auto firstUnitHealthRatio = firstUnitHpLeft == 0 ? 1 : static_cast(firstUnitHpLeft) / maxHealth; auto firstUnitKillValue = (1 - firstUnitHealthRatio) * (1 - firstUnitHealthRatio); - return (int64_t)(damagePerEnemy * (enemiesKilled + firstUnitKillValue * HEALTH_BOUNTY)); + return damagePerEnemy * (enemiesKilled + firstUnitKillValue * HEALTH_BOUNTY); } int64_t AttackPossibility::evaluateBlockedShootersDmg( @@ -270,7 +270,8 @@ AttackPossibility AttackPossibility::evaluate( for(int i = 0; i < totalAttacks; i++) { - int64_t damageDealt, damageReceived, defenderDamageReduce, attackerDamageReduce; + int64_t damageDealt, damageReceived; + float defenderDamageReduce, attackerDamageReduce; DamageEstimation retaliation; auto attackDmg = state->battleEstimateDamage(ap.attack, &retaliation); diff --git a/AI/BattleAI/AttackPossibility.h b/AI/BattleAI/AttackPossibility.h index cd28839f4..2181d883a 100644 --- a/AI/BattleAI/AttackPossibility.h +++ b/AI/BattleAI/AttackPossibility.h @@ -46,16 +46,16 @@ public: std::vector> affectedUnits; - int64_t defenderDamageReduce = 0; - int64_t attackerDamageReduce = 0; //usually by counter-attack - int64_t collateralDamageReduce = 0; // friendly fire (usually by two-hex attacks) + float defenderDamageReduce = 0; + float attackerDamageReduce = 0; //usually by counter-attack + float collateralDamageReduce = 0; // friendly fire (usually by two-hex attacks) int64_t shootersBlockedDmg = 0; AttackPossibility(BattleHex from, BattleHex dest, const BattleAttackInfo & attack_); - int64_t damageDiff() const; - int64_t attackValue() const; - int64_t damageDiff(float positiveEffectMultiplier, float negativeEffectMultiplier) const; + float damageDiff() const; + float attackValue() const; + float damageDiff(float positiveEffectMultiplier, float negativeEffectMultiplier) const; static AttackPossibility evaluate( const BattleAttackInfo & attackInfo, @@ -63,7 +63,7 @@ public: DamageCache & damageCache, std::shared_ptr state); - static int64_t calculateDamageReduce( + static float calculateDamageReduce( const battle::Unit * attacker, const battle::Unit * defender, uint64_t damageDealt, diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 60e2729f6..3ff581c61 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -81,7 +81,28 @@ void CBattleAI::yourTacticPhase(int distance) cb->battleMakeTacticAction(BattleAction::makeEndOFTacticPhase(cb->battleGetTacticsSide())); } -void CBattleAI::activeStack( const CStack * stack ) +float getStrengthRatio(std::shared_ptr cb, int side) +{ + auto stacks = cb->battleGetAllStacks(); + auto our = 0, enemy = 0; + + for(auto stack : stacks) + { + auto creature = stack->creatureId().toCreature(); + + if(!creature) + continue; + + if(stack->unitSide() == side) + our += stack->getCount() * creature->getAIValue(); + else + enemy += stack->getCount() * creature->getAIValue(); + } + + return enemy == 0 ? 1.0f : static_cast(our) / enemy; +} + +void CBattleAI::activeStack(const CStack * stack ) { LOG_TRACE_PARAMS(logAi, "stack: %s", stack->nodeName()); @@ -110,7 +131,11 @@ void CBattleAI::activeStack( const CStack * stack ) return; } - BattleEvaluator evaluator(env, cb, stack, playerID, side, strengthRatio); +#if BATTLE_TRACE_LEVEL>=1 + logAi->trace("Build evaluator and targets"); +#endif + + BattleEvaluator evaluator(env, cb, stack, playerID, side, getStrengthRatio(cb, side)); result = evaluator.selectStackAction(stack); @@ -207,10 +232,6 @@ void CBattleAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2 { LOG_TRACE(logAi); side = Side; - strengthRatio = static_cast(army1->getArmyStrength()) / static_cast(army2->getArmyStrength()); - - if(side == 1) - strengthRatio = 1 / strengthRatio; skipCastUntilNextBattle = false; } diff --git a/AI/BattleAI/BattleAI.h b/AI/BattleAI/BattleAI.h index 9c60a32b4..95841684e 100644 --- a/AI/BattleAI/BattleAI.h +++ b/AI/BattleAI/BattleAI.h @@ -62,7 +62,6 @@ class CBattleAI : public CBattleGameInterface bool wasWaitingForRealize; bool wasUnlockingGs; int movesSkippedByDefense; - float strengthRatio; bool skipCastUntilNextBattle; public: diff --git a/AI/BattleAI/BattleEvaluator.cpp b/AI/BattleAI/BattleEvaluator.cpp index d5d101f1e..64c505591 100644 --- a/AI/BattleAI/BattleEvaluator.cpp +++ b/AI/BattleAI/BattleEvaluator.cpp @@ -100,11 +100,14 @@ std::optional BattleEvaluator::findBestCreatureSpell(const CS BattleAction BattleEvaluator::selectStackAction(const CStack * stack) { +#if BATTLE_TRACE_LEVEL >= 1 + logAi->trace("Select stack action"); +#endif //evaluate casting spell for spellcasting stack std::optional bestSpellcast = findBestCreatureSpell(stack); auto moveTarget = scoreEvaluator.findMoveTowardsUnreachable(stack, *targets, damageCache, hb); - auto score = EvaluationResult::INEFFECTIVE_SCORE; + float score = EvaluationResult::INEFFECTIVE_SCORE; if(targets->possibleAttacks.empty() && bestSpellcast.has_value()) { @@ -136,7 +139,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack) { score = evaluationResult.score; - logAi->debug("BattleAI: %s -> %s x %d, from %d curpos %d dist %d speed %d: +%lld -%lld = %lld", + logAi->debug("BattleAI: %s -> %s x %d, from %d curpos %d dist %d speed %d: +%2f -%2f = %2f", bestAttack.attackerState->unitType()->getJsonKey(), bestAttack.affectedUnits[0]->unitType()->getJsonKey(), (int)bestAttack.affectedUnits[0]->getCount(), @@ -145,7 +148,8 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack) bestAttack.attack.chargeDistance, bestAttack.attack.attacker->speed(0, true), bestAttack.defenderDamageReduce, - bestAttack.attackerDamageReduce, bestAttack.attackValue() + bestAttack.attackerDamageReduce, + bestAttack.attackValue() ); if (moveTarget.scorePerTurn <= score) @@ -513,11 +517,20 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack) CStopWatch timer; +#if BATTLE_TRACE_LEVEL >= 1 + tbb::blocked_range r(0, possibleCasts.size()); +#else tbb::parallel_for(tbb::blocked_range(0, possibleCasts.size()), [&](const tbb::blocked_range & r) { +#endif for(auto i = r.begin(); i != r.end(); i++) { auto & ps = possibleCasts[i]; + +#if BATTLE_TRACE_LEVEL >= 1 + logAi->trace("Evaluating %s", ps.spell->getNameTranslated()); +#endif + auto state = std::make_shared(env.get(), cb); spells::BattleCast cast(state.get(), hero, spells::Mode::HERO, ps.spell); @@ -531,12 +544,17 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack) return !original || u->speed() != original->speed(); }); - DamageCache innerCache(&damageCache); + DamageCache safeCopy = damageCache; + DamageCache innerCache(&safeCopy); innerCache.buildDamageCache(state, side); if(needFullEval || !cachedAttack) { - PotentialTargets innerTargets(activeStack, damageCache, state); +#if BATTLE_TRACE_LEVEL >= 1 + logAi->trace("Full evaluation is started due to stack speed affected."); +#endif + + PotentialTargets innerTargets(activeStack, innerCache, state); BattleExchangeEvaluator innerEvaluator(state, env, strengthRatio); if(!innerTargets.possibleAttacks.empty()) @@ -586,14 +604,27 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack) } else ps.value -= dpsReduce * scoreEvaluator.getNegativeEffectMultiplier(); + +#if BATTLE_TRACE_LEVEL >= 1 + logAi->trace( + "Spell affects %s (%d), dps: %2f", + unit->creatureId().toCreature()->getNameSingularTranslated(), + unit->getCount(), + dpsReduce); +#endif } } +#if BATTLE_TRACE_LEVEL >= 1 + logAi->trace("Total score: %2f", ps.value); +#endif } +#if BATTLE_TRACE_LEVEL == 0 }); +#endif LOGFL("Evaluation took %d ms", timer.getDiff()); - auto pscValue = [](const PossibleSpellcast &ps) -> int64_t + auto pscValue = [](const PossibleSpellcast &ps) -> float { return ps.value; }; diff --git a/AI/BattleAI/BattleEvaluator.h b/AI/BattleAI/BattleEvaluator.h index b3f61091c..cb183f46b 100644 --- a/AI/BattleAI/BattleEvaluator.h +++ b/AI/BattleAI/BattleEvaluator.h @@ -33,7 +33,7 @@ class BattleEvaluator std::optional cachedAttack; PlayerColor playerID; int side; - int64_t cachedScore; + float cachedScore; DamageCache damageCache; float strengthRatio; diff --git a/AI/BattleAI/BattleExchangeVariant.cpp b/AI/BattleAI/BattleExchangeVariant.cpp index b392af13a..8ae233e41 100644 --- a/AI/BattleAI/BattleExchangeVariant.cpp +++ b/AI/BattleAI/BattleExchangeVariant.cpp @@ -25,40 +25,107 @@ MoveTarget::MoveTarget() turnsToRich = 1; } -int64_t BattleExchangeVariant::trackAttack(const AttackPossibility & ap, HypotheticBattle & state) +float BattleExchangeVariant::trackAttack( + const AttackPossibility & ap, + std::shared_ptr hb, + DamageCache & damageCache) { + auto attacker = hb->getForUpdate(ap.attack.attacker->unitId()); + + const std::string cachingStringBlocksRetaliation = "type_BLOCKS_RETALIATION"; + static const auto selectorBlocksRetaliation = Selector::type()(BonusType::BLOCKS_RETALIATION); + const bool counterAttacksBlocked = attacker->hasBonus(selectorBlocksRetaliation, cachingStringBlocksRetaliation); + + float attackValue = 0; auto affectedUnits = ap.affectedUnits; affectedUnits.push_back(ap.attackerState); for(auto affectedUnit : affectedUnits) { - auto unitToUpdate = state.getForUpdate(affectedUnit->unitId()); + auto unitToUpdate = hb->getForUpdate(affectedUnit->unitId()); - unitToUpdate->health = affectedUnit->health; - unitToUpdate->shots = affectedUnit->shots; - unitToUpdate->counterAttacks = affectedUnit->counterAttacks; - unitToUpdate->movedThisRound = affectedUnit->movedThisRound; - } + if(unitToUpdate->unitSide() == attacker->unitSide()) + { + if(unitToUpdate->unitId() == attacker->unitId()) + { + auto defender = hb->getForUpdate(ap.attack.defender->unitId()); - auto attackValue = ap.damageDiff(positiveEffectMultiplier, negativeEffectMultiplier); + if(!defender->alive() || counterAttacksBlocked || ap.attack.shooting || !defender->ableToRetaliate()) + continue; - dpsScore += attackValue; + auto retaliationDamage = damageCache.getDamage(defender.get(), unitToUpdate.get(), hb); + auto attackerDamageReduce = AttackPossibility::calculateDamageReduce(defender.get(), unitToUpdate.get(), retaliationDamage, damageCache, hb); + + attackValue -= attackerDamageReduce; + dpsScore -= attackerDamageReduce * negativeEffectMultiplier; + attackerValue[unitToUpdate->unitId()].isRetalitated = true; + + unitToUpdate->damage(retaliationDamage); + defender->afterAttack(false, true); #if BATTLE_TRACE_LEVEL>=1 - logAi->trace( - "%s -> %s, ap attack, %s, dps: %lld, score: %lld", - ap.attack.attacker->getDescription(), - ap.attack.defender->getDescription(), - ap.attack.shooting ? "shot" : "mellee", - ap.damageDealt, - attackValue); + logAi->trace( + "%s -> %s, ap retalitation, %s, dps: %2f, score: %2f", + defender->getDescription(), + unitToUpdate->getDescription(), + ap.attack.shooting ? "shot" : "mellee", + retaliationDamage, + attackerDamageReduce); #endif + } + else + { + auto collateralDamage = damageCache.getDamage(attacker.get(), unitToUpdate.get(), hb); + auto collateralDamageReduce = AttackPossibility::calculateDamageReduce(attacker.get(), unitToUpdate.get(), collateralDamage, damageCache, hb); + + attackValue -= collateralDamageReduce; + dpsScore -= collateralDamageReduce * negativeEffectMultiplier; + + unitToUpdate->damage(collateralDamage); + +#if BATTLE_TRACE_LEVEL>=1 + logAi->trace( + "%s -> %s, ap collateral, %s, dps: %2f, score: %2f", + attacker->getDescription(), + unitToUpdate->getDescription(), + ap.attack.shooting ? "shot" : "mellee", + collateralDamage, + collateralDamageReduce); +#endif + } + } + else + { + int64_t attackDamage = damageCache.getDamage(attacker.get(), unitToUpdate.get(), hb); + float defenderDamageReduce = AttackPossibility::calculateDamageReduce(attacker.get(), unitToUpdate.get(), attackDamage, damageCache, hb); + + attackValue += defenderDamageReduce; + dpsScore += defenderDamageReduce * positiveEffectMultiplier; + attackerValue[attacker->unitId()].value += defenderDamageReduce; + + unitToUpdate->damage(attackDamage); + +#if BATTLE_TRACE_LEVEL>=1 + logAi->trace( + "%s -> %s, ap attack, %s, dps: %2f, score: %2f", + attacker->getDescription(), + unitToUpdate->getDescription(), + ap.attack.shooting ? "shot" : "mellee", + attackDamage, + defenderDamageReduce); +#endif + } + } + + attackValue += ap.shootersBlockedDmg; + dpsScore += ap.shootersBlockedDmg * positiveEffectMultiplier; + attacker->afterAttack(ap.attack.shooting, false); return attackValue; } -int64_t BattleExchangeVariant::trackAttack( +float BattleExchangeVariant::trackAttack( std::shared_ptr attacker, std::shared_ptr defender, bool shooting, @@ -71,23 +138,15 @@ int64_t BattleExchangeVariant::trackAttack( static const auto selectorBlocksRetaliation = Selector::type()(BonusType::BLOCKS_RETALIATION); const bool counterAttacksBlocked = attacker->hasBonus(selectorBlocksRetaliation, cachingStringBlocksRetaliation); - // FIXME: provide distance info for Jousting bonus - BattleAttackInfo bai(attacker.get(), defender.get(), 0, shooting); - - if(shooting) - { - bai.attackerPos.setXY(8, 5); - } - int64_t attackDamage = damageCache.getDamage(attacker.get(), defender.get(), hb); - int64_t defenderDamageReduce = AttackPossibility::calculateDamageReduce(attacker.get(), defender.get(), attackDamage, damageCache, hb); - int64_t attackerDamageReduce = 0; + float defenderDamageReduce = AttackPossibility::calculateDamageReduce(attacker.get(), defender.get(), attackDamage, damageCache, hb); + float attackerDamageReduce = 0; if(!evaluateOnly) { #if BATTLE_TRACE_LEVEL>=1 logAi->trace( - "%s -> %s, normal attack, %s, dps: %lld, %lld", + "%s -> %s, normal attack, %s, dps: %lld, %2f", attacker->getDescription(), defender->getDescription(), shooting ? "shot" : "mellee", @@ -107,36 +166,33 @@ int64_t BattleExchangeVariant::trackAttack( attacker->afterAttack(shooting, false); } - if(defender->alive() && defender->ableToRetaliate() && !counterAttacksBlocked && !shooting) + if(!evaluateOnly && defender->alive() && defender->ableToRetaliate() && !counterAttacksBlocked && !shooting) { auto retaliationDamage = damageCache.getDamage(defender.get(), attacker.get(), hb); attackerDamageReduce = AttackPossibility::calculateDamageReduce(defender.get(), attacker.get(), retaliationDamage, damageCache, hb); - if(!evaluateOnly) - { #if BATTLE_TRACE_LEVEL>=1 - logAi->trace( - "%s -> %s, retaliation, dps: %lld, %lld", - defender->getDescription(), - attacker->getDescription(), - retaliationDamage, - attackerDamageReduce); + logAi->trace( + "%s -> %s, retaliation, dps: %lld, %2f", + defender->getDescription(), + attacker->getDescription(), + retaliationDamage, + attackerDamageReduce); #endif - if(isOurAttack) - { - dpsScore -= attackerDamageReduce * negativeEffectMultiplier; - attackerValue[attacker->unitId()].isRetalitated = true; - } - else - { - dpsScore += attackerDamageReduce * positiveEffectMultiplier; - attackerValue[defender->unitId()].value += attackerDamageReduce; - } - - attacker->damage(retaliationDamage); - defender->afterAttack(false, true); + if(isOurAttack) + { + dpsScore -= attackerDamageReduce * negativeEffectMultiplier; + attackerValue[attacker->unitId()].isRetalitated = true; } + else + { + dpsScore += attackerDamageReduce * positiveEffectMultiplier; + attackerValue[defender->unitId()].value += attackerDamageReduce; + } + + attacker->damage(retaliationDamage); + defender->afterAttack(false, true); } auto score = defenderDamageReduce - attackerDamageReduce; @@ -144,7 +200,7 @@ int64_t BattleExchangeVariant::trackAttack( #if BATTLE_TRACE_LEVEL>=1 if(!score) { - logAi->trace("Attack has zero score d:%lld a:%lld", defenderDamageReduce, attackerDamageReduce); + logAi->trace("Attack has zero score d:%2f a:%2f", defenderDamageReduce, attackerDamageReduce); } #endif @@ -159,33 +215,22 @@ EvaluationResult BattleExchangeEvaluator::findBestTarget( { EvaluationResult result(targets.bestAction()); - updateReachabilityMap(hb); - - for(auto & ap : targets.possibleAttacks) - { - int64_t score = calculateExchange(ap, targets, damageCache, hb); - - if(score > result.score) - { - result.score = score; - result.bestAttack = ap; - } - } - if(!activeStack->waited()) { #if BATTLE_TRACE_LEVEL>=1 logAi->trace("Evaluating waited attack for %s", activeStack->getDescription()); #endif - hb->getForUpdate(activeStack->unitId())->waiting = true; - hb->getForUpdate(activeStack->unitId())->waitedThisTurn = true; + auto hbWaited = std::make_shared(env.get(), hb); - updateReachabilityMap(hb); + hbWaited->getForUpdate(activeStack->unitId())->waiting = true; + hbWaited->getForUpdate(activeStack->unitId())->waitedThisTurn = true; + + updateReachabilityMap(hbWaited); for(auto & ap : targets.possibleAttacks) { - int64_t score = calculateExchange(ap, targets, damageCache, hb); + float score = calculateExchange(ap, targets, damageCache, hbWaited); if(score > result.score) { @@ -196,6 +241,24 @@ EvaluationResult BattleExchangeEvaluator::findBestTarget( } } +#if BATTLE_TRACE_LEVEL>=1 + logAi->trace("Evaluating normal attack for %s", activeStack->getDescription()); +#endif + + updateReachabilityMap(hb); + + for(auto & ap : targets.possibleAttacks) + { + float score = calculateExchange(ap, targets, damageCache, hb); + + if(score >= result.score) + { + result.score = score; + result.bestAttack = ap; + result.wait = false; + } + } + return result; } @@ -361,14 +424,14 @@ std::vector BattleExchangeEvaluator::getExchangeUnits( return exchangeUnits; } -int64_t BattleExchangeEvaluator::calculateExchange( +float BattleExchangeEvaluator::calculateExchange( const AttackPossibility & ap, PotentialTargets & targets, DamageCache & damageCache, std::shared_ptr hb) { #if BATTLE_TRACE_LEVEL>=1 - logAi->trace("Battle exchange at %lld", ap.attack.shooting ? ap.dest : ap.from); + logAi->trace("Battle exchange at %d", ap.attack.shooting ? ap.dest.hex : ap.from.hex); #endif if(cb->battleGetMySide() == BattlePerspective::LEFT_SIDE @@ -439,7 +502,7 @@ int64_t BattleExchangeEvaluator::calculateExchange( if(!isOur || !exchangeBattle->battleGetUnitByID(targetUnit->unitId())->alive()) { - auto estimateAttack = [&](const battle::Unit * u) -> int64_t + auto estimateAttack = [&](const battle::Unit * u) -> float { auto stackWithBonuses = exchangeBattle->getForUpdate(u->unitId()); auto score = v.trackAttack( @@ -452,7 +515,7 @@ int64_t BattleExchangeEvaluator::calculateExchange( true); #if BATTLE_TRACE_LEVEL>=1 - logAi->trace("Best target selector %s->%s score = %lld", attacker->getDescription(), u->getDescription(), score); + logAi->trace("Best target selector %s->%s score = %2f", attacker->getDescription(), u->getDescription(), score); #endif return score; @@ -497,9 +560,10 @@ int64_t BattleExchangeEvaluator::calculateExchange( auto shooting = exchangeBattle->battleCanShoot(attacker.get()); const int totalAttacks = attacker->getTotalAttacks(shooting); - if(canUseAp && activeUnit->unitId() == ap.attack.attacker->unitId() && targetUnit->unitId() == ap.attack.defender->unitId()) + if(canUseAp && activeUnit->unitId() == ap.attack.attacker->unitId() + && targetUnit->unitId() == ap.attack.defender->unitId()) { - v.trackAttack(ap, *exchangeBattle); + v.trackAttack(ap, exchangeBattle, damageCache); } else { @@ -530,7 +594,7 @@ int64_t BattleExchangeEvaluator::calculateExchange( v.adjustPositions(melleeAttackers, ap, reachabilityMap); #if BATTLE_TRACE_LEVEL>=1 - logAi->trace("Exchange score: %lld", v.getScore()); + logAi->trace("Exchange score: %2f", v.getScore()); #endif return v.getScore(); @@ -560,7 +624,7 @@ void BattleExchangeVariant::adjustPositions( vstd::erase_if_present(hexes, ap.attack.attacker->occupiedHex(ap.attack.attackerPos)); } - int64_t notRealizedDamage = 0; + float notRealizedDamage = 0; for(auto unit : attackers) { @@ -576,7 +640,7 @@ void BattleExchangeVariant::adjustPositions( continue; } - auto desiredPosition = vstd::minElementByFun(hexes, [&](BattleHex h) -> int64_t + auto desiredPosition = vstd::minElementByFun(hexes, [&](BattleHex h) -> float { auto score = vstd::contains(reachabilityMap[h], unit) ? reachabilityMap[h].size() diff --git a/AI/BattleAI/BattleExchangeVariant.h b/AI/BattleAI/BattleExchangeVariant.h index 9837924a6..19da2721a 100644 --- a/AI/BattleAI/BattleExchangeVariant.h +++ b/AI/BattleAI/BattleExchangeVariant.h @@ -16,7 +16,7 @@ struct AttackerValue { - int64_t value; + float value; bool isRetalitated; BattleHex position; @@ -25,8 +25,8 @@ struct AttackerValue struct MoveTarget { - int64_t score; - int64_t scorePerTurn; + float score; + float scorePerTurn; std::vector positions; std::optional cachedAttack; uint8_t turnsToRich; @@ -36,12 +36,12 @@ struct MoveTarget struct EvaluationResult { - static const int64_t INEFFECTIVE_SCORE = -1000000; + static const int64_t INEFFECTIVE_SCORE = -10000; AttackPossibility bestAttack; MoveTarget bestMove; bool wait; - int64_t score; + float score; bool defend; EvaluationResult(const AttackPossibility & ap) @@ -62,9 +62,12 @@ public: BattleExchangeVariant(float positiveEffectMultiplier, float negativeEffectMultiplier) : dpsScore(0), positiveEffectMultiplier(positiveEffectMultiplier), negativeEffectMultiplier(negativeEffectMultiplier) {} - int64_t trackAttack(const AttackPossibility & ap, HypotheticBattle & state); + float trackAttack( + const AttackPossibility & ap, + std::shared_ptr hb, + DamageCache & damageCache); - int64_t trackAttack( + float trackAttack( std::shared_ptr attacker, std::shared_ptr defender, bool shooting, @@ -73,7 +76,7 @@ public: std::shared_ptr hb, bool evaluateOnly = false); - int64_t getScore() const { return dpsScore; } + float getScore() const { return dpsScore; } void adjustPositions( std::vector attackers, @@ -83,7 +86,7 @@ public: private: float positiveEffectMultiplier; float negativeEffectMultiplier; - int64_t dpsScore; + float dpsScore; std::map attackerValue; }; @@ -110,7 +113,7 @@ public: DamageCache & damageCache, std::shared_ptr hb); - int64_t calculateExchange( + float calculateExchange( const AttackPossibility & ap, PotentialTargets & targets, DamageCache & damageCache, diff --git a/AI/BattleAI/PossibleSpellcast.h b/AI/BattleAI/PossibleSpellcast.h index a4e0021c7..4581d381d 100644 --- a/AI/BattleAI/PossibleSpellcast.h +++ b/AI/BattleAI/PossibleSpellcast.h @@ -27,7 +27,7 @@ public: const CSpell * spell; spells::Target dest; - int64_t value; + float value; PossibleSpellcast(); virtual ~PossibleSpellcast(); diff --git a/AI/BattleAI/StackWithBonuses.cpp b/AI/BattleAI/StackWithBonuses.cpp index c77e2b4fc..6c5f57e31 100644 --- a/AI/BattleAI/StackWithBonuses.cpp +++ b/AI/BattleAI/StackWithBonuses.cpp @@ -45,7 +45,8 @@ StackWithBonuses::StackWithBonuses(const HypotheticBattle * Owner, const battle: id(Stack->unitId()), side(Stack->unitSide()), player(Stack->unitOwner()), - slot(Stack->unitSlot()) + slot(Stack->unitSlot()), + treeVersionLocal(0) { localInit(Owner); @@ -61,7 +62,8 @@ StackWithBonuses::StackWithBonuses(const HypotheticBattle * Owner, const battle: id(Stack->unitId()), side(Stack->unitSide()), player(Stack->unitOwner()), - slot(Stack->unitSlot()) + slot(Stack->unitSlot()), + treeVersionLocal(0) { localInit(Owner); @@ -76,7 +78,8 @@ StackWithBonuses::StackWithBonuses(const HypotheticBattle * Owner, const battle: baseAmount(info.count), id(info.id), side(info.side), - slot(SlotID::SUMMONED_SLOT_PLACEHOLDER) + slot(SlotID::SUMMONED_SLOT_PLACEHOLDER), + treeVersionLocal(0) { type = info.type.toCreature(); origBearer = type;