2022-01-27 09:00:27 +02:00
|
|
|
/*
|
|
|
|
* BattleExchangeVariant.h, part of VCMI engine
|
|
|
|
*
|
|
|
|
* Authors: listed in file AUTHORS in main folder
|
|
|
|
*
|
|
|
|
* License: GNU General Public License v2.0 or later
|
|
|
|
* Full text of license available in license.txt file, in main folder
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "../../lib/AI_Base.h"
|
|
|
|
#include "../../lib/battle/ReachabilityInfo.h"
|
|
|
|
#include "PotentialTargets.h"
|
|
|
|
#include "StackWithBonuses.h"
|
|
|
|
|
2023-10-03 07:16:12 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-01-27 09:00:27 +02:00
|
|
|
struct AttackerValue
|
|
|
|
{
|
2023-08-26 12:06:41 +02:00
|
|
|
float value;
|
2024-06-24 03:23:26 +02:00
|
|
|
bool isRetaliated;
|
2022-01-27 09:00:27 +02:00
|
|
|
BattleHex position;
|
|
|
|
|
2022-04-11 08:12:41 +02:00
|
|
|
AttackerValue();
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MoveTarget
|
|
|
|
{
|
2023-08-26 12:06:41 +02:00
|
|
|
float score;
|
|
|
|
float scorePerTurn;
|
2022-04-11 08:12:41 +02:00
|
|
|
std::vector<BattleHex> positions;
|
2023-08-08 17:37:42 +02:00
|
|
|
std::optional<AttackPossibility> cachedAttack;
|
|
|
|
uint8_t turnsToRich;
|
2022-04-11 08:12:41 +02:00
|
|
|
|
|
|
|
MoveTarget();
|
|
|
|
};
|
|
|
|
|
|
|
|
struct EvaluationResult
|
|
|
|
{
|
2023-08-26 12:06:41 +02:00
|
|
|
static const int64_t INEFFECTIVE_SCORE = -10000;
|
2022-04-11 08:12:41 +02:00
|
|
|
|
|
|
|
AttackPossibility bestAttack;
|
|
|
|
MoveTarget bestMove;
|
|
|
|
bool wait;
|
2023-08-26 12:06:41 +02:00
|
|
|
float score;
|
2022-04-11 08:12:41 +02:00
|
|
|
bool defend;
|
|
|
|
|
|
|
|
EvaluationResult(const AttackPossibility & ap)
|
2023-04-08 13:05:47 +02:00
|
|
|
:wait(false), score(INEFFECTIVE_SCORE), bestAttack(ap), defend(false)
|
2022-01-27 09:00:27 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-10-17 18:47:16 +02:00
|
|
|
/// <summary>
|
|
|
|
/// The class represents evaluation of attack value
|
|
|
|
/// of exchanges between all stacks which can access particular hex
|
|
|
|
/// starting from initial attack represented by AttackPossibility and further according turn order.
|
|
|
|
/// Negative score value means we get more demage than deal
|
|
|
|
/// </summary>
|
2022-01-27 09:00:27 +02:00
|
|
|
class BattleExchangeVariant
|
|
|
|
{
|
|
|
|
public:
|
2023-10-03 07:16:12 +02:00
|
|
|
BattleExchangeVariant()
|
|
|
|
: dpsScore() {}
|
2022-01-27 09:00:27 +02:00
|
|
|
|
2023-08-26 12:06:41 +02:00
|
|
|
float trackAttack(
|
|
|
|
const AttackPossibility & ap,
|
|
|
|
std::shared_ptr<HypotheticBattle> hb,
|
|
|
|
DamageCache & damageCache);
|
2022-01-27 09:00:27 +02:00
|
|
|
|
2023-08-26 12:06:41 +02:00
|
|
|
float trackAttack(
|
2022-01-27 09:00:27 +02:00
|
|
|
std::shared_ptr<StackWithBonuses> attacker,
|
|
|
|
std::shared_ptr<StackWithBonuses> defender,
|
|
|
|
bool shooting,
|
|
|
|
bool isOurAttack,
|
2023-08-08 17:37:42 +02:00
|
|
|
DamageCache & damageCache,
|
|
|
|
std::shared_ptr<HypotheticBattle> hb,
|
2022-01-27 09:00:27 +02:00
|
|
|
bool evaluateOnly = false);
|
|
|
|
|
2023-10-03 07:16:12 +02:00
|
|
|
const BattleScore & getScore() const { return dpsScore; }
|
2022-01-27 09:00:27 +02:00
|
|
|
|
|
|
|
private:
|
2023-10-03 07:16:12 +02:00
|
|
|
BattleScore dpsScore;
|
2022-01-27 09:00:27 +02:00
|
|
|
std::map<uint32_t, AttackerValue> attackerValue;
|
|
|
|
};
|
|
|
|
|
2023-10-03 07:16:12 +02:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2022-01-27 09:00:27 +02:00
|
|
|
class BattleExchangeEvaluator
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
std::shared_ptr<CBattleInfoCallback> cb;
|
|
|
|
std::shared_ptr<Environment> env;
|
2023-10-13 21:04:26 +02:00
|
|
|
std::map<uint32_t, ReachabilityInfo> reachabilityCache;
|
2022-01-27 09:00:27 +02:00
|
|
|
std::map<BattleHex, std::vector<const battle::Unit *>> reachabilityMap;
|
|
|
|
std::vector<battle::Units> turnOrder;
|
2023-08-19 09:22:34 +02:00
|
|
|
float negativeEffectMultiplier;
|
2022-01-27 09:00:27 +02:00
|
|
|
|
2023-10-03 07:16:12 +02:00
|
|
|
float scoreValue(const BattleScore & score) const;
|
|
|
|
|
|
|
|
BattleScore calculateExchange(
|
|
|
|
const AttackPossibility & ap,
|
2023-10-13 21:04:26 +02:00
|
|
|
uint8_t turn,
|
2023-10-03 07:16:12 +02:00
|
|
|
PotentialTargets & targets,
|
|
|
|
DamageCache & damageCache,
|
2024-06-17 17:28:30 +02:00
|
|
|
std::shared_ptr<HypotheticBattle> hb) const;
|
2023-10-03 07:16:12 +02:00
|
|
|
|
|
|
|
bool canBeHitThisTurn(const AttackPossibility & ap);
|
|
|
|
|
2022-01-27 09:00:27 +02:00
|
|
|
public:
|
2023-08-19 09:22:34 +02:00
|
|
|
BattleExchangeEvaluator(
|
|
|
|
std::shared_ptr<CBattleInfoCallback> cb,
|
|
|
|
std::shared_ptr<Environment> env,
|
|
|
|
float strengthRatio): cb(cb), env(env) {
|
2024-08-04 16:50:19 +02:00
|
|
|
negativeEffectMultiplier = strengthRatio >= 1 ? 1 : strengthRatio * strengthRatio;
|
2023-08-19 09:22:34 +02:00
|
|
|
}
|
2022-01-27 09:00:27 +02:00
|
|
|
|
2023-08-08 17:37:42 +02:00
|
|
|
EvaluationResult findBestTarget(
|
|
|
|
const battle::Unit * activeStack,
|
|
|
|
PotentialTargets & targets,
|
|
|
|
DamageCache & damageCache,
|
|
|
|
std::shared_ptr<HypotheticBattle> hb);
|
|
|
|
|
2023-10-03 07:16:12 +02:00
|
|
|
float evaluateExchange(
|
2023-08-08 17:37:42 +02:00
|
|
|
const AttackPossibility & ap,
|
2023-10-13 21:04:26 +02:00
|
|
|
uint8_t turn,
|
2023-08-08 17:37:42 +02:00
|
|
|
PotentialTargets & targets,
|
|
|
|
DamageCache & damageCache,
|
2024-06-17 17:28:30 +02:00
|
|
|
std::shared_ptr<HypotheticBattle> hb) const;
|
2023-08-08 17:37:42 +02:00
|
|
|
|
2024-06-17 17:28:30 +02:00
|
|
|
std::vector<const battle::Unit *> getOneTurnReachableUnits(uint8_t turn, BattleHex hex) const;
|
2023-08-08 17:37:42 +02:00
|
|
|
void updateReachabilityMap(std::shared_ptr<HypotheticBattle> hb);
|
2023-10-03 07:16:12 +02:00
|
|
|
|
|
|
|
ReachabilityData getExchangeUnits(
|
|
|
|
const AttackPossibility & ap,
|
|
|
|
uint8_t turn,
|
|
|
|
PotentialTargets & targets,
|
2024-06-17 17:28:30 +02:00
|
|
|
std::shared_ptr<HypotheticBattle> hb) const;
|
2023-10-03 07:16:12 +02:00
|
|
|
|
2022-01-27 09:00:27 +02:00
|
|
|
bool checkPositionBlocksOurStacks(HypotheticBattle & hb, const battle::Unit * unit, BattleHex position);
|
2023-08-08 17:37:42 +02:00
|
|
|
|
|
|
|
MoveTarget findMoveTowardsUnreachable(
|
|
|
|
const battle::Unit * activeStack,
|
|
|
|
PotentialTargets & targets,
|
|
|
|
DamageCache & damageCache,
|
|
|
|
std::shared_ptr<HypotheticBattle> hb);
|
|
|
|
|
2023-10-27 19:29:08 +02:00
|
|
|
std::vector<const battle::Unit *> getAdjacentUnits(const battle::Unit * unit) const;
|
2023-08-19 09:22:34 +02:00
|
|
|
|
2023-10-03 07:16:12 +02:00
|
|
|
float getPositiveEffectMultiplier() const { return 1; }
|
|
|
|
float getNegativeEffectMultiplier() const { return negativeEffectMultiplier; }
|
2023-08-28 17:59:12 +02:00
|
|
|
};
|