diff --git a/AI/BattleAI/AttackPossibility.cpp b/AI/BattleAI/AttackPossibility.cpp index cfda077f3..8098e4668 100644 --- a/AI/BattleAI/AttackPossibility.cpp +++ b/AI/BattleAI/AttackPossibility.cpp @@ -212,11 +212,13 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo & attackInf // check how much damage we gain from blocking enemy shooters on this hex bestAp.shootersBlockedDmg = evaluateBlockedShootersDmg(attackInfo, hex, state); - logAi->debug("BattleAI best AP: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld", +#if BATTLE_TRACE_LEVEL>=1 + 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.defenderDamageReduce, bestAp.attackerDamageReduce, bestAp.collateralDamageReduce, bestAp.shootersBlockedDmg); +#endif //TODO other damage related to attack (eg. fire shield and other abilities) return bestAp; diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 99a5c922e..8cf523b62 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -89,6 +89,7 @@ void CBattleAI::initBattleInterface(std::shared_ptr ENV, std::share wasUnlockingGs = CB->unlockGsWhenWaiting; CB->waitTillRealize = true; CB->unlockGsWhenWaiting = false; + movesSkippedByDefense = 0; } BattleAction CBattleAI::activeStack( const CStack * stack ) @@ -181,6 +182,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) if(bestSpellcast.is_initialized() && bestSpellcast->value > bestAttack.damageDiff()) { // return because spellcast value is damage dealt and score is dps reduce + movesSkippedByDefense = 0; return BattleAction::makeCreatureSpellcast(stack, bestSpellcast->dest, bestSpellcast->spell->id); } @@ -196,14 +198,15 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) } else if(bestAttack.attack.shooting) { - result = BattleAction::makeShotAttack(stack, bestAttack.attack.defender); action = "shot"; + movesSkippedByDefense = 0; } else { result = BattleAction::makeMeleeAttack(stack, bestAttack.attack.defender->getPosition(), bestAttack.from); action = "melee"; + movesSkippedByDefense = 0; } logAi->debug("BattleAI: %s -> %s x %d, %s, from %d curpos %d dist %d speed %d: +%lld -%lld = %lld", @@ -217,6 +220,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) } else if(bestSpellcast.is_initialized()) { + movesSkippedByDefense = 0; return BattleAction::makeCreatureSpellcast(stack, bestSpellcast->dest, bestSpellcast->spell->id); } @@ -235,12 +239,8 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) } } - if(score > EvaluationResult::INEFFECTIVE_SCORE) - { - return result; - } - - if(!stack->hasBonusOfType(Bonus::FLYING) + if(score <= EvaluationResult::INEFFECTIVE_SCORE + && !stack->hasBonusOfType(Bonus::FLYING) && stack->unitSide() == BattleSide::ATTACKER && cb->battleGetSiegeLevel() >= CGTownInstance::CITADEL) { @@ -248,10 +248,12 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) if(brokenWallMoat.size()) { + movesSkippedByDefense = 0; + if(stack->doubleWide() && vstd::contains(brokenWallMoat, stack->getPosition())) - return BattleAction::makeMove(stack, stack->getPosition().cloneInDirection(BattleHex::RIGHT)); + result = BattleAction::makeMove(stack, stack->getPosition().cloneInDirection(BattleHex::RIGHT)); else - return goTowardsNearest(stack, brokenWallMoat); + result = goTowardsNearest(stack, brokenWallMoat); } } } @@ -264,6 +266,15 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) logAi->error("Exception occurred in %s %s",__FUNCTION__, e.what()); } + if(result.actionType == EActionType::DEFEND) + { + movesSkippedByDefense++; + } + else if(result.actionType != EActionType::WAIT) + { + movesSkippedByDefense = 0; + } + return result; } @@ -285,7 +296,9 @@ BattleAction CBattleAI::goTowardsNearest(const CStack * stack, std::vectorcoversPos(hex)) { @@ -396,6 +409,8 @@ BattleAction CBattleAI::useCatapult(const CStack * stack) attack.side = side; attack.stackNumber = stack->ID; + movesSkippedByDefense = 0; + return attack; } @@ -703,6 +718,7 @@ void CBattleAI::attemptCastingSpell() spellcast.side = side; spellcast.stackNumber = (!side) ? -1 : -2; cb->battleMakeAction(&spellcast); + movesSkippedByDefense = 0; } else { @@ -796,12 +812,21 @@ boost::optional CBattleAI::considerFleeingOrSurrendering() } } + bs.turnsSkippedByDefense = movesSkippedByDefense / bs.ourStacks.size(); + if(!bs.canFlee || !bs.canSurrender) { return boost::none; } - return cb->makeSurrenderRetreatDecision(bs); + auto result = cb->makeSurrenderRetreatDecision(bs); + + if(!result && bs.canFlee && bs.turnsSkippedByDefense > 30) + { + return BattleAction::makeRetreat(bs.ourSide); + } + + return result; } diff --git a/AI/BattleAI/BattleAI.h b/AI/BattleAI/BattleAI.h index 679d54f0a..dd9499d51 100644 --- a/AI/BattleAI/BattleAI.h +++ b/AI/BattleAI/BattleAI.h @@ -60,6 +60,7 @@ class CBattleAI : public CBattleGameInterface //Previous setting of cb bool wasWaitingForRealize, wasUnlockingGs; + int movesSkippedByDefense; public: CBattleAI(); diff --git a/AI/BattleAI/BattleExchangeVariant.cpp b/AI/BattleAI/BattleExchangeVariant.cpp index cbc4a2d7f..a0100e4a8 100644 --- a/AI/BattleAI/BattleExchangeVariant.cpp +++ b/AI/BattleAI/BattleExchangeVariant.cpp @@ -355,6 +355,13 @@ int64_t BattleExchangeEvaluator::calculateExchange( logAi->trace("Battle exchange at %lld", ap.attack.shooting ? ap.dest : ap.from); #endif + if(cb->battleGetMySide() == BattlePerspective::LEFT_SIDE + && cb->battleGetGateState() == EGateState::BLOCKED + && ap.attack.defender->coversPos(ESiegeHex::GATE_BRIDGE)) + { + return EvaluationResult::INEFFECTIVE_SCORE; + } + std::vector ourStacks; std::vector enemyStacks; diff --git a/AI/BattleAI/BattleExchangeVariant.h b/AI/BattleAI/BattleExchangeVariant.h index d6082c2b5..6c8e0b448 100644 --- a/AI/BattleAI/BattleExchangeVariant.h +++ b/AI/BattleAI/BattleExchangeVariant.h @@ -42,7 +42,7 @@ struct EvaluationResult bool defend; EvaluationResult(const AttackPossibility & ap) - :wait(false), score(0), bestAttack(ap), defend(false) + :wait(false), score(INEFFECTIVE_SCORE), bestAttack(ap), defend(false) { } }; diff --git a/lib/battle/BattleStateInfoForRetreat.h b/lib/battle/BattleStateInfoForRetreat.h index 4dc713e41..341ed7ffe 100644 --- a/lib/battle/BattleStateInfoForRetreat.h +++ b/lib/battle/BattleStateInfoForRetreat.h @@ -29,6 +29,7 @@ public: std::vector enemyStacks; const CGHeroInstance * ourHero; const CGHeroInstance * enemyHero; + int turnsSkippedByDefense; BattleStateInfoForRetreat(); uint64_t getOurStrength() const;