mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-25 22:42:04 +02:00
extract trading logic into ResourceTrader, test preparation WIP
This commit is contained in:
@@ -50,6 +50,7 @@ set(Nullkiller2_SRCS
|
|||||||
Engine/Nullkiller.cpp
|
Engine/Nullkiller.cpp
|
||||||
Engine/DeepDecomposer.cpp
|
Engine/DeepDecomposer.cpp
|
||||||
Engine/PriorityEvaluator.cpp
|
Engine/PriorityEvaluator.cpp
|
||||||
|
Engine/ResourceTrader.cpp
|
||||||
Analyzers/DangerHitMapAnalyzer.cpp
|
Analyzers/DangerHitMapAnalyzer.cpp
|
||||||
Analyzers/BuildAnalyzer.cpp
|
Analyzers/BuildAnalyzer.cpp
|
||||||
Analyzers/ObjectClusterizer.cpp
|
Analyzers/ObjectClusterizer.cpp
|
||||||
@@ -126,6 +127,7 @@ set(Nullkiller2_HEADERS
|
|||||||
Engine/Nullkiller.h
|
Engine/Nullkiller.h
|
||||||
Engine/DeepDecomposer.h
|
Engine/DeepDecomposer.h
|
||||||
Engine/PriorityEvaluator.h
|
Engine/PriorityEvaluator.h
|
||||||
|
Engine/ResourceTrader.h
|
||||||
Analyzers/DangerHitMapAnalyzer.h
|
Analyzers/DangerHitMapAnalyzer.h
|
||||||
Analyzers/BuildAnalyzer.h
|
Analyzers/BuildAnalyzer.h
|
||||||
Analyzers/ObjectClusterizer.h
|
Analyzers/ObjectClusterizer.h
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "../Behaviors/StayAtTownBehavior.h"
|
#include "../Behaviors/StayAtTownBehavior.h"
|
||||||
#include "../Goals/Invalid.h"
|
#include "../Goals/Invalid.h"
|
||||||
#include "Goals/RecruitHero.h"
|
#include "Goals/RecruitHero.h"
|
||||||
|
#include "ResourceTrader.h"
|
||||||
#include <boost/range/algorithm/sort.hpp>
|
#include <boost/range/algorithm/sort.hpp>
|
||||||
|
|
||||||
namespace NK2AI
|
namespace NK2AI
|
||||||
@@ -479,7 +480,7 @@ void Nullkiller::makeTurn()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasAnySuccess |= handleTrading();
|
hasAnySuccess |= ResourceTrader::trade(buildAnalyzer, cc, getFreeResources());
|
||||||
if(!hasAnySuccess)
|
if(!hasAnySuccess)
|
||||||
{
|
{
|
||||||
logAi->trace("Nothing was done this turn. Ending turn.");
|
logAi->trace("Nothing was done this turn. Ending turn.");
|
||||||
@@ -606,160 +607,6 @@ void Nullkiller::lockResources(const TResources & res)
|
|||||||
lockedResources += res;
|
lockedResources += res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Nullkiller::handleTrading()
|
|
||||||
{
|
|
||||||
// TODO: Mircea: Maybe include based on how close danger is: X as default + proportion of close danger or something around that
|
|
||||||
constexpr float ARMY_GOLD_RATIO_PER_MAKE_TURN_PASS = 0.1f;
|
|
||||||
constexpr float EXPENDABLE_BULK_RATIO = 0.25f;
|
|
||||||
bool haveTraded = false;
|
|
||||||
ObjectInstanceID marketId;
|
|
||||||
|
|
||||||
// TODO: Mircea: What about outside town markets that have better rates than a single town for example?
|
|
||||||
// Are those used anywhere? To inspect.
|
|
||||||
for (const auto * const town : cc->getTownsInfo())
|
|
||||||
{
|
|
||||||
if (town->hasBuiltSomeTradeBuilding())
|
|
||||||
{
|
|
||||||
marketId = town->id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!marketId.hasValue())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const CGObjectInstance * obj = cc->getObj(marketId, false);
|
|
||||||
assert(obj);
|
|
||||||
// if (!obj)
|
|
||||||
// return false;
|
|
||||||
|
|
||||||
const auto * market = dynamic_cast<const IMarket *>(obj);
|
|
||||||
assert(market);
|
|
||||||
// if (!market)
|
|
||||||
// return false;
|
|
||||||
|
|
||||||
bool shouldTryToTrade = true;
|
|
||||||
while(shouldTryToTrade)
|
|
||||||
{
|
|
||||||
shouldTryToTrade = false;
|
|
||||||
buildAnalyzer->update();
|
|
||||||
|
|
||||||
// if we favor getResourcesRequiredNow is better on short term, if we favor getTotalResourcesRequired is better on long term
|
|
||||||
TResources missingNow = buildAnalyzer->getMissingResourcesNow(ARMY_GOLD_RATIO_PER_MAKE_TURN_PASS);
|
|
||||||
if(missingNow.empty())
|
|
||||||
break;
|
|
||||||
|
|
||||||
TResources income = buildAnalyzer->getDailyIncome();
|
|
||||||
// We don't want to sell something that's necessary later on, though that could make short term a bit harder sometimes
|
|
||||||
TResources freeAfterMissingTotal = buildAnalyzer->getFreeResourcesAfterMissingTotal(ARMY_GOLD_RATIO_PER_MAKE_TURN_PASS);
|
|
||||||
|
|
||||||
#if NK2AI_TRACE_LEVEL >= 2
|
|
||||||
logAi->info("Nullkiller::handleTrading Free %s. FreeAfterMissingTotal %s. MissingNow %s", getFreeResources().toString(), freeAfterMissingTotal.toString(), missingNow.toString());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
constexpr int EMPTY = -1;
|
|
||||||
int mostWanted = EMPTY;
|
|
||||||
TResource mostWantedScoreNeg = std::numeric_limits<TResource>::max();
|
|
||||||
int mostExpendable = EMPTY;
|
|
||||||
TResource mostExpendableAmountPos = 0;
|
|
||||||
|
|
||||||
// Find the most wanted resource
|
|
||||||
for(int i = 0; i < missingNow.size(); ++i)
|
|
||||||
{
|
|
||||||
if(missingNow[i] == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const TResource score = income[i] - missingNow[i];
|
|
||||||
if(score < mostWantedScoreNeg)
|
|
||||||
{
|
|
||||||
mostWanted = i;
|
|
||||||
mostWantedScoreNeg = score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the most expendable resource
|
|
||||||
for(int i = 0; i < missingNow.size(); ++i)
|
|
||||||
{
|
|
||||||
const TResource amountToSell = freeAfterMissingTotal[i];
|
|
||||||
if (amountToSell == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bool okToSell = false;
|
|
||||||
if(i == GameResID::GOLD)
|
|
||||||
{
|
|
||||||
// TODO: Mircea: Check if we should negate isGoldPressureOverMax() instead
|
|
||||||
if(income[GameResID::GOLD] > 0 && !buildAnalyzer->isGoldPressureOverMax())
|
|
||||||
okToSell = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
okToSell = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(amountToSell > mostExpendableAmountPos && okToSell)
|
|
||||||
{
|
|
||||||
mostExpendable = i;
|
|
||||||
mostExpendableAmountPos = amountToSell;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if NK2AI_TRACE_LEVEL >= 2
|
|
||||||
logAi->trace(
|
|
||||||
"Nullkiller::handleTrading mostWanted: %d, mostWantedScoreNeg %d, mostExpendable: %d, mostExpendableAmountPos %d",
|
|
||||||
mostWanted,
|
|
||||||
mostWantedScoreNeg,
|
|
||||||
mostExpendable,
|
|
||||||
mostExpendableAmountPos
|
|
||||||
);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(mostExpendable == mostWanted || mostWanted == EMPTY || mostExpendable == EMPTY)
|
|
||||||
break;
|
|
||||||
|
|
||||||
int givenPerUnit;
|
|
||||||
int receivedUnits;
|
|
||||||
market->getOffer(mostExpendable, mostWanted, givenPerUnit, receivedUnits, EMarketMode::RESOURCE_RESOURCE);
|
|
||||||
if (!givenPerUnit || !receivedUnits)
|
|
||||||
{
|
|
||||||
logGlobal->error(
|
|
||||||
"Nullkiller::handleTrading No offer for %d of %d, given %d, received %d. Should never happen",
|
|
||||||
mostExpendable,
|
|
||||||
mostWanted,
|
|
||||||
givenPerUnit,
|
|
||||||
receivedUnits
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Mircea: if 15 wood and 14 gems, gems can be used a lot more for buying other things
|
|
||||||
if (givenPerUnit > mostExpendableAmountPos)
|
|
||||||
break;
|
|
||||||
|
|
||||||
TResource multiplier = std::min(static_cast<int>(mostExpendableAmountPos * EXPENDABLE_BULK_RATIO / givenPerUnit),
|
|
||||||
missingNow[mostWanted] / receivedUnits); // for gold we have to / receivedUnits, because 1 ore gives many gold units
|
|
||||||
if(multiplier == 0) // could happen for very small values due to EXPENDABLE_BULK_RATIO
|
|
||||||
multiplier = 1;
|
|
||||||
|
|
||||||
const TResource givenMultiplied = givenPerUnit * multiplier;
|
|
||||||
if(givenMultiplied > freeAfterMissingTotal[mostExpendable])
|
|
||||||
{
|
|
||||||
logGlobal->error(
|
|
||||||
"Nullkiller::handleTrading Something went wrong with the multiplier %d",
|
|
||||||
multiplier
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
cc->trade(market->getObjInstanceID(), EMarketMode::RESOURCE_RESOURCE, GameResID(mostExpendable), GameResID(mostWanted), givenMultiplied);
|
|
||||||
#if NK2AI_TRACE_LEVEL >= 2
|
|
||||||
logAi->info("Nullkiller::handleTrading Traded %d of %s for %d of %s at %s", givenMultiplied, mostExpendable, receivedUnits, mostWanted, obj->getObjectName());
|
|
||||||
#endif
|
|
||||||
haveTraded = true;
|
|
||||||
shouldTryToTrade = true;
|
|
||||||
}
|
|
||||||
return haveTraded;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const CPathsInfo> Nullkiller::getPathsInfo(const CGHeroInstance * h) const
|
std::shared_ptr<const CPathsInfo> Nullkiller::getPathsInfo(const CGHeroInstance * h) const
|
||||||
{
|
{
|
||||||
return pathfinderCache->getPathsInfo(h);
|
return pathfinderCache->getPathsInfo(h);
|
||||||
|
|||||||
@@ -136,9 +136,7 @@ public:
|
|||||||
ScanDepth getScanDepth() const { return scanDepth; }
|
ScanDepth getScanDepth() const { return scanDepth; }
|
||||||
bool isOpenMap() const { return openMap; }
|
bool isOpenMap() const { return openMap; }
|
||||||
bool isObjectGraphAllowed() const { return useObjectGraph; }
|
bool isObjectGraphAllowed() const { return useObjectGraph; }
|
||||||
bool handleTrading();
|
|
||||||
void invalidatePathfinderData();
|
void invalidatePathfinderData();
|
||||||
|
|
||||||
std::shared_ptr<const CPathsInfo> getPathsInfo(const CGHeroInstance * h) const;
|
std::shared_ptr<const CPathsInfo> getPathsInfo(const CGHeroInstance * h) const;
|
||||||
void invalidatePaths();
|
void invalidatePaths();
|
||||||
std::map<const CGHeroInstance *, HeroRole> getHeroesForPathfinding() const;
|
std::map<const CGHeroInstance *, HeroRole> getHeroesForPathfinding() const;
|
||||||
|
|||||||
181
AI/Nullkiller2/Engine/ResourceTrader.cpp
Normal file
181
AI/Nullkiller2/Engine/ResourceTrader.cpp
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* ResourceTrader.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 "ResourceTrader.h"
|
||||||
|
|
||||||
|
namespace NK2AI
|
||||||
|
{
|
||||||
|
bool ResourceTrader::trade(const std::unique_ptr<BuildAnalyzer> & buildAnalyzer, std::shared_ptr<CCallback> cc, TResources freeResources)
|
||||||
|
{
|
||||||
|
// TODO: Mircea: Maybe include based on how close danger is: X as default + proportion of close danger or something around that
|
||||||
|
constexpr float ARMY_GOLD_RATIO_PER_MAKE_TURN_PASS = 0.1f;
|
||||||
|
constexpr float EXPENDABLE_BULK_RATIO = 0.3f;
|
||||||
|
bool haveTraded = false;
|
||||||
|
ObjectInstanceID marketId;
|
||||||
|
|
||||||
|
// TODO: Mircea: What about outside town markets that have better rates than a single town for example?
|
||||||
|
// Are those used anywhere? To inspect.
|
||||||
|
for (const auto * const town : cc->getTownsInfo())
|
||||||
|
{
|
||||||
|
if (town->hasBuiltSomeTradeBuilding())
|
||||||
|
{
|
||||||
|
marketId = town->id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!marketId.hasValue())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const CGObjectInstance * obj = cc->getObj(marketId, false);
|
||||||
|
assert(obj);
|
||||||
|
// if (!obj)
|
||||||
|
// return false;
|
||||||
|
|
||||||
|
const auto * market = dynamic_cast<const IMarket *>(obj);
|
||||||
|
assert(market);
|
||||||
|
// if (!market)
|
||||||
|
// return false;
|
||||||
|
|
||||||
|
bool shouldTryToTrade = true;
|
||||||
|
while(shouldTryToTrade)
|
||||||
|
{
|
||||||
|
shouldTryToTrade = false;
|
||||||
|
buildAnalyzer->update();
|
||||||
|
|
||||||
|
// if we favor getResourcesRequiredNow is better on short term, if we favor getTotalResourcesRequired is better on long term
|
||||||
|
TResources missingNow = buildAnalyzer->getMissingResourcesNow(ARMY_GOLD_RATIO_PER_MAKE_TURN_PASS);
|
||||||
|
if(missingNow.empty())
|
||||||
|
break;
|
||||||
|
|
||||||
|
TResources income = buildAnalyzer->getDailyIncome();
|
||||||
|
// We don't want to sell something that's necessary later on, though that could make short term a bit harder sometimes
|
||||||
|
TResources freeAfterMissingTotal = buildAnalyzer->getFreeResourcesAfterMissingTotal(ARMY_GOLD_RATIO_PER_MAKE_TURN_PASS);
|
||||||
|
|
||||||
|
#if NK2AI_TRACE_LEVEL >= 2
|
||||||
|
logAi->info("ResourceTrader: Free %s. FreeAfterMissingTotal %s. MissingNow %s", freeResources.toString(), freeAfterMissingTotal.toString(), missingNow.toString());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(ResourceTrader::tradeHelper(EXPENDABLE_BULK_RATIO, market, missingNow, income, freeAfterMissingTotal, buildAnalyzer, cc))
|
||||||
|
{
|
||||||
|
haveTraded = true;
|
||||||
|
shouldTryToTrade = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return haveTraded;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourceTrader::tradeHelper(
|
||||||
|
float EXPENDABLE_BULK_RATIO,
|
||||||
|
const IMarket * market,
|
||||||
|
TResources missingNow,
|
||||||
|
TResources income,
|
||||||
|
TResources freeAfterMissingTotal,
|
||||||
|
const std::unique_ptr<BuildAnalyzer> & buildAnalyzer,
|
||||||
|
std::shared_ptr<CCallback> cc
|
||||||
|
)
|
||||||
|
{
|
||||||
|
constexpr int EMPTY = -1;
|
||||||
|
int mostWanted = EMPTY;
|
||||||
|
TResource mostWantedScoreNeg = std::numeric_limits<TResource>::max();
|
||||||
|
int mostExpendable = EMPTY;
|
||||||
|
TResource mostExpendableAmountPos = 0;
|
||||||
|
|
||||||
|
// Find the most wanted resource
|
||||||
|
for(int i = 0; i < missingNow.size(); ++i)
|
||||||
|
{
|
||||||
|
if(missingNow[i] == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const TResource score = income[i] - missingNow[i];
|
||||||
|
if(score < mostWantedScoreNeg)
|
||||||
|
{
|
||||||
|
mostWanted = i;
|
||||||
|
mostWantedScoreNeg = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the most expendable resource
|
||||||
|
for(int i = 0; i < missingNow.size(); ++i)
|
||||||
|
{
|
||||||
|
const TResource amountToSell = freeAfterMissingTotal[i];
|
||||||
|
if(amountToSell == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool okToSell = false;
|
||||||
|
if(i == GameResID::GOLD)
|
||||||
|
{
|
||||||
|
// TODO: Mircea: Check if we should negate isGoldPressureOverMax() instead
|
||||||
|
if(income[GameResID::GOLD] > 0 && !buildAnalyzer->isGoldPressureOverMax())
|
||||||
|
okToSell = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
okToSell = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(amountToSell > mostExpendableAmountPos && okToSell)
|
||||||
|
{
|
||||||
|
mostExpendable = i;
|
||||||
|
mostExpendableAmountPos = amountToSell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NK2AI_TRACE_LEVEL >= 2
|
||||||
|
logAi->trace(
|
||||||
|
"ResourceTrader: mostWanted: %d, mostWantedScoreNeg %d, mostExpendable: %d, mostExpendableAmountPos %d",
|
||||||
|
mostWanted,
|
||||||
|
mostWantedScoreNeg,
|
||||||
|
mostExpendable,
|
||||||
|
mostExpendableAmountPos
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(mostExpendable == mostWanted || mostWanted == EMPTY || mostExpendable == EMPTY)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int givenPerUnit;
|
||||||
|
int receivedPerUnit;
|
||||||
|
market->getOffer(mostExpendable, mostWanted, givenPerUnit, receivedPerUnit, EMarketMode::RESOURCE_RESOURCE);
|
||||||
|
if(!givenPerUnit || !receivedPerUnit)
|
||||||
|
{
|
||||||
|
logGlobal->error(
|
||||||
|
"ResourceTrader: No offer for %d of %d, given %d, received %d. Should never happen",
|
||||||
|
mostExpendable,
|
||||||
|
mostWanted,
|
||||||
|
givenPerUnit,
|
||||||
|
receivedPerUnit
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Mircea: if 15 wood and 14 gems, gems can be used a lot more for buying other things
|
||||||
|
if(givenPerUnit > mostExpendableAmountPos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TResource multiplier = std::min(
|
||||||
|
static_cast<int>(mostExpendableAmountPos * EXPENDABLE_BULK_RATIO / givenPerUnit),
|
||||||
|
missingNow[mostWanted] / receivedPerUnit
|
||||||
|
); // for gold we have to / receivedUnits, because 1 ore gives many gold units
|
||||||
|
if(multiplier == 0) // could happen for very small values due to EXPENDABLE_BULK_RATIO
|
||||||
|
multiplier = 1;
|
||||||
|
|
||||||
|
const TResource givenMultiplied = givenPerUnit * multiplier;
|
||||||
|
if(givenMultiplied > freeAfterMissingTotal[mostExpendable])
|
||||||
|
{
|
||||||
|
logGlobal->error("ResourceTrader: Something went wrong with the multiplier %d", multiplier);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cc->trade(market->getObjInstanceID(), EMarketMode::RESOURCE_RESOURCE, GameResID(mostExpendable), GameResID(mostWanted), givenMultiplied);
|
||||||
|
#if NK2AI_TRACE_LEVEL >= 2
|
||||||
|
logAi->info("ResourceTrader: Traded %d of %s for %d receivedPerUnit of %s", givenMultiplied, mostExpendable, receivedPerUnit, mostWanted);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
31
AI/Nullkiller2/Engine/ResourceTrader.h
Normal file
31
AI/Nullkiller2/Engine/ResourceTrader.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* ResourceTrader.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 "Nullkiller.h"
|
||||||
|
|
||||||
|
namespace NK2AI
|
||||||
|
{
|
||||||
|
|
||||||
|
class ResourceTrader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static bool trade(const std::unique_ptr<BuildAnalyzer> & buildAnalyzer, std::shared_ptr<CCallback> cc, TResources freeResources);
|
||||||
|
static bool tradeHelper(
|
||||||
|
float EXPENDABLE_BULK_RATIO,
|
||||||
|
const IMarket * market,
|
||||||
|
TResources missingNow,
|
||||||
|
TResources income,
|
||||||
|
TResources freeAfterMissingTotal,
|
||||||
|
const std::unique_ptr<BuildAnalyzer> & buildAnalyzer,
|
||||||
|
std::shared_ptr<CCallback> cc
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -148,6 +148,7 @@ if(ENABLE_NULLKILLER_AI)
|
|||||||
list(APPEND test_SRCS
|
list(APPEND test_SRCS
|
||||||
${NULLKILLER2_TEST_SRCS}
|
${NULLKILLER2_TEST_SRCS}
|
||||||
nullkiller2/Behaviors/RecruitHeroBehaviorTest.cpp
|
nullkiller2/Behaviors/RecruitHeroBehaviorTest.cpp
|
||||||
|
nullkiller2/Engine/ResourceTraderTest.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND test_HEADERS
|
list(APPEND test_HEADERS
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* PriorityEvaluatorTest.cpp, part of VCMI engine
|
* RecruitHeroBehaviorTest.cpp, part of VCMI engine
|
||||||
*
|
*
|
||||||
* Authors: listed in file AUTHORS in main folder
|
* Authors: listed in file AUTHORS in main folder
|
||||||
*
|
*
|
||||||
* License: GNU General Public License v2.0 or later
|
* License: GNU General Public License v2.0 or later
|
||||||
* Full text of license available in license.txt file, in main folder
|
* Full text of license available in license.txt file, in main folder
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Global.h"
|
#include "Global.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
|
|||||||
66
test/nullkiller2/Engine/ResourceTraderTest.cpp
Normal file
66
test/nullkiller2/Engine/ResourceTraderTest.cpp
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* RecruitHeroBehaviorTest.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 "AI/Nullkiller2/Behaviors/RecruitHeroBehavior.h"
|
||||||
|
#include "AI/Nullkiller2/Engine/Nullkiller.h"
|
||||||
|
#include "Global.h"
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
class MockMarket : public IMarket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MockMarket(IGameInfoCallback * cb)
|
||||||
|
: IMarket(cb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~MockMarket() override = default;
|
||||||
|
MOCK_METHOD(bool, getOffer, (int id1, int id2, int & val1, int & val2, EMarketMode mode), ());
|
||||||
|
ObjectInstanceID getObjInstanceID() const override;
|
||||||
|
int getMarketEfficiency() const override;
|
||||||
|
std::set<EMarketMode> availableModes() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(Nullkiller2_Engine_ResourceTrader, tradeHelper)
|
||||||
|
{
|
||||||
|
// auto * const market = new MockMarket(nullptr);
|
||||||
|
// EXPECT_CALL(*market, getOffer(testing::internal::Any, testing::internal::Any, testing::internal::Any, testing::internal::Any, EMarketMode::RESOURCE_RESOURCE)).Times(1);
|
||||||
|
// market->getOffer(0, 0, 0, 0, EMarketMode::RESOURCE_RESOURCE);
|
||||||
|
// delete market;
|
||||||
|
}
|
||||||
|
|
||||||
|
TResources res(const int wood, const int mercury, const int ore, const int sulfur, const int crystals, const int gems, const int gold, const int mithril)
|
||||||
|
{
|
||||||
|
TResources resources;
|
||||||
|
resources[0] = wood;
|
||||||
|
resources[1] = mercury;
|
||||||
|
resources[2] = ore;
|
||||||
|
resources[3] = sulfur;
|
||||||
|
resources[4] = crystals;
|
||||||
|
resources[5] = gems;
|
||||||
|
resources[6] = gold;
|
||||||
|
resources[7] = mithril;
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nullkiller::handleTrading Free [13919, 13883, 13921, 13857, 13792, 13883, 14, 0]. FreeAfterMissingTotal [13859, 13819, 13891, 13833, 13718, 13763, 0, 0]. MissingNow [0, 0, 0, 0, 0, 0, 193445, 0]
|
||||||
|
// Nullkiller::handleTrading Traded 1547 of 2 for 125 of 6
|
||||||
|
// Nullkiller::handleTrading Free [13919, 13883, 13921, 13857, 13792, 13883, 14, 0]. FreeAfterMissingTotal [13859, 13819, 12344, 13833, 13718, 13763, 0, 0]. MissingNow [0, 0, 0, 0, 0, 0, 70, 0]
|
||||||
|
// Nullkiller::handleTrading Traded 1 of 0 for 125 of 6
|
||||||
|
// Nullkiller::handleTrading Free [13908, 13883, 12374, 13857, 13722, 13883, 414, 0]. FreeAfterMissingTotal [13848, 13819, 12344, 13833, 13648, 13763, 0, 0]. MissingNow [0, 0, 0, 0, 0, 0, 193075, 0]
|
||||||
|
// Nullkiller::handleTrading Traded 1544 of 0 for 125 of 6
|
||||||
|
// Nullkiller::handleTrading Free [13908, 13883, 12374, 13857, 13722, 13883, 414, 0]. FreeAfterMissingTotal [12304, 13819, 12344, 13833, 13648, 13763, 0, 0]. MissingNow [0, 0, 0, 0, 0, 0, 75, 0]
|
||||||
|
// Nullkiller::handleTrading Traded 1 of 3 for 250 of 6
|
||||||
|
// Nullkiller::handleTrading Free [12364, 13883, 12374, 13841, 13722, 13883, 24, 0]. FreeAfterMissingTotal [12304, 13819, 12344, 13817, 13648, 13763, 0, 0]. MissingNow [0, 0, 0, 0, 0, 0, 193465, 0]
|
||||||
|
// Nullkiller::handleTrading Traded 773 of 1 for 250 of 6
|
||||||
|
// Nullkiller::handleTrading Free [12364, 13883, 12374, 13841, 13722, 13883, 24, 0]. FreeAfterMissingTotal [12304, 13046, 12344, 13817, 13648, 13763, 0, 0]. MissingNow [0, 0, 0, 0, 0, 0, 215, 0]
|
||||||
|
// Nullkiller::handleTrading Traded 1 of 3 for 250 of 6
|
||||||
|
// Nullkiller::handleTrading Free [12364, 13110, 12374, 13837, 13722, 13883, 52524, 0]. FreeAfterMissingTotal [12304, 13046, 12344, 13813, 13648, 13763, 0, 0]. MissingNow [0, 0, 0, 0, 0, 0, 140965, 0]
|
||||||
|
// Nullkiller::handleTrading Traded 563 of 3 for 250 of 6
|
||||||
|
// Nullkiller::handleTrading Free [12364, 13110, 12374, 13837, 13722, 13883, 52524, 0]. FreeAfterMissingTotal [12304, 13046, 12344, 13250, 13648, 13763, 0, 0]. MissingNow [0, 0, 0, 0, 0, 0, 215, 0]
|
||||||
|
// Nullkiller::handleTrading Traded 1 of 5 for 250 of 6
|
||||||
Reference in New Issue
Block a user