mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-18 03:21:27 +02:00
Merge pull request #4998 from IvanSavenko/ai_difficulty
Allow per-difficulty parameters for NKAI
This commit is contained in:
commit
5138412255
@ -34,11 +34,6 @@
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
// our to enemy strength ratio constants
|
||||
const float SAFE_ATTACK_CONSTANT = 1.1f;
|
||||
const float RETREAT_THRESHOLD = 0.3f;
|
||||
const double RETREAT_ABSOLUTE_THRESHOLD = 10000.;
|
||||
|
||||
//one thread may be turn of AI and another will be handling a side effect for AI2
|
||||
thread_local CCallback * cb = nullptr;
|
||||
thread_local AIGateway * ai = nullptr;
|
||||
@ -553,7 +548,7 @@ std::optional<BattleAction> AIGateway::makeSurrenderRetreatDecision(const Battle
|
||||
double fightRatio = ourStrength / (double)battleState.getEnemyStrength();
|
||||
|
||||
// if we have no towns - things are already bad, so retreat is not an option.
|
||||
if(cb->getTownsInfo().size() && ourStrength < RETREAT_ABSOLUTE_THRESHOLD && fightRatio < RETREAT_THRESHOLD && battleState.canFlee)
|
||||
if(cb->getTownsInfo().size() && ourStrength < nullkiller->settings->getRetreatThresholdAbsolute() && fightRatio < nullkiller->settings->getRetreatThresholdRelative() && battleState.canFlee)
|
||||
{
|
||||
return BattleAction::makeRetreat(battleState.ourSide);
|
||||
}
|
||||
@ -670,7 +665,7 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
|
||||
else if(objType == Obj::ARTIFACT || objType == Obj::RESOURCE)
|
||||
{
|
||||
bool dangerUnknown = danger == 0;
|
||||
bool dangerTooHigh = ratio > (1 / SAFE_ATTACK_CONSTANT);
|
||||
bool dangerTooHigh = ratio * nullkiller->settings->getSafeAttackRatio() > 1;
|
||||
|
||||
answer = !dangerUnknown && !dangerTooHigh;
|
||||
}
|
||||
|
@ -146,21 +146,21 @@ bool HeroPtr::operator==(const HeroPtr & rhs) const
|
||||
return h == rhs.get(true);
|
||||
}
|
||||
|
||||
bool isSafeToVisit(const CGHeroInstance * h, const CCreatureSet * heroArmy, uint64_t dangerStrength)
|
||||
bool isSafeToVisit(const CGHeroInstance * h, const CCreatureSet * heroArmy, uint64_t dangerStrength, float safeAttackRatio)
|
||||
{
|
||||
const ui64 heroStrength = h->getHeroStrength() * heroArmy->getArmyStrength();
|
||||
|
||||
if(dangerStrength)
|
||||
{
|
||||
return heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength;
|
||||
return heroStrength > dangerStrength * safeAttackRatio;
|
||||
}
|
||||
|
||||
return true; //there's no danger
|
||||
}
|
||||
|
||||
bool isSafeToVisit(const CGHeroInstance * h, uint64_t dangerStrength)
|
||||
bool isSafeToVisit(const CGHeroInstance * h, uint64_t dangerStrength, float safeAttackRatio)
|
||||
{
|
||||
return isSafeToVisit(h, h, dangerStrength);
|
||||
return isSafeToVisit(h, h, dangerStrength, safeAttackRatio);
|
||||
}
|
||||
|
||||
bool isObjectRemovable(const CGObjectInstance * obj)
|
||||
|
@ -61,11 +61,6 @@ const int GOLD_MINE_PRODUCTION = 1000;
|
||||
const int WOOD_ORE_MINE_PRODUCTION = 2;
|
||||
const int RESOURCE_MINE_PRODUCTION = 1;
|
||||
const int ACTUAL_RESOURCE_COUNT = 7;
|
||||
const int ALLOWED_ROAMING_HEROES = 8;
|
||||
|
||||
//implementation-dependent
|
||||
extern const float SAFE_ATTACK_CONSTANT;
|
||||
extern const int GOLD_RESERVE;
|
||||
|
||||
extern thread_local CCallback * cb;
|
||||
|
||||
@ -213,8 +208,8 @@ bool isBlockVisitObj(const int3 & pos);
|
||||
bool isWeeklyRevisitable(const Nullkiller * ai, const CGObjectInstance * obj);
|
||||
|
||||
bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property!
|
||||
bool isSafeToVisit(const CGHeroInstance * h, uint64_t dangerStrength);
|
||||
bool isSafeToVisit(const CGHeroInstance * h, const CCreatureSet *, uint64_t dangerStrength);
|
||||
bool isSafeToVisit(const CGHeroInstance * h, uint64_t dangerStrength, float safeAttackRatio);
|
||||
bool isSafeToVisit(const CGHeroInstance * h, const CCreatureSet *, uint64_t dangerStrength, float safeAttackRatio);
|
||||
|
||||
bool compareHeroStrength(const CGHeroInstance * h1, const CGHeroInstance * h2);
|
||||
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "../../../CCallback.h"
|
||||
#include "../../../lib/mapObjects/MapObjects.h"
|
||||
#include "../../../lib/IGameSettings.h"
|
||||
#include "../../../lib/GameConstants.h"
|
||||
|
||||
namespace NKAI
|
||||
@ -187,16 +188,18 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
||||
auto morale = slot.second->moraleVal();
|
||||
auto multiplier = 1.0f;
|
||||
|
||||
const float BadMoraleChance = 0.083f;
|
||||
const float HighMoraleChance = 0.04f;
|
||||
const auto & badMoraleDice = cb->getSettings().getVector(EGameSettings::COMBAT_BAD_MORALE_DICE);
|
||||
const auto & highMoraleDice = cb->getSettings().getVector(EGameSettings::COMBAT_GOOD_MORALE_DICE);
|
||||
|
||||
if(morale < 0)
|
||||
if(morale < 0 && !badMoraleDice.empty())
|
||||
{
|
||||
multiplier += morale * BadMoraleChance;
|
||||
size_t diceIndex = std::min<size_t>(badMoraleDice.size(), -morale) - 1;
|
||||
multiplier -= 1.0 / badMoraleDice.at(diceIndex);
|
||||
}
|
||||
else if(morale > 0)
|
||||
else if(morale > 0 && !highMoraleDice.empty())
|
||||
{
|
||||
multiplier += morale * HighMoraleChance;
|
||||
size_t diceIndex = std::min<size_t>(highMoraleDice.size(), morale) - 1;
|
||||
multiplier += 1.0 / highMoraleDice.at(diceIndex);
|
||||
}
|
||||
|
||||
newValue += multiplier * slot.second->getPower();
|
||||
|
@ -316,8 +316,8 @@ uint64_t DangerHitMapAnalyzer::enemyCanKillOurHeroesAlongThePath(const AIPath &
|
||||
|
||||
const auto& info = getTileThreat(tile);
|
||||
|
||||
return (info.fastestDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.fastestDanger.danger))
|
||||
|| (info.maximumDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.maximumDanger.danger));
|
||||
return (info.fastestDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.fastestDanger.danger, ai->settings->getSafeAttackRatio()))
|
||||
|| (info.maximumDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.maximumDanger.danger, ai->settings->getSafeAttackRatio()));
|
||||
}
|
||||
|
||||
const HitMapNode & DangerHitMapAnalyzer::getObjectThreat(const CGObjectInstance * obj) const
|
||||
|
@ -195,8 +195,7 @@ bool HeroManager::heroCapReached(bool includeGarrisoned) const
|
||||
{
|
||||
int heroCount = cb->getHeroCount(ai->playerID, includeGarrisoned);
|
||||
|
||||
return heroCount >= ALLOWED_ROAMING_HEROES
|
||||
|| heroCount >= ai->settings->getMaxRoamingHeroes()
|
||||
return heroCount >= ai->settings->getMaxRoamingHeroes()
|
||||
|| heroCount >= cb->getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP)
|
||||
|| heroCount >= cb->getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP);
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(
|
||||
continue;
|
||||
}
|
||||
|
||||
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
|
||||
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger, nullkiller->settings->getSafeAttackRatio());
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace(
|
||||
|
@ -305,7 +305,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
continue;
|
||||
}
|
||||
|
||||
if(threat.turn == 0 || (path.turn() <= threat.turn && path.getHeroStrength() * SAFE_ATTACK_CONSTANT >= threat.danger))
|
||||
if(threat.turn == 0 || (path.turn() <= threat.turn && path.getHeroStrength() * ai->settings->getSafeAttackRatio() >= threat.danger))
|
||||
{
|
||||
if(ai->arePathHeroesLocked(path))
|
||||
{
|
||||
|
@ -145,7 +145,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const Nullkiller * ai, con
|
||||
}
|
||||
|
||||
auto danger = path.getTotalDanger();
|
||||
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
|
||||
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger, ai->settings->getSafeAttackRatio());
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace(
|
||||
@ -341,7 +341,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const Nullkiller * ai, const CGT
|
||||
|
||||
auto danger = path.getTotalDanger();
|
||||
|
||||
auto isSafe = isSafeToVisit(path.targetHero, path.heroArmy, danger);
|
||||
auto isSafe = isSafeToVisit(path.targetHero, path.heroArmy, danger, ai->settings->getSafeAttackRatio());
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace(
|
||||
|
@ -17,8 +17,7 @@
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
#define MIN_AI_STRENGTH (0.5f) //lower when combat AI gets smarter
|
||||
#define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us
|
||||
constexpr float MIN_AI_STRENGTH = 0.5f; //lower when combat AI gets smarter
|
||||
|
||||
engineBase::engineBase()
|
||||
{
|
||||
|
@ -34,13 +34,12 @@ using namespace Goals;
|
||||
std::unique_ptr<ObjectGraph> Nullkiller::baseGraph;
|
||||
|
||||
Nullkiller::Nullkiller()
|
||||
:activeHero(nullptr), scanDepth(ScanDepth::MAIN_FULL), useHeroChain(true)
|
||||
: activeHero(nullptr)
|
||||
, scanDepth(ScanDepth::MAIN_FULL)
|
||||
, useHeroChain(true)
|
||||
, memory(std::make_unique<AIMemory>())
|
||||
{
|
||||
memory = std::make_unique<AIMemory>();
|
||||
settings = std::make_unique<Settings>();
|
||||
|
||||
useObjectGraph = settings->isObjectGraphAllowed();
|
||||
openMap = settings->isOpenMap() || useObjectGraph;
|
||||
}
|
||||
|
||||
bool canUseOpenMap(std::shared_ptr<CCallback> cb, PlayerColor playerID)
|
||||
@ -62,17 +61,23 @@ bool canUseOpenMap(std::shared_ptr<CCallback> cb, PlayerColor playerID)
|
||||
return false;
|
||||
}
|
||||
|
||||
return cb->getStartInfo()->difficulty >= 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Nullkiller::init(std::shared_ptr<CCallback> cb, AIGateway * gateway)
|
||||
{
|
||||
this->cb = cb;
|
||||
this->gateway = gateway;
|
||||
|
||||
playerID = gateway->playerID;
|
||||
this->playerID = gateway->playerID;
|
||||
|
||||
if(openMap && !canUseOpenMap(cb, playerID))
|
||||
settings = std::make_unique<Settings>(cb->getStartInfo()->difficulty);
|
||||
|
||||
if(canUseOpenMap(cb, playerID))
|
||||
{
|
||||
useObjectGraph = settings->isObjectGraphAllowed();
|
||||
openMap = settings->isOpenMap() || useObjectGraph;
|
||||
}
|
||||
else
|
||||
{
|
||||
useObjectGraph = false;
|
||||
openMap = false;
|
||||
|
@ -35,9 +35,7 @@
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
#define MIN_AI_STRENGTH (0.5f) //lower when combat AI gets smarter
|
||||
#define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us
|
||||
const float MIN_CRITICAL_VALUE = 2.0f;
|
||||
constexpr float MIN_CRITICAL_VALUE = 2.0f;
|
||||
|
||||
EvaluationContext::EvaluationContext(const Nullkiller* ai)
|
||||
: movementCost(0.0),
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <limits>
|
||||
|
||||
#include "Settings.h"
|
||||
|
||||
#include "../../../lib/constants/StringConstants.h"
|
||||
#include "../../../lib/mapObjectConstructors/AObjectTypeHandler.h"
|
||||
#include "../../../lib/mapObjectConstructors/CObjectClassesHandler.h"
|
||||
#include "../../../lib/mapObjectConstructors/CBankInstanceConstructor.h"
|
||||
@ -22,11 +24,14 @@
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
Settings::Settings()
|
||||
Settings::Settings(int difficultyLevel)
|
||||
: maxRoamingHeroes(8),
|
||||
mainHeroTurnDistanceLimit(10),
|
||||
scoutHeroTurnDistanceLimit(5),
|
||||
maxGoldPressure(0.3f),
|
||||
maxGoldPressure(0.3f),
|
||||
retreatThresholdRelative(0.3),
|
||||
retreatThresholdAbsolute(10000),
|
||||
safeAttackRatio(1.1),
|
||||
maxpass(10),
|
||||
pathfinderBucketsCount(1),
|
||||
pathfinderBucketSize(32),
|
||||
@ -35,7 +40,9 @@ namespace NKAI
|
||||
openMap(true),
|
||||
useFuzzy(false)
|
||||
{
|
||||
JsonNode node = JsonUtils::assembleFromFiles("config/ai/nkai/nkai-settings");
|
||||
const std::string & difficultyName = GameConstants::DIFFICULTY_NAMES[difficultyLevel];
|
||||
const JsonNode & rootNode = JsonUtils::assembleFromFiles("config/ai/nkai/nkai-settings");
|
||||
const JsonNode & node = rootNode[difficultyName];
|
||||
|
||||
maxRoamingHeroes = node["maxRoamingHeroes"].Integer();
|
||||
mainHeroTurnDistanceLimit = node["mainHeroTurnDistanceLimit"].Integer();
|
||||
@ -44,6 +51,9 @@ namespace NKAI
|
||||
pathfinderBucketsCount = node["pathfinderBucketsCount"].Integer();
|
||||
pathfinderBucketSize = node["pathfinderBucketSize"].Integer();
|
||||
maxGoldPressure = node["maxGoldPressure"].Float();
|
||||
retreatThresholdRelative = node["retreatThresholdRelative"].Float();
|
||||
retreatThresholdAbsolute = node["retreatThresholdAbsolute"].Float();
|
||||
safeAttackRatio = node["safeAttackRatio"].Float();
|
||||
allowObjectGraph = node["allowObjectGraph"].Bool();
|
||||
openMap = node["openMap"].Bool();
|
||||
useFuzzy = node["useFuzzy"].Bool();
|
||||
|
@ -28,16 +28,22 @@ namespace NKAI
|
||||
int pathfinderBucketsCount;
|
||||
int pathfinderBucketSize;
|
||||
float maxGoldPressure;
|
||||
float retreatThresholdRelative;
|
||||
float retreatThresholdAbsolute;
|
||||
float safeAttackRatio;
|
||||
bool allowObjectGraph;
|
||||
bool useTroopsFromGarrisons;
|
||||
bool openMap;
|
||||
bool useFuzzy;
|
||||
|
||||
public:
|
||||
Settings();
|
||||
explicit Settings(int difficultyLevel);
|
||||
|
||||
int getMaxPass() const { return maxpass; }
|
||||
float getMaxGoldPressure() const { return maxGoldPressure; }
|
||||
float getRetreatThresholdRelative() const { return retreatThresholdRelative; }
|
||||
float getRetreatThresholdAbsolute() const { return retreatThresholdAbsolute; }
|
||||
float getSafeAttackRatio() const { return safeAttackRatio; }
|
||||
int getMaxRoamingHeroes() const { return maxRoamingHeroes; }
|
||||
int getMainHeroTurnDistanceLimit() const { return mainHeroTurnDistanceLimit; }
|
||||
int getScoutHeroTurnDistanceLimit() const { return scoutHeroTurnDistanceLimit; }
|
||||
|
@ -175,7 +175,7 @@ void ExplorationHelper::scanTile(const int3 & tile)
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isSafeToVisit(hero, path.heroArmy, path.getTotalDanger()))
|
||||
if(isSafeToVisit(hero, path.heroArmy, path.getTotalDanger(), ai->settings->getSafeAttackRatio()))
|
||||
{
|
||||
bestGoal = goal;
|
||||
bestValue = ourValue;
|
||||
|
@ -25,11 +25,9 @@ using crstring = const std::string &;
|
||||
using dwellingContent = std::pair<ui32, std::vector<CreatureID>>;
|
||||
|
||||
const int ACTUAL_RESOURCE_COUNT = 7;
|
||||
const int ALLOWED_ROAMING_HEROES = 8;
|
||||
|
||||
//implementation-dependent
|
||||
extern const double SAFE_ATTACK_CONSTANT;
|
||||
extern const int GOLD_RESERVE;
|
||||
|
||||
extern thread_local CCallback * cb;
|
||||
extern thread_local VCAI * ai;
|
||||
|
@ -14,8 +14,6 @@
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/mapObjects/MapObjects.h"
|
||||
|
||||
#define GOLD_RESERVE (10000); //at least we'll be able to reach capitol
|
||||
|
||||
ResourceObjective::ResourceObjective(const TResources & Res, Goals::TSubgoal Goal)
|
||||
: resources(Res), goal(Goal)
|
||||
{
|
||||
|
@ -1314,8 +1314,6 @@ bool VCAI::canRecruitAnyHero(const CGTownInstance * t) const
|
||||
return false;
|
||||
if(cb->getResourceAmount(EGameResID::GOLD) < GameConstants::HERO_GOLD_COST) //TODO: use ResourceManager
|
||||
return false;
|
||||
if(cb->getHeroesInfo().size() >= ALLOWED_ROAMING_HEROES)
|
||||
return false;
|
||||
if(cb->getHeroesInfo().size() >= cb->getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP))
|
||||
return false;
|
||||
if(!cb->getAvailableHeroes(t).size())
|
||||
|
@ -1,13 +1,86 @@
|
||||
{
|
||||
"maxRoamingHeroes" : 8,
|
||||
"maxpass" : 30,
|
||||
"mainHeroTurnDistanceLimit" : 10,
|
||||
"scoutHeroTurnDistanceLimit" : 5,
|
||||
"maxGoldPressure" : 0.3,
|
||||
"useTroopsFromGarrisons" : true,
|
||||
"openMap": true,
|
||||
"allowObjectGraph": false,
|
||||
"pathfinderBucketsCount" : 1, // old value: 3,
|
||||
"pathfinderBucketSize" : 32, // old value: 7,
|
||||
"useFuzzy" : false
|
||||
"pawn" : {
|
||||
"maxRoamingHeroes" : 8,
|
||||
"maxpass" : 30,
|
||||
"mainHeroTurnDistanceLimit" : 10,
|
||||
"scoutHeroTurnDistanceLimit" : 5,
|
||||
"maxGoldPressure" : 0.3,
|
||||
"useTroopsFromGarrisons" : true,
|
||||
"openMap": false,
|
||||
"allowObjectGraph": false,
|
||||
"pathfinderBucketsCount" : 1, // old value: 3,
|
||||
"pathfinderBucketSize" : 32, // old value: 7,
|
||||
"retreatThresholdRelative" : 0.3,
|
||||
"retreatThresholdAbsolute" : 10000,
|
||||
"safeAttackRatio" : 1.1,
|
||||
"useFuzzy" : false
|
||||
},
|
||||
|
||||
"knight" : {
|
||||
"maxRoamingHeroes" : 8,
|
||||
"maxpass" : 30,
|
||||
"mainHeroTurnDistanceLimit" : 10,
|
||||
"scoutHeroTurnDistanceLimit" : 5,
|
||||
"maxGoldPressure" : 0.3,
|
||||
"useTroopsFromGarrisons" : true,
|
||||
"openMap": false,
|
||||
"allowObjectGraph": false,
|
||||
"pathfinderBucketsCount" : 1, // old value: 3,
|
||||
"pathfinderBucketSize" : 32, // old value: 7,
|
||||
"retreatThresholdRelative" : 0.3,
|
||||
"retreatThresholdAbsolute" : 10000,
|
||||
"safeAttackRatio" : 1.1,
|
||||
"useFuzzy" : false
|
||||
},
|
||||
|
||||
"rook" : {
|
||||
"maxRoamingHeroes" : 8,
|
||||
"maxpass" : 30,
|
||||
"mainHeroTurnDistanceLimit" : 10,
|
||||
"scoutHeroTurnDistanceLimit" : 5,
|
||||
"maxGoldPressure" : 0.3,
|
||||
"useTroopsFromGarrisons" : true,
|
||||
"openMap": false,
|
||||
"allowObjectGraph": false,
|
||||
"pathfinderBucketsCount" : 1, // old value: 3,
|
||||
"pathfinderBucketSize" : 32, // old value: 7,
|
||||
"retreatThresholdRelative" : 0.3,
|
||||
"retreatThresholdAbsolute" : 10000,
|
||||
"safeAttackRatio" : 1.1,
|
||||
"useFuzzy" : false
|
||||
},
|
||||
|
||||
"queen" : {
|
||||
"maxRoamingHeroes" : 8,
|
||||
"maxpass" : 30,
|
||||
"mainHeroTurnDistanceLimit" : 10,
|
||||
"scoutHeroTurnDistanceLimit" : 5,
|
||||
"maxGoldPressure" : 0.3,
|
||||
"useTroopsFromGarrisons" : true,
|
||||
"openMap": true,
|
||||
"allowObjectGraph": false,
|
||||
"pathfinderBucketsCount" : 1, // old value: 3,
|
||||
"pathfinderBucketSize" : 32, // old value: 7,
|
||||
"retreatThresholdRelative" : 0.3,
|
||||
"retreatThresholdAbsolute" : 10000,
|
||||
"safeAttackRatio" : 1.1,
|
||||
"useFuzzy" : false
|
||||
},
|
||||
|
||||
"king" : {
|
||||
"maxRoamingHeroes" : 8,
|
||||
"maxpass" : 30,
|
||||
"mainHeroTurnDistanceLimit" : 10,
|
||||
"scoutHeroTurnDistanceLimit" : 5,
|
||||
"maxGoldPressure" : 0.3,
|
||||
"useTroopsFromGarrisons" : true,
|
||||
"openMap": true,
|
||||
"allowObjectGraph": false,
|
||||
"pathfinderBucketsCount" : 1, // old value: 3,
|
||||
"pathfinderBucketSize" : 32, // old value: 7,
|
||||
"retreatThresholdRelative" : 0.3,
|
||||
"retreatThresholdAbsolute" : 10000,
|
||||
"safeAttackRatio" : 1.1,
|
||||
"useFuzzy" : false
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user