Browse Source

BattleAI: avoid selfblocking on siege

Andrii Danylchenko 2 years ago
parent
commit
737c34b8c6

+ 3 - 1
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;

+ 35 - 10
AI/BattleAI/BattleAI.cpp

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

+ 1 - 0
AI/BattleAI/BattleAI.h

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

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

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

+ 1 - 0
lib/battle/BattleStateInfoForRetreat.h

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