1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

BattleAI: avoid selfblocking on siege

This commit is contained in:
Andrii Danylchenko 2023-04-08 14:05:47 +03:00
parent ae5d273df9
commit 737c34b8c6
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
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;

View File

@ -89,6 +89,7 @@ void CBattleAI::initBattleInterface(std::shared_ptr<Environment> 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::vector<Battl
for(auto hex : hexes)
{
if(vstd::contains(avHexes, hex))
{
return BattleAction::makeMove(stack, hex);
}
if(stack->coversPos(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<BattleAction> 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;
}

View File

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

View File

@ -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<const battle::Unit *> ourStacks;
std::vector<const battle::Unit *> enemyStacks;

View File

@ -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)
{
}
};

View File

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