| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- /*
- * SpellTargetsEvaluatorTest.cpp, 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
- *
- */
- #include "StdInc.h"
- #include "../mock/BattleFake.h"
- #include "../mock/mock_battle_Unit.h"
- #include "../mock/mock_spells_Mechanics.h"
- #include "AI/BattleAI/SpellTargetsEvaluator.h"
- #include "lib/CStack.h"
- #include "lib/battle/CBattleInfoCallback.h"
- namespace test
- {
- using namespace ::testing;
- using namespace spells;
- using namespace battle;
- using PossiblePositions = std::vector<BattleHex>;
- class CBattleInfoCallbackMock : public CBattleInfoCallback
- {
- public:
- MOCK_CONST_METHOD1(battleGetAllUnits, battle::Units(bool));
- MOCK_CONST_METHOD0(getBattle, IBattleInfo *());
- MOCK_CONST_METHOD0(getPlayerID, std::optional<PlayerColor>());
- #if SCRIPTING_ENABLED
- MOCK_CONST_METHOD0(getContextPool, scripting::Pool *());
- #endif
- };
- class CStackMock : public CStack
- {
- public:
- MOCK_CONST_METHOD0(unitSide, BattleSide());
- };
- class SpellTargetEvaluatorTest : public ::testing::Test
- {
- public:
- MechanicsMock mechMock;
- CBattleInfoCallbackMock battleMock;
- battle::Units allUnits;
- TStacks allStacks;
- BattleSide casterSide = BattleSide::ATTACKER;
- BattleSide enemySide = BattleSide::DEFENDER;
- void SetUp() override
- {
- mechMock.casterSide = casterSide;
- ON_CALL(mechMock, battle()).WillByDefault(Return(&battleMock));
- ON_CALL(mechMock, canBeCastAt(_, _)).WillByDefault(Return(true));
- }
- void TearDown() override
- {
- for(const auto * unit : allUnits)
- delete unit;
- allUnits.clear();
- for(const auto * stack : allStacks)
- delete stack;
- allStacks.clear();
- }
- void spellTargetTypes(std::vector<AimType> targetTypes)
- {
- ON_CALL(mechMock, getTargetTypes()).WillByDefault(Return(std::move(targetTypes)));
- }
- CStackMock * addStack(BattleHex position, BattleSide battleSide, bool isSuspectible = true)
- {
- auto * stack = new CStackMock();
- ON_CALL(*stack, unitSide()).WillByDefault(Return(battleSide));
- allStacks.push_back(stack);
- auto * unit = new UnitMock();
- ON_CALL(*unit, getPosition()).WillByDefault(Return(position));
- allUnits.push_back(unit);
- if(!isSuspectible)
- ON_CALL(mechMock, canBeCastAt(Contains(Field(&Destination::hexValue, position)), _)).WillByDefault(Return(false));
- return stack;
- }
- void setAffectedStacksForCast(BattleHex position, std::vector<const CStack *> stacks)
- {
- ON_CALL(mechMock, getAffectedStacks(Contains(Field(&Destination::hexValue, position)))).WillByDefault(Return(stacks));
- }
- void confirmResults(std::vector<PossiblePositions> allRequiredCasts)
- {
- std::vector<Target> result = SpellTargetEvaluator::getViableTargets(&mechMock);
- basicCheck(result);
- ASSERT_EQ(result.size(), allRequiredCasts.size());
- std::vector<BattleHex> targetedHexes;
- targetedHexes.reserve(result.size());
- for(Target target : result)
- targetedHexes.push_back(target.front().hexValue);
- for(const PossiblePositions & requiredCast : allRequiredCasts)
- EXPECT_TRUE(containCommonValue(requiredCast, targetedHexes));
- }
- void basicCheck(std::vector<Target> & result)
- {
- for(const Target & target : result)
- EXPECT_EQ(target.size(), 1); //multi-destination spells are not handled by targetEvaluator
- }
- template<typename T>
- bool containCommonValue(const std::vector<T> & v1, const std::vector<T> & v2)
- {
- for(T val1 : v1)
- {
- for(T val2 : v2)
- {
- if(val1 == val2)
- return true;
- }
- }
- return false;
- }
- };
- TEST_F(SpellTargetEvaluatorTest, ReturnsEmptyIfMultiDestinationSpell)
- {
- spellTargetTypes({AimType::CREATURE, AimType::LOCATION});
- std::vector<Target> result = SpellTargetEvaluator::getViableTargets(&mechMock);
- EXPECT_TRUE(result.empty());
- }
- TEST_F(SpellTargetEvaluatorTest, ReturnSingleEmptyDestinationIfTargetIsNone)
- {
- spellTargetTypes({AimType::NO_TARGET});
- std::vector<Target> result = SpellTargetEvaluator::getViableTargets(&mechMock);
- EXPECT_EQ(result.size(), 1);
- EXPECT_TRUE(result.front().empty());
- }
- TEST_F(SpellTargetEvaluatorTest, ReturnsSuspectibleCreaturePositionsIfSpellTargetsCreatures)
- {
- spellTargetTypes({AimType::CREATURE});
- addStack(BattleHex(1), casterSide);
- addStack(BattleHex(2), enemySide, false);
- addStack(BattleHex(3), casterSide);
- ON_CALL(battleMock, battleGetAllUnits(Eq(false))).WillByDefault(Return(allUnits));
- confirmResults({{BattleHex(1)}, {BattleHex(3)}});
- }
- TEST_F(SpellTargetEvaluatorTest, ReturnsSuspectibleCreaturePositionsAndSingleRandomSurroundingHexForEachStackIfNeutralLocationSpell)
- {
- spellTargetTypes({AimType::LOCATION});
- ON_CALL(mechMock, isNeutralSpell()).WillByDefault(Return(true));
- addStack(BattleHex(72), casterSide);
- addStack(BattleHex(159), enemySide, false);
- addStack(BattleHex(23), casterSide);
- ON_CALL(battleMock, battleGetAllUnits(Eq(false))).WillByDefault(Return(allUnits));
- confirmResults(
- {{BattleHex(72)},
- BattleHex(72).getAllNeighbouringTiles().toVector(),
- BattleHex(159).getAllNeighbouringTiles().toVector(),
- {BattleHex(23)},
- BattleHex(23).getAllNeighbouringTiles().toVector()}
- );
- }
- TEST_F(SpellTargetEvaluatorTest, ReturnsOneCaseOfEachOptimalCastIfNegativeLocationSpell)
- {
- spellTargetTypes({AimType::LOCATION});
- ON_CALL(mechMock, isNegativeSpell()).WillByDefault(Return(true));
- auto * enemyStack1 = addStack(BattleHex(90), enemySide);
- auto * enemyStack2 = addStack(BattleHex(106), enemySide);
- auto * enemyStack3 = addStack(BattleHex(1), enemySide);
- auto * enemyStack4 = addStack(BattleHex(37), enemySide);
- auto * enemyStack5 = addStack(BattleHex(41), enemySide);
- auto * alliedStack1 = addStack(BattleHex(107), casterSide);
- auto * alliedStack2 = addStack(BattleHex(19), casterSide);
- //optimal
- setAffectedStacksForCast(BattleHex(71), {enemyStack1, enemyStack2});
- setAffectedStacksForCast(BattleHex(88), {enemyStack1, enemyStack2});
- //suboptimal
- setAffectedStacksForCast(BattleHex(55), {enemyStack1});
- setAffectedStacksForCast(BattleHex(89), {enemyStack1, enemyStack2, alliedStack1});
- //optimal
- setAffectedStacksForCast(BattleHex(18), {enemyStack3, alliedStack2});
- setAffectedStacksForCast(BattleHex(2), {enemyStack3, alliedStack2});
- //suboptimal
- setAffectedStacksForCast(BattleHex(53), {alliedStack2});
- //optimal
- setAffectedStacksForCast(BattleHex(39), {enemyStack4, enemyStack5});
- //suboptimal
- setAffectedStacksForCast(BattleHex(21), {enemyStack4});
- setAffectedStacksForCast(BattleHex(25), {enemyStack5});
- confirmResults({
- {BattleHex(71), BattleHex(88)},
- {BattleHex(2), BattleHex(18)},
- {BattleHex(39)}
- });
- }
- }
|