mirror of
https://github.com/vcmi/vcmi.git
synced 2025-09-16 09:26:28 +02:00
finished FuzzyHelper logic separation
This commit is contained in:
@@ -125,38 +125,6 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
|
||||
|
||||
}
|
||||
|
||||
float FuzzyHelper::getWanderTargetObjectValue(const CGHeroInstance & h, const ObjectIdRef & obj)
|
||||
{
|
||||
float distFromObject = calculateTurnDistanceInputValue(&h, obj->pos);
|
||||
boost::optional<int> objValueKnownByAI = MapObjectsEvaluator::getInstance().getObjectValue(obj->ID, obj->subID);
|
||||
int objValue = 0;
|
||||
|
||||
if(objValueKnownByAI != boost::none) //consider adding value manipulation based on object instances on map
|
||||
{
|
||||
objValue = std::min(std::max(objValueKnownByAI.get(), 0), 20000);
|
||||
}
|
||||
else
|
||||
{
|
||||
MapObjectsEvaluator::getInstance().addObjectData(obj->ID, obj->subID, 0);
|
||||
logGlobal->warn("AI met object type it doesn't know - ID: " + std::to_string(obj->ID) + ", subID: " + std::to_string(obj->subID) + " - adding to database with value " + std::to_string(objValue));
|
||||
}
|
||||
|
||||
float output = -1.0f;
|
||||
try
|
||||
{
|
||||
wanderTarget.turnDistance->setValue(distFromObject);
|
||||
wanderTarget.objectValue->setValue(objValue);
|
||||
wanderTarget.engine.process();
|
||||
output = wanderTarget.value->getValue();
|
||||
}
|
||||
catch (fl::Exception & fe)
|
||||
{
|
||||
logAi->error("evaluate getWanderTargetObjectValue: %s", fe.getWhat());
|
||||
}
|
||||
assert(output >= 0.0f);
|
||||
return output;
|
||||
}
|
||||
|
||||
TacticalAdvantageEngine::TacticalAdvantageEngine()
|
||||
{
|
||||
try
|
||||
@@ -287,7 +255,6 @@ float TacticalAdvantageEngine::getTacticalAdvantage(const CArmedInstance * we, c
|
||||
else
|
||||
castleWalls->setValue(0);
|
||||
|
||||
//engine.process(TACTICAL_ADVANTAGE);//TODO: Process only Tactical_Advantage
|
||||
engine.process();
|
||||
output = threat->getValue();
|
||||
}
|
||||
@@ -371,12 +338,11 @@ HeroMovementGoalEngineBase::HeroMovementGoalEngineBase()
|
||||
heroStrength = new fl::InputVariable("heroStrength"); //we want to use weakest possible hero
|
||||
turnDistance = new fl::InputVariable("turnDistance"); //we want to use hero who is near
|
||||
missionImportance = new fl::InputVariable("lockedMissionImportance"); //we may want to preempt hero with low-priority mission
|
||||
estimatedReward = new fl::InputVariable("estimatedReward"); //indicate AI that content of the file is important or it is probably bad
|
||||
value = new fl::OutputVariable("Value");
|
||||
value->setMinimum(0);
|
||||
value->setMaximum(5);
|
||||
|
||||
std::vector<fl::InputVariable *> helper = { strengthRatio, heroStrength, turnDistance, missionImportance, estimatedReward };
|
||||
std::vector<fl::InputVariable *> helper = { strengthRatio, heroStrength, turnDistance, missionImportance };
|
||||
for(auto val : helper)
|
||||
{
|
||||
engine.addInputVariable(val);
|
||||
@@ -403,10 +369,6 @@ HeroMovementGoalEngineBase::HeroMovementGoalEngineBase()
|
||||
missionImportance->addTerm(new fl::Ramp("HIGH", 2.5, 5));
|
||||
missionImportance->setRange(0.0, 5.0);
|
||||
|
||||
estimatedReward->addTerm(new fl::Ramp("LOW", 2.5, 0));
|
||||
estimatedReward->addTerm(new fl::Ramp("HIGH", 2.5, 5));
|
||||
estimatedReward->setRange(0.0, 5.0);
|
||||
|
||||
//an issue: in 99% cases this outputs center of mass (2.5) regardless of actual input :/
|
||||
//should be same as "mission Importance" to keep consistency
|
||||
value->addTerm(new fl::Ramp("LOW", 2.5, 0));
|
||||
@@ -433,9 +395,6 @@ HeroMovementGoalEngineBase::HeroMovementGoalEngineBase()
|
||||
addRule("if turnDistance is SMALL then Value is HIGH");
|
||||
addRule("if turnDistance is MEDIUM then Value is MEDIUM");
|
||||
addRule("if turnDistance is LONG then Value is LOW");
|
||||
//some goals are more rewarding by definition f.e. capturing town is more important than collecting resource - experimental
|
||||
addRule("if estimatedReward is HIGH then Value is very HIGH");
|
||||
addRule("if estimatedReward is LOW then Value is somewhat LOW");
|
||||
}
|
||||
catch(fl::Exception & fe)
|
||||
{
|
||||
@@ -443,19 +402,47 @@ HeroMovementGoalEngineBase::HeroMovementGoalEngineBase()
|
||||
}
|
||||
}
|
||||
|
||||
void HeroMovementGoalEngineBase::setSharedFuzzyVariables(Goals::AbstractGoal & goal)
|
||||
{
|
||||
float turns = calculateTurnDistanceInputValue(goal.hero.h, goal.tile);
|
||||
float missionImportanceData = 0;
|
||||
if(vstd::contains(ai->lockedHeroes, goal.hero))
|
||||
missionImportanceData = ai->lockedHeroes[goal.hero]->priority;
|
||||
|
||||
float strengthRatioData = 10.0f; //we are much stronger than enemy
|
||||
ui64 danger = evaluateDanger(goal.tile, goal.hero.h);
|
||||
if(danger)
|
||||
strengthRatioData = (fl::scalar)goal.hero.h->getTotalStrength() / danger;
|
||||
|
||||
try
|
||||
{
|
||||
strengthRatio->setValue(strengthRatioData);
|
||||
heroStrength->setValue((fl::scalar)goal.hero->getTotalStrength() / ai->primaryHero()->getTotalStrength());
|
||||
turnDistance->setValue(turns);
|
||||
missionImportance->setValue(missionImportanceData);
|
||||
}
|
||||
catch(fl::Exception & fe)
|
||||
{
|
||||
logAi->error("HeroMovementGoalEngineBase::setSharedFuzzyVariables: %s", fe.getWhat());
|
||||
}
|
||||
}
|
||||
|
||||
HeroMovementGoalEngineBase::~HeroMovementGoalEngineBase()
|
||||
{
|
||||
delete strengthRatio;
|
||||
delete heroStrength;
|
||||
delete turnDistance;
|
||||
delete missionImportance;
|
||||
delete estimatedReward;
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate(Goals::VisitTile & g)
|
||||
{
|
||||
return visitTileEngine.evaluate(g);
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::GetObj & g)
|
||||
{
|
||||
return getObjEngine.evaluate(g);
|
||||
}
|
||||
float FuzzyHelper::evaluate(Goals::VisitHero & g)
|
||||
{
|
||||
auto obj = cb->getObj(ObjectInstanceID(g.objid)); //we assume for now that these goals are similar
|
||||
@@ -533,15 +520,13 @@ void FuzzyHelper::setPriority(Goals::TSubgoal & g) //calls evaluate - Visitor pa
|
||||
g->setpriority(g->accept(this)); //this enforces returned value is set
|
||||
}
|
||||
|
||||
EvalWanderTargetObject::EvalWanderTargetObject()
|
||||
GetObjEngine::GetObjEngine()
|
||||
{
|
||||
try
|
||||
{
|
||||
objectValue = new fl::InputVariable("objectValue"); //value of that object type known by AI
|
||||
|
||||
engine.addInputVariable(turnDistance);
|
||||
engine.addInputVariable(objectValue);
|
||||
engine.addOutputVariable(value);
|
||||
|
||||
//objectValue ranges are based on checking RMG priorities of some objects and trying to guess sane value ranges
|
||||
objectValue->addTerm(new fl::Ramp("LOW", 3000, 0)); //I have feeling that concave shape might work well instead of ramp for objectValue FL terms
|
||||
@@ -568,11 +553,51 @@ EvalWanderTargetObject::EvalWanderTargetObject()
|
||||
configure();
|
||||
}
|
||||
|
||||
EvalWanderTargetObject::~EvalWanderTargetObject()
|
||||
GetObjEngine::~GetObjEngine()
|
||||
{
|
||||
delete objectValue;
|
||||
}
|
||||
|
||||
float GetObjEngine::evaluate(Goals::AbstractGoal & goal)
|
||||
{
|
||||
auto g = dynamic_cast<Goals::GetObj &>(goal);
|
||||
|
||||
if(!g.hero)
|
||||
return 0;
|
||||
|
||||
auto obj = cb->getObj(ObjectInstanceID(g.objid));
|
||||
|
||||
|
||||
boost::optional<int> objValueKnownByAI = MapObjectsEvaluator::getInstance().getObjectValue(obj->ID, obj->subID);
|
||||
int objValue = 0;
|
||||
|
||||
if(objValueKnownByAI != boost::none) //consider adding value manipulation based on object instances on map
|
||||
{
|
||||
objValue = std::min(std::max(objValueKnownByAI.get(), 0), 20000);
|
||||
}
|
||||
else
|
||||
{
|
||||
MapObjectsEvaluator::getInstance().addObjectData(obj->ID, obj->subID, 0);
|
||||
logGlobal->warn("AI met object type it doesn't know - ID: " + std::to_string(obj->ID) + ", subID: " + std::to_string(obj->subID) + " - adding to database with value " + std::to_string(objValue));
|
||||
}
|
||||
|
||||
setSharedFuzzyVariables(goal);
|
||||
|
||||
float output = -1.0f;
|
||||
try
|
||||
{
|
||||
objectValue->setValue(objValue);
|
||||
engine.process();
|
||||
output = value->getValue();
|
||||
}
|
||||
catch(fl::Exception & fe)
|
||||
{
|
||||
logAi->error("evaluate getWanderTargetObjectValue: %s", fe.getWhat());
|
||||
}
|
||||
assert(output >= 0.0f);
|
||||
return output;
|
||||
}
|
||||
|
||||
VisitTileEngine::VisitTileEngine() //so far no VisitTile-specific variables that are not shared with HeroMovementGoalEngineBase
|
||||
{
|
||||
configure();
|
||||
@@ -590,37 +615,12 @@ float VisitTileEngine::evaluate(Goals::AbstractGoal & goal)
|
||||
return 0;
|
||||
|
||||
//assert(cb->isInTheMap(g.tile));
|
||||
float turns = calculateTurnDistanceInputValue(g.hero.h, g.tile);
|
||||
float missionImportanceData = 0;
|
||||
if(vstd::contains(ai->lockedHeroes, g.hero))
|
||||
missionImportanceData = ai->lockedHeroes[g.hero]->priority;
|
||||
|
||||
float strengthRatioData = 10.0f; //we are much stronger than enemy
|
||||
ui64 danger = evaluateDanger(g.tile, g.hero.h);
|
||||
if(danger)
|
||||
strengthRatioData = (fl::scalar)g.hero.h->getTotalStrength() / danger;
|
||||
|
||||
float tilePriority = 0;
|
||||
if(g.objid == -1)
|
||||
{
|
||||
estimatedReward->setEnabled(false);
|
||||
}
|
||||
else if(g.objid == Obj::TOWN) //TODO: move to getObj eventually and add appropiate logic there
|
||||
{
|
||||
estimatedReward->setEnabled(true);
|
||||
tilePriority = 5;
|
||||
}
|
||||
setSharedFuzzyVariables(goal);
|
||||
|
||||
try
|
||||
{
|
||||
strengthRatio->setValue(strengthRatioData);
|
||||
heroStrength->setValue((fl::scalar)g.hero->getTotalStrength() / ai->primaryHero()->getTotalStrength());
|
||||
turnDistance->setValue(turns);
|
||||
missionImportance->setValue(missionImportanceData);
|
||||
estimatedReward->setValue(tilePriority);
|
||||
|
||||
engine.process();
|
||||
//engine.process(VISIT_TILE); //TODO: Process only Visit_Tile
|
||||
g.priority = value->getValue();
|
||||
}
|
||||
catch(fl::Exception & fe)
|
||||
|
@@ -31,31 +31,35 @@ class TacticalAdvantageEngine : public engineBase
|
||||
{
|
||||
public:
|
||||
TacticalAdvantageEngine();
|
||||
|
||||
float getTacticalAdvantage(const CArmedInstance * we, const CArmedInstance * enemy); //returns factor how many times enemy is stronger than us
|
||||
~TacticalAdvantageEngine();
|
||||
private:
|
||||
fl::InputVariable * ourWalkers, *ourShooters, *ourFlyers, *enemyWalkers, *enemyShooters, *enemyFlyers;
|
||||
fl::InputVariable * ourSpeed, *enemySpeed;
|
||||
fl::InputVariable * bankPresent;
|
||||
fl::InputVariable * castleWalls;
|
||||
fl::OutputVariable * threat;
|
||||
|
||||
float getTacticalAdvantage(const CArmedInstance * we, const CArmedInstance * enemy); //returns factor how many times enemy is stronger than us
|
||||
~TacticalAdvantageEngine();
|
||||
};
|
||||
|
||||
class HeroMovementGoalEngineBase : public engineBase
|
||||
class HeroMovementGoalEngineBase : public engineBase //in future - maybe derive from some (GoalEngineBase : public engineBase) class for handling non-movement goals with common utility for goal engines
|
||||
{
|
||||
public:
|
||||
HeroMovementGoalEngineBase();
|
||||
|
||||
virtual float evaluate(Goals::AbstractGoal & goal) = 0;
|
||||
virtual ~HeroMovementGoalEngineBase();
|
||||
|
||||
protected:
|
||||
void setSharedFuzzyVariables(Goals::AbstractGoal & goal);
|
||||
|
||||
fl::InputVariable * strengthRatio;
|
||||
fl::InputVariable * heroStrength;
|
||||
fl::InputVariable * turnDistance;
|
||||
fl::InputVariable * missionImportance;
|
||||
fl::InputVariable * estimatedReward;
|
||||
fl::OutputVariable * value;
|
||||
|
||||
virtual float evaluate(Goals::AbstractGoal & goal) = 0;
|
||||
~HeroMovementGoalEngineBase();
|
||||
|
||||
protected:
|
||||
private:
|
||||
float calculateTurnDistanceInputValue(const CGHeroInstance * h, int3 tile) const;
|
||||
};
|
||||
|
||||
@@ -67,12 +71,14 @@ public:
|
||||
float evaluate(Goals::AbstractGoal & goal) override;
|
||||
};
|
||||
|
||||
class EvalWanderTargetObject : public HeroMovementGoalEngineBase //designed for use with VCAI::wander()
|
||||
class GetObjEngine : public HeroMovementGoalEngineBase
|
||||
{
|
||||
public:
|
||||
EvalWanderTargetObject();
|
||||
GetObjEngine();
|
||||
~GetObjEngine();
|
||||
float evaluate(Goals::AbstractGoal & goal) override;
|
||||
protected:
|
||||
fl::InputVariable * objectValue;
|
||||
~EvalWanderTargetObject();
|
||||
};
|
||||
|
||||
class FuzzyHelper
|
||||
@@ -83,12 +89,13 @@ class FuzzyHelper
|
||||
|
||||
VisitTileEngine visitTileEngine;
|
||||
|
||||
EvalWanderTargetObject wanderTargetEngine;
|
||||
GetObjEngine getObjEngine;
|
||||
|
||||
public:
|
||||
float evaluate(Goals::Explore & g);
|
||||
float evaluate(Goals::RecruitHero & g);
|
||||
float evaluate(Goals::VisitTile & g);
|
||||
float evaluate(Goals::GetObj & g);
|
||||
float evaluate(Goals::VisitHero & g);
|
||||
float evaluate(Goals::BuildThis & g);
|
||||
float evaluate(Goals::DigAtTile & g);
|
||||
@@ -102,7 +109,6 @@ public:
|
||||
void setPriority(Goals::TSubgoal & g);
|
||||
|
||||
ui64 estimateBankDanger(const CBank * bank); //TODO: move to another class?
|
||||
float getWanderTargetObjectValue(const CGHeroInstance & h, const ObjectIdRef & obj);
|
||||
|
||||
Goals::TSubgoal chooseSolution(Goals::TGoalVec vec);
|
||||
//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec);
|
||||
|
Reference in New Issue
Block a user