1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-05 00:49:09 +02:00

Merge pull request #1909 from vcmi/avoid-freeze-on-siege-selfblocking

BattleAI: avoid selfblocking on siege
This commit is contained in:
Ivan Savenko
2023-04-11 11:11:05 +03:00
committed by GitHub
6 changed files with 48 additions and 12 deletions

View File

@ -212,11 +212,13 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo & attackInf
// check how much damage we gain from blocking enemy shooters on this hex // check how much damage we gain from blocking enemy shooters on this hex
bestAp.shootersBlockedDmg = evaluateBlockedShootersDmg(attackInfo, hex, state); 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.attacker->unitType()->getJsonKey(),
attackInfo.defender->unitType()->getJsonKey(), attackInfo.defender->unitType()->getJsonKey(),
(int)bestAp.dest, (int)bestAp.from, (int)bestAp.affectedUnits.size(), (int)bestAp.dest, (int)bestAp.from, (int)bestAp.affectedUnits.size(),
bestAp.defenderDamageReduce, bestAp.attackerDamageReduce, bestAp.collateralDamageReduce, bestAp.shootersBlockedDmg); bestAp.defenderDamageReduce, bestAp.attackerDamageReduce, bestAp.collateralDamageReduce, bestAp.shootersBlockedDmg);
#endif
//TODO other damage related to attack (eg. fire shield and other abilities) //TODO other damage related to attack (eg. fire shield and other abilities)
return bestAp; return bestAp;

View File

@ -89,6 +89,7 @@ void CBattleAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::share
wasUnlockingGs = CB->unlockGsWhenWaiting; wasUnlockingGs = CB->unlockGsWhenWaiting;
CB->waitTillRealize = true; CB->waitTillRealize = true;
CB->unlockGsWhenWaiting = false; CB->unlockGsWhenWaiting = false;
movesSkippedByDefense = 0;
} }
BattleAction CBattleAI::activeStack( const CStack * stack ) BattleAction CBattleAI::activeStack( const CStack * stack )
@ -181,6 +182,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
if(bestSpellcast.is_initialized() && bestSpellcast->value > bestAttack.damageDiff()) if(bestSpellcast.is_initialized() && bestSpellcast->value > bestAttack.damageDiff())
{ {
// return because spellcast value is damage dealt and score is dps reduce // return because spellcast value is damage dealt and score is dps reduce
movesSkippedByDefense = 0;
return BattleAction::makeCreatureSpellcast(stack, bestSpellcast->dest, bestSpellcast->spell->id); return BattleAction::makeCreatureSpellcast(stack, bestSpellcast->dest, bestSpellcast->spell->id);
} }
@ -196,14 +198,15 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
} }
else if(bestAttack.attack.shooting) else if(bestAttack.attack.shooting)
{ {
result = BattleAction::makeShotAttack(stack, bestAttack.attack.defender); result = BattleAction::makeShotAttack(stack, bestAttack.attack.defender);
action = "shot"; action = "shot";
movesSkippedByDefense = 0;
} }
else else
{ {
result = BattleAction::makeMeleeAttack(stack, bestAttack.attack.defender->getPosition(), bestAttack.from); result = BattleAction::makeMeleeAttack(stack, bestAttack.attack.defender->getPosition(), bestAttack.from);
action = "melee"; action = "melee";
movesSkippedByDefense = 0;
} }
logAi->debug("BattleAI: %s -> %s x %d, %s, from %d curpos %d dist %d speed %d: +%lld -%lld = %lld", 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()) else if(bestSpellcast.is_initialized())
{ {
movesSkippedByDefense = 0;
return BattleAction::makeCreatureSpellcast(stack, bestSpellcast->dest, bestSpellcast->spell->id); return BattleAction::makeCreatureSpellcast(stack, bestSpellcast->dest, bestSpellcast->spell->id);
} }
@ -235,12 +239,8 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
} }
} }
if(score > EvaluationResult::INEFFECTIVE_SCORE) if(score <= EvaluationResult::INEFFECTIVE_SCORE
{ && !stack->hasBonusOfType(Bonus::FLYING)
return result;
}
if(!stack->hasBonusOfType(Bonus::FLYING)
&& stack->unitSide() == BattleSide::ATTACKER && stack->unitSide() == BattleSide::ATTACKER
&& cb->battleGetSiegeLevel() >= CGTownInstance::CITADEL) && cb->battleGetSiegeLevel() >= CGTownInstance::CITADEL)
{ {
@ -248,10 +248,12 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
if(brokenWallMoat.size()) if(brokenWallMoat.size())
{ {
movesSkippedByDefense = 0;
if(stack->doubleWide() && vstd::contains(brokenWallMoat, stack->getPosition())) 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 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()); 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; return result;
} }
@ -285,7 +296,9 @@ BattleAction CBattleAI::goTowardsNearest(const CStack * stack, std::vector<Battl
for(auto hex : hexes) for(auto hex : hexes)
{ {
if(vstd::contains(avHexes, hex)) if(vstd::contains(avHexes, hex))
{
return BattleAction::makeMove(stack, hex); return BattleAction::makeMove(stack, hex);
}
if(stack->coversPos(hex)) if(stack->coversPos(hex))
{ {
@ -396,6 +409,8 @@ BattleAction CBattleAI::useCatapult(const CStack * stack)
attack.side = side; attack.side = side;
attack.stackNumber = stack->ID; attack.stackNumber = stack->ID;
movesSkippedByDefense = 0;
return attack; return attack;
} }
@ -703,6 +718,7 @@ void CBattleAI::attemptCastingSpell()
spellcast.side = side; spellcast.side = side;
spellcast.stackNumber = (!side) ? -1 : -2; spellcast.stackNumber = (!side) ? -1 : -2;
cb->battleMakeAction(&spellcast); cb->battleMakeAction(&spellcast);
movesSkippedByDefense = 0;
} }
else else
{ {
@ -796,12 +812,21 @@ boost::optional<BattleAction> CBattleAI::considerFleeingOrSurrendering()
} }
} }
bs.turnsSkippedByDefense = movesSkippedByDefense / bs.ourStacks.size();
if(!bs.canFlee || !bs.canSurrender) if(!bs.canFlee || !bs.canSurrender)
{ {
return boost::none; 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;
} }

View File

@ -60,6 +60,7 @@ class CBattleAI : public CBattleGameInterface
//Previous setting of cb //Previous setting of cb
bool wasWaitingForRealize, wasUnlockingGs; bool wasWaitingForRealize, wasUnlockingGs;
int movesSkippedByDefense;
public: public:
CBattleAI(); CBattleAI();

View File

@ -355,6 +355,13 @@ int64_t BattleExchangeEvaluator::calculateExchange(
logAi->trace("Battle exchange at %lld", ap.attack.shooting ? ap.dest : ap.from); logAi->trace("Battle exchange at %lld", ap.attack.shooting ? ap.dest : ap.from);
#endif #endif
if(cb->battleGetMySide() == BattlePerspective::LEFT_SIDE
&& cb->battleGetGateState() == EGateState::BLOCKED
&& ap.attack.defender->coversPos(ESiegeHex::GATE_BRIDGE))
{
return EvaluationResult::INEFFECTIVE_SCORE;
}
std::vector<const battle::Unit *> ourStacks; std::vector<const battle::Unit *> ourStacks;
std::vector<const battle::Unit *> enemyStacks; std::vector<const battle::Unit *> enemyStacks;

View File

@ -42,7 +42,7 @@ struct EvaluationResult
bool defend; bool defend;
EvaluationResult(const AttackPossibility & ap) EvaluationResult(const AttackPossibility & ap)
:wait(false), score(0), bestAttack(ap), defend(false) :wait(false), score(INEFFECTIVE_SCORE), bestAttack(ap), defend(false)
{ {
} }
}; };

View File

@ -29,6 +29,7 @@ public:
std::vector<const battle::Unit *> enemyStacks; std::vector<const battle::Unit *> enemyStacks;
const CGHeroInstance * ourHero; const CGHeroInstance * ourHero;
const CGHeroInstance * enemyHero; const CGHeroInstance * enemyHero;
int turnsSkippedByDefense;
BattleStateInfoForRetreat(); BattleStateInfoForRetreat();
uint64_t getOurStrength() const; uint64_t getOurStrength() const;