mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
BattleAI: divide only positive part of score onto turns count
This commit is contained in:
parent
3880ea58b9
commit
9eb9404f28
@ -190,6 +190,14 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
|
|||||||
|
|
||||||
if(stack->waited())
|
if(stack->waited())
|
||||||
{
|
{
|
||||||
|
logAi->debug(
|
||||||
|
"Moving %s towards hex %s[%d], score: %2f/%2f",
|
||||||
|
stack->getDescription(),
|
||||||
|
moveTarget.cachedAttack->attack.defender->getDescription(),
|
||||||
|
moveTarget.cachedAttack->attack.defender->getPosition().hex,
|
||||||
|
moveTarget.score,
|
||||||
|
moveTarget.scorePerTurn);
|
||||||
|
|
||||||
return goTowardsNearest(stack, moveTarget.positions);
|
return goTowardsNearest(stack, moveTarget.positions);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -572,7 +580,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ps.value = scoreEvaluator.calculateExchange(*cachedAttack, *targets, innerCache, state);
|
ps.value = scoreEvaluator.evaluateExchange(*cachedAttack, *targets, innerCache, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto unit : allUnits)
|
for(auto unit : allUnits)
|
||||||
|
@ -18,10 +18,8 @@ AttackerValue::AttackerValue()
|
|||||||
}
|
}
|
||||||
|
|
||||||
MoveTarget::MoveTarget()
|
MoveTarget::MoveTarget()
|
||||||
: positions(), cachedAttack()
|
: positions(), cachedAttack(), score(EvaluationResult::INEFFECTIVE_SCORE), scorePerTurn(EvaluationResult::INEFFECTIVE_SCORE)
|
||||||
{
|
{
|
||||||
score = EvaluationResult::INEFFECTIVE_SCORE;
|
|
||||||
scorePerTurn = EvaluationResult::INEFFECTIVE_SCORE;
|
|
||||||
turnsToRich = 1;
|
turnsToRich = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +56,7 @@ float BattleExchangeVariant::trackAttack(
|
|||||||
auto attackerDamageReduce = AttackPossibility::calculateDamageReduce(defender.get(), unitToUpdate.get(), retaliationDamage, damageCache, hb);
|
auto attackerDamageReduce = AttackPossibility::calculateDamageReduce(defender.get(), unitToUpdate.get(), retaliationDamage, damageCache, hb);
|
||||||
|
|
||||||
attackValue -= attackerDamageReduce;
|
attackValue -= attackerDamageReduce;
|
||||||
dpsScore -= attackerDamageReduce * negativeEffectMultiplier;
|
dpsScore.ourDamageReduce += attackerDamageReduce;
|
||||||
attackerValue[unitToUpdate->unitId()].isRetalitated = true;
|
attackerValue[unitToUpdate->unitId()].isRetalitated = true;
|
||||||
|
|
||||||
unitToUpdate->damage(retaliationDamage);
|
unitToUpdate->damage(retaliationDamage);
|
||||||
@ -80,7 +78,7 @@ float BattleExchangeVariant::trackAttack(
|
|||||||
auto collateralDamageReduce = AttackPossibility::calculateDamageReduce(attacker.get(), unitToUpdate.get(), collateralDamage, damageCache, hb);
|
auto collateralDamageReduce = AttackPossibility::calculateDamageReduce(attacker.get(), unitToUpdate.get(), collateralDamage, damageCache, hb);
|
||||||
|
|
||||||
attackValue -= collateralDamageReduce;
|
attackValue -= collateralDamageReduce;
|
||||||
dpsScore -= collateralDamageReduce * negativeEffectMultiplier;
|
dpsScore.ourDamageReduce += collateralDamageReduce;
|
||||||
|
|
||||||
unitToUpdate->damage(collateralDamage);
|
unitToUpdate->damage(collateralDamage);
|
||||||
|
|
||||||
@ -101,7 +99,7 @@ float BattleExchangeVariant::trackAttack(
|
|||||||
float defenderDamageReduce = AttackPossibility::calculateDamageReduce(attacker.get(), unitToUpdate.get(), attackDamage, damageCache, hb);
|
float defenderDamageReduce = AttackPossibility::calculateDamageReduce(attacker.get(), unitToUpdate.get(), attackDamage, damageCache, hb);
|
||||||
|
|
||||||
attackValue += defenderDamageReduce;
|
attackValue += defenderDamageReduce;
|
||||||
dpsScore += defenderDamageReduce * positiveEffectMultiplier;
|
dpsScore.enemyDamageReduce += defenderDamageReduce;
|
||||||
attackerValue[attacker->unitId()].value += defenderDamageReduce;
|
attackerValue[attacker->unitId()].value += defenderDamageReduce;
|
||||||
|
|
||||||
unitToUpdate->damage(attackDamage);
|
unitToUpdate->damage(attackDamage);
|
||||||
@ -119,7 +117,7 @@ float BattleExchangeVariant::trackAttack(
|
|||||||
}
|
}
|
||||||
|
|
||||||
attackValue += ap.shootersBlockedDmg;
|
attackValue += ap.shootersBlockedDmg;
|
||||||
dpsScore += ap.shootersBlockedDmg * positiveEffectMultiplier;
|
dpsScore.enemyDamageReduce += ap.shootersBlockedDmg;
|
||||||
attacker->afterAttack(ap.attack.shooting, false);
|
attacker->afterAttack(ap.attack.shooting, false);
|
||||||
|
|
||||||
return attackValue;
|
return attackValue;
|
||||||
@ -156,11 +154,11 @@ float BattleExchangeVariant::trackAttack(
|
|||||||
|
|
||||||
if(isOurAttack)
|
if(isOurAttack)
|
||||||
{
|
{
|
||||||
dpsScore += defenderDamageReduce * positiveEffectMultiplier;
|
dpsScore.enemyDamageReduce += defenderDamageReduce;
|
||||||
attackerValue[attacker->unitId()].value += defenderDamageReduce;
|
attackerValue[attacker->unitId()].value += defenderDamageReduce;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
dpsScore -= defenderDamageReduce * negativeEffectMultiplier;
|
dpsScore.ourDamageReduce += defenderDamageReduce;
|
||||||
|
|
||||||
defender->damage(attackDamage);
|
defender->damage(attackDamage);
|
||||||
attacker->afterAttack(shooting, false);
|
attacker->afterAttack(shooting, false);
|
||||||
@ -182,12 +180,12 @@ float BattleExchangeVariant::trackAttack(
|
|||||||
|
|
||||||
if(isOurAttack)
|
if(isOurAttack)
|
||||||
{
|
{
|
||||||
dpsScore -= attackerDamageReduce * negativeEffectMultiplier;
|
dpsScore.ourDamageReduce += attackerDamageReduce;
|
||||||
attackerValue[attacker->unitId()].isRetalitated = true;
|
attackerValue[attacker->unitId()].isRetalitated = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dpsScore += attackerDamageReduce * positiveEffectMultiplier;
|
dpsScore.enemyDamageReduce += attackerDamageReduce;
|
||||||
attackerValue[defender->unitId()].value += attackerDamageReduce;
|
attackerValue[defender->unitId()].value += attackerDamageReduce;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,13 +198,18 @@ float BattleExchangeVariant::trackAttack(
|
|||||||
#if BATTLE_TRACE_LEVEL>=1
|
#if BATTLE_TRACE_LEVEL>=1
|
||||||
if(!score)
|
if(!score)
|
||||||
{
|
{
|
||||||
logAi->trace("Attack has zero score d:%2f a:%2f", defenderDamageReduce, attackerDamageReduce);
|
logAi->trace("Attack has zero score def:%2f att:%2f", defenderDamageReduce, attackerDamageReduce);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float BattleExchangeEvaluator::scoreValue(const BattleScore & score) const
|
||||||
|
{
|
||||||
|
return score.enemyDamageReduce * getPositiveEffectMultiplier() - score.ourDamageReduce * getNegativeEffectMultiplier();
|
||||||
|
}
|
||||||
|
|
||||||
EvaluationResult BattleExchangeEvaluator::findBestTarget(
|
EvaluationResult BattleExchangeEvaluator::findBestTarget(
|
||||||
const battle::Unit * activeStack,
|
const battle::Unit * activeStack,
|
||||||
PotentialTargets & targets,
|
PotentialTargets & targets,
|
||||||
@ -215,7 +218,7 @@ EvaluationResult BattleExchangeEvaluator::findBestTarget(
|
|||||||
{
|
{
|
||||||
EvaluationResult result(targets.bestAction());
|
EvaluationResult result(targets.bestAction());
|
||||||
|
|
||||||
if(!activeStack->waited())
|
if(!activeStack->waited() && !activeStack->acquireState()->hadMorale)
|
||||||
{
|
{
|
||||||
#if BATTLE_TRACE_LEVEL>=1
|
#if BATTLE_TRACE_LEVEL>=1
|
||||||
logAi->trace("Evaluating waited attack for %s", activeStack->getDescription());
|
logAi->trace("Evaluating waited attack for %s", activeStack->getDescription());
|
||||||
@ -230,7 +233,7 @@ EvaluationResult BattleExchangeEvaluator::findBestTarget(
|
|||||||
|
|
||||||
for(auto & ap : targets.possibleAttacks)
|
for(auto & ap : targets.possibleAttacks)
|
||||||
{
|
{
|
||||||
float score = calculateExchange(ap, targets, damageCache, hbWaited);
|
float score = evaluateExchange(ap, targets, damageCache, hbWaited);
|
||||||
|
|
||||||
if(score > result.score)
|
if(score > result.score)
|
||||||
{
|
{
|
||||||
@ -247,9 +250,15 @@ EvaluationResult BattleExchangeEvaluator::findBestTarget(
|
|||||||
|
|
||||||
updateReachabilityMap(hb);
|
updateReachabilityMap(hb);
|
||||||
|
|
||||||
|
if(result.bestAttack.attack.shooting && hb->battleHasShootingPenalty(activeStack, result.bestAttack.dest))
|
||||||
|
{
|
||||||
|
if(!canBeHitThisTurn(result.bestAttack))
|
||||||
|
return result; // lets wait
|
||||||
|
}
|
||||||
|
|
||||||
for(auto & ap : targets.possibleAttacks)
|
for(auto & ap : targets.possibleAttacks)
|
||||||
{
|
{
|
||||||
float score = calculateExchange(ap, targets, damageCache, hb);
|
float score = evaluateExchange(ap, targets, damageCache, hb);
|
||||||
|
|
||||||
if(score >= result.score)
|
if(score >= result.score)
|
||||||
{
|
{
|
||||||
@ -269,7 +278,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
|
|||||||
std::shared_ptr<HypotheticBattle> hb)
|
std::shared_ptr<HypotheticBattle> hb)
|
||||||
{
|
{
|
||||||
MoveTarget result;
|
MoveTarget result;
|
||||||
BattleExchangeVariant ev(getPositiveEffectMultiplier(), getNegativeEffectMultiplier());
|
BattleExchangeVariant ev;
|
||||||
|
|
||||||
if(targets.unreachableEnemies.empty())
|
if(targets.unreachableEnemies.empty())
|
||||||
return result;
|
return result;
|
||||||
@ -301,6 +310,9 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
|
|||||||
|
|
||||||
auto turnsToRich = (distance - 1) / speed + 1;
|
auto turnsToRich = (distance - 1) / speed + 1;
|
||||||
auto hexes = closestStack->getSurroundingHexes();
|
auto hexes = closestStack->getSurroundingHexes();
|
||||||
|
auto enemySpeed = closestStack->speed();
|
||||||
|
auto speedRatio = speed / static_cast<float>(enemySpeed);
|
||||||
|
auto penalty = speedRatio > 1 ? 1 : speedRatio;
|
||||||
|
|
||||||
for(auto hex : hexes)
|
for(auto hex : hexes)
|
||||||
{
|
{
|
||||||
@ -310,13 +322,13 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
|
|||||||
|
|
||||||
attack.shootersBlockedDmg = 0; // we do not want to count on it, it is not for sure
|
attack.shootersBlockedDmg = 0; // we do not want to count on it, it is not for sure
|
||||||
|
|
||||||
auto score = calculateExchange(attack, targets, damageCache, hb);
|
auto score = calculateExchange(attack, turnsToRich, targets, damageCache, hb);
|
||||||
auto scorePerTurn = score / turnsToRich;
|
auto scorePerTurn = BattleScore(score.enemyDamageReduce * std::sqrt(penalty / turnsToRich), score.ourDamageReduce);
|
||||||
|
|
||||||
if(result.scorePerTurn < scorePerTurn)
|
if(result.scorePerTurn < scoreValue(scorePerTurn))
|
||||||
{
|
{
|
||||||
result.scorePerTurn = scorePerTurn;
|
result.scorePerTurn = scoreValue(scorePerTurn);
|
||||||
result.score = score;
|
result.score = scoreValue(score);
|
||||||
result.positions = closestStack->getAttackableHexes(activeStack);
|
result.positions = closestStack->getAttackableHexes(activeStack);
|
||||||
result.cachedAttack = attack;
|
result.cachedAttack = attack;
|
||||||
result.turnsToRich = turnsToRich;
|
result.turnsToRich = turnsToRich;
|
||||||
@ -357,16 +369,17 @@ std::vector<const battle::Unit *> BattleExchangeEvaluator::getAdjacentUnits(cons
|
|||||||
return checkedStacks;
|
return checkedStacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const battle::Unit *> BattleExchangeEvaluator::getExchangeUnits(
|
ReachabilityData BattleExchangeEvaluator::getExchangeUnits(
|
||||||
const AttackPossibility & ap,
|
const AttackPossibility & ap,
|
||||||
PotentialTargets & targets,
|
PotentialTargets & targets,
|
||||||
std::shared_ptr<HypotheticBattle> hb)
|
std::shared_ptr<HypotheticBattle> hb)
|
||||||
{
|
{
|
||||||
|
ReachabilityData result;
|
||||||
|
|
||||||
auto hexes = ap.attack.defender->getHexes();
|
auto hexes = ap.attack.defender->getHexes();
|
||||||
|
|
||||||
if(!ap.attack.shooting) hexes.push_back(ap.from);
|
if(!ap.attack.shooting) hexes.push_back(ap.from);
|
||||||
|
|
||||||
std::vector<const battle::Unit *> exchangeUnits;
|
|
||||||
std::vector<const battle::Unit *> allReachableUnits;
|
std::vector<const battle::Unit *> allReachableUnits;
|
||||||
|
|
||||||
for(auto hex : hexes)
|
for(auto hex : hexes)
|
||||||
@ -404,7 +417,28 @@ std::vector<const battle::Unit *> BattleExchangeEvaluator::getExchangeUnits(
|
|||||||
logAi->trace("Reachability map contains only %d stacks", allReachableUnits.size());
|
logAi->trace("Reachability map contains only %d stacks", allReachableUnits.size());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return exchangeUnits;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto unit : allReachableUnits)
|
||||||
|
{
|
||||||
|
auto accessible = !unit->canShoot();
|
||||||
|
|
||||||
|
if(!accessible)
|
||||||
|
{
|
||||||
|
for(auto hex : unit->getSurroundingHexes())
|
||||||
|
{
|
||||||
|
if(ap.attack.defender->coversPos(hex))
|
||||||
|
{
|
||||||
|
accessible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(accessible)
|
||||||
|
result.melleeAccessible.push_back(unit);
|
||||||
|
else
|
||||||
|
result.shooters.push_back(unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int turn = 0; turn < turnOrder.size(); turn++)
|
for(int turn = 0; turn < turnOrder.size(); turn++)
|
||||||
@ -412,19 +446,30 @@ std::vector<const battle::Unit *> BattleExchangeEvaluator::getExchangeUnits(
|
|||||||
for(auto unit : turnOrder[turn])
|
for(auto unit : turnOrder[turn])
|
||||||
{
|
{
|
||||||
if(vstd::contains(allReachableUnits, unit))
|
if(vstd::contains(allReachableUnits, unit))
|
||||||
exchangeUnits.push_back(unit);
|
result.units.push_back(unit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vstd::erase_if(exchangeUnits, [&](const battle::Unit * u) -> bool
|
vstd::erase_if(result.units, [&](const battle::Unit * u) -> bool
|
||||||
{
|
{
|
||||||
return !hb->battleGetUnitByID(u->unitId())->alive();
|
return !hb->battleGetUnitByID(u->unitId())->alive();
|
||||||
});
|
});
|
||||||
|
|
||||||
return exchangeUnits;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
float BattleExchangeEvaluator::calculateExchange(
|
float BattleExchangeEvaluator::evaluateExchange(
|
||||||
|
const AttackPossibility & ap,
|
||||||
|
PotentialTargets & targets,
|
||||||
|
DamageCache & damageCache,
|
||||||
|
std::shared_ptr<HypotheticBattle> hb)
|
||||||
|
{
|
||||||
|
BattleScore score = calculateExchange(ap, targets, damageCache, hb);
|
||||||
|
|
||||||
|
return scoreValue(score);
|
||||||
|
}
|
||||||
|
|
||||||
|
BattleScore BattleExchangeEvaluator::calculateExchange(
|
||||||
const AttackPossibility & ap,
|
const AttackPossibility & ap,
|
||||||
PotentialTargets & targets,
|
PotentialTargets & targets,
|
||||||
DamageCache & damageCache,
|
DamageCache & damageCache,
|
||||||
@ -438,7 +483,7 @@ float BattleExchangeEvaluator::calculateExchange(
|
|||||||
&& cb->battleGetGateState() == EGateState::BLOCKED
|
&& cb->battleGetGateState() == EGateState::BLOCKED
|
||||||
&& ap.attack.defender->coversPos(BattleHex::GATE_BRIDGE))
|
&& ap.attack.defender->coversPos(BattleHex::GATE_BRIDGE))
|
||||||
{
|
{
|
||||||
return EvaluationResult::INEFFECTIVE_SCORE;
|
return BattleScore(EvaluationResult::INEFFECTIVE_SCORE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const battle::Unit *> ourStacks;
|
std::vector<const battle::Unit *> ourStacks;
|
||||||
@ -447,17 +492,19 @@ float BattleExchangeEvaluator::calculateExchange(
|
|||||||
if(hb->battleGetUnitByID(ap.attack.defender->unitId())->alive())
|
if(hb->battleGetUnitByID(ap.attack.defender->unitId())->alive())
|
||||||
enemyStacks.push_back(ap.attack.defender);
|
enemyStacks.push_back(ap.attack.defender);
|
||||||
|
|
||||||
std::vector<const battle::Unit *> exchangeUnits = getExchangeUnits(ap, targets, hb);
|
vstd::amin(turn, reachabilityMapByTurns.size() - 1);
|
||||||
|
|
||||||
if(exchangeUnits.empty())
|
ReachabilityData exchangeUnits = getExchangeUnits(ap, turn, targets, hb);
|
||||||
|
|
||||||
|
if(exchangeUnits.units.empty())
|
||||||
{
|
{
|
||||||
return 0;
|
return BattleScore();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto exchangeBattle = std::make_shared<HypotheticBattle>(env.get(), hb);
|
auto exchangeBattle = std::make_shared<HypotheticBattle>(env.get(), hb);
|
||||||
BattleExchangeVariant v(getPositiveEffectMultiplier(), getNegativeEffectMultiplier());
|
BattleExchangeVariant v;
|
||||||
|
|
||||||
for(auto unit : exchangeUnits)
|
for(auto unit : exchangeUnits.units)
|
||||||
{
|
{
|
||||||
if(unit->isTurret())
|
if(unit->isTurret())
|
||||||
continue;
|
continue;
|
||||||
@ -481,7 +528,7 @@ float BattleExchangeEvaluator::calculateExchange(
|
|||||||
|
|
||||||
bool canUseAp = true;
|
bool canUseAp = true;
|
||||||
|
|
||||||
for(auto activeUnit : exchangeUnits)
|
for(auto activeUnit : exchangeUnits.units)
|
||||||
{
|
{
|
||||||
bool isOur = exchangeBattle->battleMatchOwner(ap.attack.attacker, activeUnit, true);
|
bool isOur = exchangeBattle->battleMatchOwner(ap.attack.attacker, activeUnit, true);
|
||||||
battle::Units & attackerQueue = isOur ? ourStacks : enemyStacks;
|
battle::Units & attackerQueue = isOur ? ourStacks : enemyStacks;
|
||||||
@ -521,9 +568,16 @@ float BattleExchangeEvaluator::calculateExchange(
|
|||||||
return score;
|
return score;
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!oppositeQueue.empty())
|
auto unitsInOppositeQueueExceptInaccessible = oppositeQueue;
|
||||||
|
|
||||||
|
vstd::erase_if(unitsInOppositeQueueExceptInaccessible, [&](const battle::Unit * u)->bool
|
||||||
{
|
{
|
||||||
targetUnit = *vstd::maxElementByFun(oppositeQueue, estimateAttack);
|
return vstd::contains(exchangeUnits.shooters, u);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!unitsInOppositeQueueExceptInaccessible.empty())
|
||||||
|
{
|
||||||
|
targetUnit = *vstd::maxElementByFun(unitsInOppositeQueueExceptInaccessible, estimateAttack);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -594,7 +648,7 @@ float BattleExchangeEvaluator::calculateExchange(
|
|||||||
v.adjustPositions(melleeAttackers, ap, reachabilityMap);
|
v.adjustPositions(melleeAttackers, ap, reachabilityMap);
|
||||||
|
|
||||||
#if BATTLE_TRACE_LEVEL>=1
|
#if BATTLE_TRACE_LEVEL>=1
|
||||||
logAi->trace("Exchange score: %2f", v.getScore());
|
logAi->trace("Exchange score: enemy: %2f, our -%2f", v.getScore().enemyDamageReduce, v.getScore().ourDamageReduce);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return v.getScore();
|
return v.getScore();
|
||||||
@ -662,10 +716,26 @@ void BattleExchangeVariant::adjustPositions(
|
|||||||
|
|
||||||
if(notRealizedDamage > ap.attackValue() && notRealizedDamage > attackerValue[ap.attack.attacker->unitId()].value)
|
if(notRealizedDamage > ap.attackValue() && notRealizedDamage > attackerValue[ap.attack.attacker->unitId()].value)
|
||||||
{
|
{
|
||||||
dpsScore = EvaluationResult::INEFFECTIVE_SCORE;
|
dpsScore = BattleScore(EvaluationResult::INEFFECTIVE_SCORE, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BattleExchangeEvaluator::canBeHitThisTurn(const AttackPossibility & ap)
|
||||||
|
{
|
||||||
|
for(auto pos : ap.attack.attacker->getSurroundingHexes())
|
||||||
|
{
|
||||||
|
for(auto u : reachabilityMap[pos])
|
||||||
|
{
|
||||||
|
if(u->unitSide() != ap.attack.attacker->unitSide())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void BattleExchangeEvaluator::updateReachabilityMap( std::shared_ptr<HypotheticBattle> hb)
|
void BattleExchangeEvaluator::updateReachabilityMap( std::shared_ptr<HypotheticBattle> hb)
|
||||||
{
|
{
|
||||||
const int TURN_DEPTH = 2;
|
const int TURN_DEPTH = 2;
|
||||||
|
@ -14,6 +14,34 @@
|
|||||||
#include "PotentialTargets.h"
|
#include "PotentialTargets.h"
|
||||||
#include "StackWithBonuses.h"
|
#include "StackWithBonuses.h"
|
||||||
|
|
||||||
|
struct BattleScore
|
||||||
|
{
|
||||||
|
float ourDamageReduce;
|
||||||
|
float enemyDamageReduce;
|
||||||
|
|
||||||
|
BattleScore(float enemyDamageReduce, float ourDamageReduce)
|
||||||
|
:enemyDamageReduce(enemyDamageReduce), ourDamageReduce(ourDamageReduce)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BattleScore() : BattleScore(0, 0) {}
|
||||||
|
|
||||||
|
float value()
|
||||||
|
{
|
||||||
|
return enemyDamageReduce - ourDamageReduce;
|
||||||
|
}
|
||||||
|
|
||||||
|
BattleScore operator+(BattleScore & other)
|
||||||
|
{
|
||||||
|
BattleScore result = *this;
|
||||||
|
|
||||||
|
result.ourDamageReduce += other.ourDamageReduce;
|
||||||
|
result.enemyDamageReduce += other.enemyDamageReduce;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct AttackerValue
|
struct AttackerValue
|
||||||
{
|
{
|
||||||
float value;
|
float value;
|
||||||
@ -59,8 +87,8 @@ struct EvaluationResult
|
|||||||
class BattleExchangeVariant
|
class BattleExchangeVariant
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BattleExchangeVariant(float positiveEffectMultiplier, float negativeEffectMultiplier)
|
BattleExchangeVariant()
|
||||||
: dpsScore(0), positiveEffectMultiplier(positiveEffectMultiplier), negativeEffectMultiplier(negativeEffectMultiplier) {}
|
: dpsScore() {}
|
||||||
|
|
||||||
float trackAttack(
|
float trackAttack(
|
||||||
const AttackPossibility & ap,
|
const AttackPossibility & ap,
|
||||||
@ -76,7 +104,7 @@ public:
|
|||||||
std::shared_ptr<HypotheticBattle> hb,
|
std::shared_ptr<HypotheticBattle> hb,
|
||||||
bool evaluateOnly = false);
|
bool evaluateOnly = false);
|
||||||
|
|
||||||
float getScore() const { return dpsScore; }
|
const BattleScore & getScore() const { return dpsScore; }
|
||||||
|
|
||||||
void adjustPositions(
|
void adjustPositions(
|
||||||
std::vector<const battle::Unit *> attackers,
|
std::vector<const battle::Unit *> attackers,
|
||||||
@ -84,12 +112,21 @@ public:
|
|||||||
std::map<BattleHex, battle::Units> & reachabilityMap);
|
std::map<BattleHex, battle::Units> & reachabilityMap);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float positiveEffectMultiplier;
|
BattleScore dpsScore;
|
||||||
float negativeEffectMultiplier;
|
|
||||||
float dpsScore;
|
|
||||||
std::map<uint32_t, AttackerValue> attackerValue;
|
std::map<uint32_t, AttackerValue> attackerValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ReachabilityData
|
||||||
|
{
|
||||||
|
std::vector<const battle::Unit *> units;
|
||||||
|
|
||||||
|
// shooters which are within mellee attack and mellee units
|
||||||
|
std::vector<const battle::Unit *> melleeAccessible;
|
||||||
|
|
||||||
|
// far shooters
|
||||||
|
std::vector<const battle::Unit *> shooters;
|
||||||
|
};
|
||||||
|
|
||||||
class BattleExchangeEvaluator
|
class BattleExchangeEvaluator
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@ -99,12 +136,22 @@ private:
|
|||||||
std::vector<battle::Units> turnOrder;
|
std::vector<battle::Units> turnOrder;
|
||||||
float negativeEffectMultiplier;
|
float negativeEffectMultiplier;
|
||||||
|
|
||||||
|
float scoreValue(const BattleScore & score) const;
|
||||||
|
|
||||||
|
BattleScore calculateExchange(
|
||||||
|
const AttackPossibility & ap,
|
||||||
|
PotentialTargets & targets,
|
||||||
|
DamageCache & damageCache,
|
||||||
|
std::shared_ptr<HypotheticBattle> hb);
|
||||||
|
|
||||||
|
bool canBeHitThisTurn(const AttackPossibility & ap);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BattleExchangeEvaluator(
|
BattleExchangeEvaluator(
|
||||||
std::shared_ptr<CBattleInfoCallback> cb,
|
std::shared_ptr<CBattleInfoCallback> cb,
|
||||||
std::shared_ptr<Environment> env,
|
std::shared_ptr<Environment> env,
|
||||||
float strengthRatio): cb(cb), env(env) {
|
float strengthRatio): cb(cb), env(env) {
|
||||||
negativeEffectMultiplier = strengthRatio;
|
negativeEffectMultiplier = std::sqrt(strengthRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
EvaluationResult findBestTarget(
|
EvaluationResult findBestTarget(
|
||||||
@ -113,14 +160,20 @@ public:
|
|||||||
DamageCache & damageCache,
|
DamageCache & damageCache,
|
||||||
std::shared_ptr<HypotheticBattle> hb);
|
std::shared_ptr<HypotheticBattle> hb);
|
||||||
|
|
||||||
float calculateExchange(
|
float evaluateExchange(
|
||||||
const AttackPossibility & ap,
|
const AttackPossibility & ap,
|
||||||
PotentialTargets & targets,
|
PotentialTargets & targets,
|
||||||
DamageCache & damageCache,
|
DamageCache & damageCache,
|
||||||
std::shared_ptr<HypotheticBattle> hb);
|
std::shared_ptr<HypotheticBattle> hb);
|
||||||
|
|
||||||
void updateReachabilityMap(std::shared_ptr<HypotheticBattle> hb);
|
void updateReachabilityMap(std::shared_ptr<HypotheticBattle> hb);
|
||||||
std::vector<const battle::Unit *> getExchangeUnits(const AttackPossibility & ap, PotentialTargets & targets, std::shared_ptr<HypotheticBattle> hb);
|
|
||||||
|
ReachabilityData getExchangeUnits(
|
||||||
|
const AttackPossibility & ap,
|
||||||
|
uint8_t turn,
|
||||||
|
PotentialTargets & targets,
|
||||||
|
std::shared_ptr<HypotheticBattle> hb);
|
||||||
|
|
||||||
bool checkPositionBlocksOurStacks(HypotheticBattle & hb, const battle::Unit * unit, BattleHex position);
|
bool checkPositionBlocksOurStacks(HypotheticBattle & hb, const battle::Unit * unit, BattleHex position);
|
||||||
|
|
||||||
MoveTarget findMoveTowardsUnreachable(
|
MoveTarget findMoveTowardsUnreachable(
|
||||||
@ -131,6 +184,6 @@ public:
|
|||||||
|
|
||||||
std::vector<const battle::Unit *> getAdjacentUnits(const battle::Unit * unit);
|
std::vector<const battle::Unit *> getAdjacentUnits(const battle::Unit * unit);
|
||||||
|
|
||||||
float getPositiveEffectMultiplier() { return 1; }
|
float getPositiveEffectMultiplier() const { return 1; }
|
||||||
float getNegativeEffectMultiplier() { return negativeEffectMultiplier; }
|
float getNegativeEffectMultiplier() const { return negativeEffectMultiplier; }
|
||||||
};
|
};
|
||||||
|
@ -274,6 +274,7 @@ void Nullkiller::makeTurn()
|
|||||||
|
|
||||||
bestTask = choseBestTask(bestTasks);
|
bestTask = choseBestTask(bestTasks);
|
||||||
|
|
||||||
|
std::string taskDescription = bestTask->toString();
|
||||||
HeroPtr hero = bestTask->getHero();
|
HeroPtr hero = bestTask->getHero();
|
||||||
HeroRole heroRole = HeroRole::MAIN;
|
HeroRole heroRole = HeroRole::MAIN;
|
||||||
|
|
||||||
@ -292,7 +293,7 @@ void Nullkiller::makeTurn()
|
|||||||
|
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
"Goal %s has low priority %f so decreasing scan depth to gain performance.",
|
"Goal %s has low priority %f so decreasing scan depth to gain performance.",
|
||||||
bestTask->toString(),
|
taskDescription,
|
||||||
bestTask->priority);
|
bestTask->priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,7 +309,7 @@ void Nullkiller::makeTurn()
|
|||||||
{
|
{
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
"Goal %s has too low priority %f so increasing scan depth to full.",
|
"Goal %s has too low priority %f so increasing scan depth to full.",
|
||||||
bestTask->toString(),
|
taskDescription,
|
||||||
bestTask->priority);
|
bestTask->priority);
|
||||||
|
|
||||||
scanDepth = ScanDepth::ALL_FULL;
|
scanDepth = ScanDepth::ALL_FULL;
|
||||||
@ -316,7 +317,7 @@ void Nullkiller::makeTurn()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
logAi->trace("Goal %s has too low priority. It is not worth doing it. Ending turn.", bestTask->toString());
|
logAi->trace("Goal %s has too low priority. It is not worth doing it. Ending turn.", taskDescription);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -325,7 +326,7 @@ void Nullkiller::makeTurn()
|
|||||||
|
|
||||||
if(i == MAXPASS)
|
if(i == MAXPASS)
|
||||||
{
|
{
|
||||||
logAi->error("Goal %s exceeded maxpass. Terminating AI turn.", bestTask->toString());
|
logAi->error("Goal %s exceeded maxpass. Terminating AI turn.", taskDescription);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user