1
0
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:
Dydzio
2018-08-05 20:30:08 +02:00
parent 64b7c9f036
commit b29d5bb001
2 changed files with 93 additions and 87 deletions

View File

@@ -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)

View File

@@ -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);