mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
First implementation of fuzzy logic in VisitTile goal.
This commit is contained in:
parent
983c0496d0
commit
d085f8eee8
@ -5,8 +5,9 @@
|
||||
#include "../../lib/CObjectHandler.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
#include "../../CCallback.h"
|
||||
//#include "Goals.cpp"
|
||||
//#include "VCAI.h"
|
||||
#include "VCAI.h"
|
||||
|
||||
/*
|
||||
* Fuzzy.cpp, part of VCMI engine
|
||||
@ -31,6 +32,9 @@ using namespace vstd;
|
||||
|
||||
FuzzyHelper *fh;
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
|
||||
struct armyStructure
|
||||
{
|
||||
float walkers, shooters, flyers;
|
||||
@ -83,8 +87,12 @@ armyStructure evaluateArmyStructure (const CArmedInstance * army)
|
||||
|
||||
FuzzyHelper::FuzzyHelper()
|
||||
{
|
||||
engine.hedgeSet().add(new fl::HedgeSomewhat());
|
||||
engine.hedgeSet().add(new fl::HedgeVery());
|
||||
|
||||
initBank();
|
||||
initTacticalAdvantage();
|
||||
initVisitTile();
|
||||
}
|
||||
|
||||
void FuzzyHelper::initBank()
|
||||
@ -117,14 +125,16 @@ void FuzzyHelper::initTacticalAdvantage()
|
||||
{
|
||||
//Tactical advantage calculation
|
||||
std::vector<fl::InputLVar*> helper;
|
||||
ourShooters = new fl::InputLVar("OurShooters");
|
||||
ourWalkers = new fl::InputLVar("OurWalkers");
|
||||
ourFlyers = new fl::InputLVar("OurFlyers");
|
||||
enemyShooters = new fl::InputLVar("EnemyShooters");
|
||||
enemyWalkers = new fl::InputLVar("EnemyWalkers");
|
||||
enemyFlyers = new fl::InputLVar("EnemyFlyers");
|
||||
|
||||
helper += ourShooters, ourWalkers, ourFlyers, enemyShooters, enemyWalkers, enemyFlyers;
|
||||
//TODO: delete all that stuff upon destruction
|
||||
ta.ourShooters = new fl::InputLVar("OurShooters");
|
||||
ta.ourWalkers = new fl::InputLVar("OurWalkers");
|
||||
ta.ourFlyers = new fl::InputLVar("OurFlyers");
|
||||
ta.enemyShooters = new fl::InputLVar("EnemyShooters");
|
||||
ta.enemyWalkers = new fl::InputLVar("EnemyWalkers");
|
||||
ta.enemyFlyers = new fl::InputLVar("EnemyFlyers");
|
||||
|
||||
helper += ta.ourShooters, ta.ourWalkers, ta.ourFlyers, ta.enemyShooters, ta.enemyWalkers, ta.enemyFlyers;
|
||||
|
||||
for (auto val : helper)
|
||||
{
|
||||
@ -134,10 +144,10 @@ void FuzzyHelper::initTacticalAdvantage()
|
||||
}
|
||||
helper.clear();
|
||||
|
||||
ourSpeed = new fl::InputLVar("OurSpeed");
|
||||
enemySpeed = new fl::InputLVar("EnemySpeed");
|
||||
ta.ourSpeed = new fl::InputLVar("OurSpeed");
|
||||
ta.enemySpeed = new fl::InputLVar("EnemySpeed");
|
||||
|
||||
helper += ourSpeed, enemySpeed;
|
||||
helper += ta.ourSpeed, ta.enemySpeed;
|
||||
|
||||
for (auto val : helper)
|
||||
{
|
||||
@ -146,40 +156,37 @@ void FuzzyHelper::initTacticalAdvantage()
|
||||
val->addTerm (new fl::ShoulderTerm("HIGH", 10.5, 16, false));
|
||||
engine.addInputLVar(val);
|
||||
}
|
||||
castleWalls = new fl::InputLVar("CastleWalls");
|
||||
castleWalls->addTerm(new fl::SingletonTerm("NONE", CGTownInstance::NONE));
|
||||
castleWalls->addTerm(new fl::TrapezoidalTerm("MEDIUM", CGTownInstance::FORT, 2.5));
|
||||
castleWalls->addTerm(new fl::ShoulderTerm("HIGH", CGTownInstance::CITADEL - 0.1, CGTownInstance::CASTLE));
|
||||
engine.addInputLVar(castleWalls);
|
||||
ta.castleWalls = new fl::InputLVar("CastleWalls");
|
||||
ta.castleWalls->addTerm(new fl::SingletonTerm("NONE", CGTownInstance::NONE));
|
||||
ta.castleWalls->addTerm(new fl::TrapezoidalTerm("MEDIUM", CGTownInstance::FORT, 2.5));
|
||||
ta.castleWalls->addTerm(new fl::ShoulderTerm("HIGH", CGTownInstance::CITADEL - 0.1, CGTownInstance::CASTLE));
|
||||
engine.addInputLVar(ta.castleWalls);
|
||||
|
||||
bankPresent = new fl::InputLVar("Bank");
|
||||
bankPresent->addTerm(new fl::SingletonTerm("FALSE", 0));
|
||||
bankPresent->addTerm(new fl::SingletonTerm("TRUE", 1));
|
||||
engine.addInputLVar(bankPresent);
|
||||
ta.bankPresent = new fl::InputLVar("Bank");
|
||||
ta.bankPresent->addTerm(new fl::SingletonTerm("FALSE", 0));
|
||||
ta.bankPresent->addTerm(new fl::SingletonTerm("TRUE", 1));
|
||||
engine.addInputLVar(ta.bankPresent);
|
||||
|
||||
threat = new fl::OutputLVar("Threat");
|
||||
threat->addTerm (new fl::TriangularTerm("LOW", MIN_AI_STRENGHT, 1));
|
||||
threat->addTerm (new fl::TriangularTerm("MEDIUM", 0.8, 1.2));
|
||||
threat->addTerm (new fl::ShoulderTerm("HIGH", 1, 1.5, false));
|
||||
engine.addOutputLVar(threat);
|
||||
ta.threat = new fl::OutputLVar("Threat");
|
||||
ta.threat->addTerm (new fl::TriangularTerm("LOW", MIN_AI_STRENGHT, 1));
|
||||
ta.threat->addTerm (new fl::TriangularTerm("MEDIUM", 0.8, 1.2));
|
||||
ta.threat->addTerm (new fl::ShoulderTerm("HIGH", 1, 1.5, false));
|
||||
engine.addOutputLVar(ta.threat);
|
||||
|
||||
engine.hedgeSet().add(new fl::HedgeSomewhat());
|
||||
engine.hedgeSet().add(new fl::HedgeVery());
|
||||
|
||||
tacticalAdvantage.addRule(new fl::MamdaniRule("if OurShooters is MANY and EnemySpeed is LOW then Threat is very LOW", engine));
|
||||
tacticalAdvantage.addRule(new fl::MamdaniRule("if OurSpeed is LOW and OurShooters is FEW and EnemyShooters is MANY then Threat is very HIGH", engine));
|
||||
tacticalAdvantage.addRule(new fl::MamdaniRule("if (OurShooters is MANY and OurFlyers is MANY) and EnemyShooters is MANY then Threat is LOW", engine));
|
||||
tacticalAdvantage.addRule(new fl::MamdaniRule("if OurShooters is MANY and EnemySpeed is HIGH then Threat is somewhat HIGH", engine));
|
||||
ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if OurShooters is MANY and EnemySpeed is LOW then Threat is very LOW", engine));
|
||||
ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if OurSpeed is LOW and OurShooters is FEW and EnemyShooters is MANY then Threat is very HIGH", engine));
|
||||
ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if (OurShooters is MANY and OurFlyers is MANY) and EnemyShooters is MANY then Threat is LOW", engine));
|
||||
ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if OurShooters is MANY and EnemySpeed is HIGH then Threat is somewhat HIGH", engine));
|
||||
//tacticalAdvantage.addRule(new fl::MamdaniRule("if OurShooters is MANY and EnemyShooters is MANY then Threat is MEDIUM", engine));
|
||||
|
||||
tacticalAdvantage.addRule(new fl::MamdaniRule("if Bank is TRUE and OurShooters is MANY then Threat is somewhat HIGH", engine));
|
||||
tacticalAdvantage.addRule(new fl::MamdaniRule("if Bank is TRUE and EnemyShooters is MANY then Threat is LOW", engine));
|
||||
ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if Bank is TRUE and OurShooters is MANY then Threat is somewhat HIGH", engine));
|
||||
ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if Bank is TRUE and EnemyShooters is MANY then Threat is LOW", engine));
|
||||
|
||||
tacticalAdvantage.addRule(new fl::MamdaniRule("if CastleWalls is HIGH and OurWalkers is MANY then Threat is very HIGH", engine));
|
||||
tacticalAdvantage.addRule(new fl::MamdaniRule("if CastleWalls is HIGH and OurFlyers is MANY and OurShooters is MANY then Threat is MEDIUM", engine));
|
||||
tacticalAdvantage.addRule(new fl::MamdaniRule("if CastleWalls is MEDIUM and OurShooters is MANY and EnemyWalkers is MANY then Threat is LOW", engine));
|
||||
ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if CastleWalls is HIGH and OurWalkers is MANY then Threat is very HIGH", engine));
|
||||
ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if CastleWalls is HIGH and OurFlyers is MANY and OurShooters is MANY then Threat is MEDIUM", engine));
|
||||
ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if CastleWalls is MEDIUM and OurShooters is MANY and EnemyWalkers is MANY then Threat is LOW", engine));
|
||||
|
||||
engine.addRuleBlock (&tacticalAdvantage);
|
||||
engine.addRuleBlock (&ta.tacticalAdvantage);
|
||||
}
|
||||
catch(fl::ParsingException & pe)
|
||||
{
|
||||
@ -243,32 +250,32 @@ float FuzzyHelper::getTacticalAdvantage (const CArmedInstance *we, const CArmedI
|
||||
armyStructure ourStructure = evaluateArmyStructure(we);
|
||||
armyStructure enemyStructure = evaluateArmyStructure(enemy);
|
||||
|
||||
ourWalkers->setInput(ourStructure.walkers);
|
||||
ourShooters->setInput(ourStructure.shooters);
|
||||
ourFlyers->setInput(ourStructure.flyers);
|
||||
ourSpeed->setInput(ourStructure.maxSpeed);
|
||||
ta.ourWalkers->setInput(ourStructure.walkers);
|
||||
ta.ourShooters->setInput(ourStructure.shooters);
|
||||
ta.ourFlyers->setInput(ourStructure.flyers);
|
||||
ta.ourSpeed->setInput(ourStructure.maxSpeed);
|
||||
|
||||
enemyWalkers->setInput(enemyStructure.walkers);
|
||||
enemyShooters->setInput(enemyStructure.shooters);
|
||||
enemyFlyers->setInput(enemyStructure.flyers);
|
||||
enemySpeed->setInput(enemyStructure.maxSpeed);
|
||||
ta.enemyWalkers->setInput(enemyStructure.walkers);
|
||||
ta.enemyShooters->setInput(enemyStructure.shooters);
|
||||
ta.enemyFlyers->setInput(enemyStructure.flyers);
|
||||
ta.enemySpeed->setInput(enemyStructure.maxSpeed);
|
||||
|
||||
bool bank = dynamic_cast<const CBank*>(enemy);
|
||||
if (bank)
|
||||
bankPresent->setInput(1);
|
||||
ta.bankPresent->setInput(1);
|
||||
else
|
||||
bankPresent->setInput(0);
|
||||
ta.bankPresent->setInput(0);
|
||||
|
||||
const CGTownInstance * fort = dynamic_cast<const CGTownInstance*>(enemy);
|
||||
if (fort)
|
||||
{
|
||||
castleWalls->setInput (fort->fortLevel());
|
||||
ta.castleWalls->setInput (fort->fortLevel());
|
||||
}
|
||||
else
|
||||
castleWalls->setInput(0);
|
||||
ta.castleWalls->setInput(0);
|
||||
|
||||
engine.process (TACTICAL_ADVANTAGE);
|
||||
output = threat->output().defuzzify();
|
||||
output = ta.threat->output().defuzzify();
|
||||
}
|
||||
catch (fl::FuzzyException & fe)
|
||||
{
|
||||
@ -277,9 +284,28 @@ float FuzzyHelper::getTacticalAdvantage (const CArmedInstance *we, const CArmedI
|
||||
return output;
|
||||
}
|
||||
|
||||
FuzzyHelper::TacticalAdvantage::~TacticalAdvantage()
|
||||
{
|
||||
//TODO: smart pointers?
|
||||
delete ourWalkers;
|
||||
delete ourShooters;
|
||||
delete ourFlyers;
|
||||
delete enemyWalkers;
|
||||
delete enemyShooters;
|
||||
delete enemyFlyers;
|
||||
delete ourSpeed;
|
||||
delete enemySpeed;
|
||||
delete bankPresent;
|
||||
delete castleWalls;
|
||||
delete threat;
|
||||
}
|
||||
|
||||
//shared_ptr<AbstractGoal> chooseSolution (std::vector<shared_ptr<AbstractGoal>> & vec)
|
||||
Goals::TSubgoal FuzzyHelper::chooseSolution (Goals::TGoalVec & vec)
|
||||
{
|
||||
if (vec.empty()) //no possibilities found
|
||||
return sptr(Goals::Invalid());
|
||||
|
||||
typedef std::pair<Goals::TSubgoal, float> goalValue;
|
||||
std::vector <goalValue> values;
|
||||
|
||||
@ -294,20 +320,109 @@ Goals::TSubgoal FuzzyHelper::chooseSolution (Goals::TGoalVec & vec)
|
||||
};
|
||||
|
||||
boost::sort (values, compareGoals);
|
||||
return values.end()->first;
|
||||
return values.back().first;
|
||||
}
|
||||
|
||||
float FuzzyHelper::evaluate (Goals::Explore & g)
|
||||
{
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
float FuzzyHelper::evaluate (Goals::RecruitHero & g)
|
||||
{
|
||||
return 0;
|
||||
return 1; //just try to recruit hero as one of options
|
||||
}
|
||||
FuzzyHelper::EvalVisitTile::~EvalVisitTile()
|
||||
{
|
||||
delete strengthRatio;
|
||||
delete heroStrength;
|
||||
delete tileDistance;
|
||||
delete missionImportance;
|
||||
}
|
||||
|
||||
void FuzzyHelper::initVisitTile()
|
||||
{
|
||||
std::vector<fl::InputLVar*> helper;
|
||||
|
||||
vt.strengthRatio = new fl::InputLVar("strengthRatio"); //hero must be strong enough to defeat guards
|
||||
vt.heroStrength = new fl::InputLVar("heroStrength"); //we want to use weakest possible hero
|
||||
vt.tileDistance = new fl::InputLVar("tileDistance"); //we want to use hero who is near
|
||||
vt.missionImportance = new fl::InputLVar("lockedMissionImportance"); //we may want to preempt hero with low-priority mission
|
||||
vt.value = new fl::OutputLVar("Value");
|
||||
|
||||
helper += vt.strengthRatio, vt.heroStrength, vt.tileDistance, vt.missionImportance;
|
||||
|
||||
vt.strengthRatio->addTerm (new fl::ShoulderTerm("LOW", 0.3, SAFE_ATTACK_CONSTANT, true));
|
||||
vt.strengthRatio->addTerm (new fl::ShoulderTerm("HIGH", SAFE_ATTACK_CONSTANT, SAFE_ATTACK_CONSTANT * 3, false));
|
||||
|
||||
vt.heroStrength->addTerm (new fl::ShoulderTerm("LOW", 1, 2500, true)); //assumed strength of new hero from tavern
|
||||
vt.heroStrength->addTerm (new fl::TriangularTerm("MEDIUM", 2500, 40000)); //assumed strength of hero able to defeat bank
|
||||
vt.heroStrength->addTerm (new fl::ShoulderTerm("HIGH", 40000, 1e5, false)); //assumed strength of hero able to clear utopia
|
||||
|
||||
vt.tileDistance->addTerm (new fl::ShoulderTerm("SMALL", 0, 3.5, true));
|
||||
vt.tileDistance->addTerm (new fl::TriangularTerm("MEDIUM", 3, 10.5));
|
||||
vt.tileDistance->addTerm (new fl::ShoulderTerm("LONG", 10, 50, false));
|
||||
|
||||
vt.missionImportance->addTerm (new fl::ShoulderTerm("LOW", 0, 3.1, true));
|
||||
vt.missionImportance->addTerm (new fl::TriangularTerm("MEDIUM", 2, 9.5));
|
||||
vt.missionImportance->addTerm (new fl::ShoulderTerm("HIGH", 4.5, 10, false));
|
||||
|
||||
vt.value->addTerm (new fl::ShoulderTerm("LOW", 0, 1.1, true));
|
||||
vt.value->addTerm (new fl::ShoulderTerm("HIGH", 1, 5, false));
|
||||
|
||||
for (auto val : helper)
|
||||
{
|
||||
engine.addInputLVar(val);
|
||||
}
|
||||
engine.addOutputLVar (vt.value);
|
||||
|
||||
//vt.rules.addRule (new fl::MamdaniRule("if OurShooters is MANY and EnemySpeed is LOW then Threat is very LOW", engine));
|
||||
//use unarmed scouts if possible
|
||||
vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is HIGH and heroStrength is LOW then Value is very HIGH", engine));
|
||||
//if medium heroes can't scratch enemy, don't try to arm them
|
||||
vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is LOW and heroStrength is MEDIUM then Value is LOW", engine));
|
||||
//do not cancel important goals
|
||||
vt.rules.addRule (new fl::MamdaniRule("if lockedMissionImportance is HIGH then Value is very LOW", engine));
|
||||
//pick nearby objects if it's easy, avoid long walks
|
||||
vt.rules.addRule (new fl::MamdaniRule("if tileDistance is SMALL then Value is HIGH", engine));
|
||||
vt.rules.addRule (new fl::MamdaniRule("if tileDistance is LONG then Value is LOW", engine));
|
||||
|
||||
engine.addRuleBlock (&vt.rules);
|
||||
}
|
||||
float FuzzyHelper::evaluate (Goals::VisitTile & g)
|
||||
{
|
||||
return 0;
|
||||
if (!g.hero)
|
||||
return 0;
|
||||
|
||||
float output = 0;
|
||||
|
||||
cb->setSelection (g.hero.h);
|
||||
int distance = cb->getDistance(g.tile); //at this point we already assume tile is reachable
|
||||
|
||||
float missionImportance = 0;
|
||||
if (vstd::contains(ai->lockedHeroes, g.hero))
|
||||
missionImportance = ai->lockedHeroes[g.hero]->importanceWhenLocked();
|
||||
|
||||
float strengthRatio = 100; //we are much stronger than enemy
|
||||
ui64 danger = evaluateDanger (g.tile, g.hero.h);
|
||||
if (danger)
|
||||
strengthRatio = g.hero.h->getTotalStrength() / danger;
|
||||
|
||||
try
|
||||
{
|
||||
vt.strengthRatio->setInput (strengthRatio);
|
||||
vt.heroStrength->setInput (g.hero->getTotalStrength());
|
||||
vt.tileDistance->setInput (distance);
|
||||
vt.missionImportance->setInput (missionImportance);
|
||||
|
||||
engine.process (VISIT_TILE);
|
||||
output = vt.value->output().defuzzify();
|
||||
}
|
||||
catch (fl::FuzzyException & fe)
|
||||
{
|
||||
logAi->errorStream() << "evaluate VisitTile " << fe.name() << ": " << fe.message();
|
||||
}
|
||||
return output;
|
||||
|
||||
}
|
||||
float FuzzyHelper::evaluate (Goals::VisitHero & g)
|
||||
{
|
||||
|
@ -28,19 +28,37 @@ class FuzzyHelper
|
||||
fl::OutputLVar* bankDanger;
|
||||
fl::RuleBlock bankBlock;
|
||||
|
||||
fl::InputLVar * ourWalkers, * ourShooters, * ourFlyers, * enemyWalkers, * enemyShooters, * enemyFlyers;
|
||||
fl::InputLVar * ourSpeed, * enemySpeed;
|
||||
fl::InputLVar * bankPresent;
|
||||
fl::InputLVar * castleWalls;
|
||||
fl::OutputLVar * threat;
|
||||
fl::RuleBlock tacticalAdvantage;
|
||||
class TacticalAdvantage
|
||||
{
|
||||
public:
|
||||
fl::InputLVar * ourWalkers, * ourShooters, * ourFlyers, * enemyWalkers, * enemyShooters, * enemyFlyers;
|
||||
fl::InputLVar * ourSpeed, * enemySpeed;
|
||||
fl::InputLVar * bankPresent;
|
||||
fl::InputLVar * castleWalls;
|
||||
fl::OutputLVar * threat;
|
||||
fl::RuleBlock tacticalAdvantage;
|
||||
~TacticalAdvantage();
|
||||
} ta;
|
||||
|
||||
class EvalVisitTile
|
||||
{
|
||||
public:
|
||||
fl::InputLVar * strengthRatio;
|
||||
fl::InputLVar * heroStrength;
|
||||
fl::InputLVar * tileDistance;
|
||||
fl::InputLVar * missionImportance;
|
||||
fl::OutputLVar * value;
|
||||
fl::RuleBlock rules;
|
||||
~EvalVisitTile();
|
||||
} vt;
|
||||
|
||||
public:
|
||||
enum RuleBlocks {BANK_DANGER, TACTICAL_ADVANTAGE};
|
||||
enum RuleBlocks {BANK_DANGER, TACTICAL_ADVANTAGE, VISIT_TILE}; //where is it used and why it's needed?
|
||||
|
||||
FuzzyHelper();
|
||||
void initBank();
|
||||
void initTacticalAdvantage();
|
||||
void initVisitTile();
|
||||
|
||||
float evaluate (Goals::Explore & g);
|
||||
float evaluate (Goals::RecruitHero & g);
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh; //TODO: this logic should be moved inside VCAI
|
||||
|
||||
using namespace vstd;
|
||||
using namespace Goals;
|
||||
@ -225,6 +226,11 @@ TSubgoal FindObj::whatToDoToAchieve()
|
||||
else
|
||||
return sptr (Goals::Explore());
|
||||
}
|
||||
float FindObj::importanceWhenLocked() const
|
||||
{
|
||||
return 1; //we will probably fins it anyway, someday
|
||||
}
|
||||
|
||||
std::string GetObj::completeMessage() const
|
||||
{
|
||||
return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid);
|
||||
@ -239,6 +245,12 @@ TSubgoal GetObj::whatToDoToAchieve()
|
||||
return sptr (Goals::VisitTile(pos));
|
||||
}
|
||||
|
||||
float GetObj::importanceWhenLocked() const
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
bool GetObj::fulfillsMe (shared_ptr<VisitTile> goal)
|
||||
{
|
||||
if (cb->getObj(ObjectInstanceID(objid))->visitablePos() == goal->tile)
|
||||
@ -268,6 +280,11 @@ TSubgoal VisitHero::whatToDoToAchieve()
|
||||
return sptr (Goals::Invalid());
|
||||
}
|
||||
|
||||
float VisitHero::importanceWhenLocked() const
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
bool VisitHero::fulfillsMe (shared_ptr<VisitTile> goal)
|
||||
{
|
||||
if (cb->getObj(ObjectInstanceID(objid))->visitablePos() == goal->tile)
|
||||
@ -284,6 +301,11 @@ TSubgoal GetArtOfType::whatToDoToAchieve()
|
||||
return sptr (Goals::Invalid());
|
||||
}
|
||||
|
||||
float GetArtOfType::importanceWhenLocked() const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
TSubgoal ClearWayTo::whatToDoToAchieve()
|
||||
{
|
||||
assert(tile.x >= 0); //set tile
|
||||
@ -334,6 +356,11 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
|
||||
throw cannotFulfillGoalException("Cannot reach given tile!"); //how and when could this be used?
|
||||
}
|
||||
|
||||
float ClearWayTo::importanceWhenLocked() const
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
std::string Explore::completeMessage() const
|
||||
{
|
||||
return "Hero " + hero.get()->name + " completed exploration";
|
||||
@ -452,6 +479,11 @@ TSubgoal Explore::whatToDoToAchieve()
|
||||
return iAmElementar(); //FIXME: how can this be called?
|
||||
};
|
||||
|
||||
float Explore::importanceWhenLocked() const
|
||||
{
|
||||
return 1; //exploration is natural and lowpriority process
|
||||
}
|
||||
|
||||
TSubgoal RecruitHero::whatToDoToAchieve()
|
||||
{
|
||||
const CGTownInstance *t = ai->findTownWithTavern();
|
||||
@ -471,42 +503,63 @@ std::string VisitTile::completeMessage() const
|
||||
|
||||
TSubgoal VisitTile::whatToDoToAchieve()
|
||||
{
|
||||
if(!cb->isVisible(tile))
|
||||
return sptr (Goals::Explore());
|
||||
//here only temporarily
|
||||
auto ret = fh->chooseSolution (getAllPossibleSubgoals());
|
||||
|
||||
if(hero && !ai->isAccessibleForHero(tile, hero))
|
||||
hero = nullptr;
|
||||
|
||||
if(!hero)
|
||||
if (ret->hero)
|
||||
{
|
||||
if(cb->getHeroesInfo().empty())
|
||||
if (isSafeToVisit(ret->hero, tile))
|
||||
{
|
||||
return sptr (Goals::RecruitHero());
|
||||
ret->setisElementar(true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for(const CGHeroInstance *h : cb->getHeroesInfo())
|
||||
{
|
||||
if(ai->isAccessibleForHero(tile, h))
|
||||
{
|
||||
hero = h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(hero)
|
||||
{
|
||||
if(isSafeToVisit(hero, tile))
|
||||
return sptr (setisElementar(true));
|
||||
else
|
||||
{
|
||||
return sptr (Goals::GatherArmy(evaluateDanger(tile, *hero) * SAFE_ATTACK_CONSTANT).sethero(hero));
|
||||
return sptr (Goals::GatherArmy(evaluateDanger(tile, *ret->hero) * SAFE_ATTACK_CONSTANT).sethero(ret->hero));
|
||||
}
|
||||
}
|
||||
else //inaccessible for all heroes
|
||||
{
|
||||
return sptr (Goals::ClearWayTo(tile));
|
||||
}
|
||||
return ret;
|
||||
//if(!cb->isVisible(tile))
|
||||
// return sptr (Goals::Explore());
|
||||
|
||||
//if(hero && !ai->isAccessibleForHero(tile, hero))
|
||||
// hero = nullptr;
|
||||
|
||||
//if(!hero)
|
||||
//{
|
||||
// if(cb->getHeroesInfo().empty())
|
||||
// {
|
||||
// return sptr (Goals::RecruitHero());
|
||||
// }
|
||||
|
||||
// for(const CGHeroInstance *h : cb->getHeroesInfo())
|
||||
// {
|
||||
// if(ai->isAccessibleForHero(tile, h))
|
||||
// {
|
||||
// hero = h;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//if(hero)
|
||||
//{
|
||||
// if(isSafeToVisit(hero, tile))
|
||||
// return sptr (setisElementar(true));
|
||||
// else
|
||||
// {
|
||||
// return sptr (Goals::GatherArmy(evaluateDanger(tile, *hero) * SAFE_ATTACK_CONSTANT).sethero(hero));
|
||||
// }
|
||||
//}
|
||||
//else //inaccessible for all heroes
|
||||
//{
|
||||
// return sptr (Goals::ClearWayTo(tile));
|
||||
//}
|
||||
}
|
||||
|
||||
float VisitTile::importanceWhenLocked() const
|
||||
{
|
||||
return 5; //depends on a distance, but we should really reach the tile once it was selected
|
||||
}
|
||||
|
||||
TGoalVec VisitTile::getAllPossibleSubgoals()
|
||||
@ -518,11 +571,15 @@ TGoalVec VisitTile::getAllPossibleSubgoals()
|
||||
{
|
||||
for (auto h : cb->getHeroesInfo())
|
||||
{
|
||||
ret.push_back (sptr(Goals::VisitTile(tile).sethero(h)));
|
||||
if (ai->isAccessibleForHero(tile, h))
|
||||
ret.push_back (sptr(Goals::VisitTile(tile).sethero(h)));
|
||||
}
|
||||
if (ai->canRecruitAnyHero())
|
||||
ret.push_back (sptr(Goals::RecruitHero()));
|
||||
}
|
||||
if (ret.empty())
|
||||
ret.push_back (sptr(Goals::ClearWayTo(tile)));
|
||||
//important - at least one sub-goal must handle case which is impossible to fulfill (unreachable tile)
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -539,6 +596,11 @@ TSubgoal DigAtTile::whatToDoToAchieve()
|
||||
return sptr (Goals::VisitTile(tile));
|
||||
}
|
||||
|
||||
float DigAtTile::importanceWhenLocked() const
|
||||
{
|
||||
return 20; //do not! interrupt tile digging
|
||||
}
|
||||
|
||||
TSubgoal BuildThis::whatToDoToAchieve()
|
||||
{
|
||||
//TODO check res
|
||||
@ -547,6 +609,11 @@ TSubgoal BuildThis::whatToDoToAchieve()
|
||||
return iAmElementar();
|
||||
}
|
||||
|
||||
float BuildThis::importanceWhenLocked() const
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
TSubgoal CollectRes::whatToDoToAchieve()
|
||||
{
|
||||
std::vector<const IMarket*> markets;
|
||||
@ -614,6 +681,11 @@ TSubgoal CollectRes::whatToDoToAchieve()
|
||||
return sptr (Goals::Invalid()); //FIXME: unused?
|
||||
}
|
||||
|
||||
float CollectRes::importanceWhenLocked() const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
TSubgoal GatherTroops::whatToDoToAchieve()
|
||||
{
|
||||
std::vector<const CGDwelling *> dwellings;
|
||||
@ -669,6 +741,11 @@ TSubgoal GatherTroops::whatToDoToAchieve()
|
||||
//TODO: exchange troops between heroes
|
||||
}
|
||||
|
||||
float GatherTroops::importanceWhenLocked() const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
TSubgoal Conquer::whatToDoToAchieve()
|
||||
{
|
||||
auto hs = cb->getHeroesInfo();
|
||||
@ -748,12 +825,22 @@ TSubgoal Conquer::whatToDoToAchieve()
|
||||
|
||||
return sptr (Goals::Explore()); //enemy is inaccessible
|
||||
}
|
||||
float Conquer::importanceWhenLocked() const
|
||||
{
|
||||
return 10; //defeating opponent is hig priority, always
|
||||
}
|
||||
|
||||
|
||||
TSubgoal Build::whatToDoToAchieve()
|
||||
{
|
||||
return iAmElementar();
|
||||
}
|
||||
|
||||
float Build::importanceWhenLocked() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
TSubgoal Invalid::whatToDoToAchieve()
|
||||
{
|
||||
return iAmElementar();
|
||||
@ -879,6 +966,11 @@ TSubgoal GatherArmy::whatToDoToAchieve()
|
||||
return sptr (Goals::Explore(hero)); //find dwelling. use current hero to prevent him from doing nothing.
|
||||
}
|
||||
|
||||
float GatherArmy::importanceWhenLocked() const
|
||||
{
|
||||
return 2.5;
|
||||
}
|
||||
|
||||
//TSubgoal AbstractGoal::whatToDoToAchieve()
|
||||
//{
|
||||
// logAi->debugStream() << boost::format("Decomposing goal of type %s") % name();
|
||||
|
@ -92,6 +92,7 @@ public:
|
||||
virtual AbstractGoal * clone() const = 0;
|
||||
|
||||
EGoals goalType;
|
||||
|
||||
std::string name() const;
|
||||
virtual std::string completeMessage() const {return "This goal is unspecified!";};
|
||||
|
||||
@ -103,6 +104,9 @@ public:
|
||||
|
||||
virtual TGoalVec getAllPossibleSubgoals() = 0;
|
||||
virtual TSubgoal whatToDoToAchieve() = 0;
|
||||
virtual float importanceWhenLocked() const {return -1e10;}; //how much would it cost to interrupt the goal
|
||||
//probably could use some sophisticated fuzzy evalluation for it as well
|
||||
|
||||
///Visitor pattern
|
||||
//TODO: make accept work for shared_ptr... somehow
|
||||
virtual void accept (VCAI * ai); //unhandled goal will report standard error
|
||||
@ -186,6 +190,7 @@ class Invalid : public CGoal<Invalid>
|
||||
Invalid() : CGoal (Goals::INVALID){};
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
//float importanceWhenLocked() const override;
|
||||
};
|
||||
class Win : public CGoal<Win>
|
||||
{
|
||||
@ -193,6 +198,7 @@ class Win : public CGoal<Win>
|
||||
Win() : CGoal (Goals::WIN){};
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
//float importanceWhenLocked() const override; //can't be locked, doesn't make much sense anyway
|
||||
};
|
||||
class NotLose : public CGoal<NotLose>
|
||||
{
|
||||
@ -200,6 +206,7 @@ class NotLose : public CGoal<NotLose>
|
||||
NotLose() : CGoal (Goals::DO_NOT_LOSE){};
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class Conquer : public CGoal<Conquer>
|
||||
{
|
||||
@ -207,6 +214,7 @@ class Conquer : public CGoal<Conquer>
|
||||
Conquer() : CGoal (Goals::CONQUER){};
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class Build : public CGoal<Build>
|
||||
{
|
||||
@ -214,6 +222,7 @@ class Build : public CGoal<Build>
|
||||
Build() : CGoal (Goals::BUILD){};
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class Explore : public CGoal<Explore>
|
||||
{
|
||||
@ -223,6 +232,7 @@ class Explore : public CGoal<Explore>
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class GatherArmy : public CGoal<GatherArmy>
|
||||
{
|
||||
@ -233,6 +243,7 @@ public:
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class BoostHero : public CGoal<BoostHero>
|
||||
{
|
||||
@ -240,6 +251,7 @@ class BoostHero : public CGoal<BoostHero>
|
||||
BoostHero() : CGoal (Goals::INVALID){}; //TODO
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class RecruitHero : public CGoal<RecruitHero>
|
||||
{
|
||||
@ -247,6 +259,7 @@ class RecruitHero : public CGoal<RecruitHero>
|
||||
RecruitHero() : CGoal (Goals::RECRUIT_HERO){};
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
//float importanceWhenLocked() const override;
|
||||
};
|
||||
class BuildThis : public CGoal<BuildThis>
|
||||
{
|
||||
@ -257,6 +270,7 @@ public:
|
||||
BuildThis(BuildingID Bid) : CGoal (Goals::BUILD_STRUCTURE) {bid = Bid;};
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class CollectRes : public CGoal<CollectRes>
|
||||
{
|
||||
@ -266,6 +280,7 @@ public:
|
||||
CollectRes(int rid, int val) : CGoal (Goals::COLLECT_RES) {resID = rid; value = val;};
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class GatherTroops : public CGoal<GatherTroops>
|
||||
{
|
||||
@ -275,6 +290,7 @@ public:
|
||||
GatherTroops(int type, int val) : CGoal (Goals::GATHER_TROOPS){objid = type; value = val;};
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class GetObj : public CGoal<GetObj>
|
||||
{
|
||||
@ -287,6 +303,7 @@ public:
|
||||
bool operator== (GetObj &g) {return g.objid == objid;}
|
||||
bool fulfillsMe (shared_ptr<VisitTile> goal) override;
|
||||
std::string completeMessage() const override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class FindObj : public CGoal<FindObj>
|
||||
{
|
||||
@ -297,6 +314,7 @@ public:
|
||||
FindObj(int ID, int subID) : CGoal(Goals::FIND_OBJ) {objid = ID; resID = subID;};
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class VisitHero : public CGoal<VisitHero>
|
||||
{
|
||||
@ -309,6 +327,7 @@ public:
|
||||
bool operator== (VisitHero &g) {return g.objid == objid;}
|
||||
bool fulfillsMe (shared_ptr<VisitTile> goal) override;
|
||||
std::string completeMessage() const override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class GetArtOfType : public CGoal<GetArtOfType>
|
||||
{
|
||||
@ -318,6 +337,7 @@ public:
|
||||
GetArtOfType(int type) : CGoal (Goals::GET_ART_TYPE){aid = type;};
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class VisitTile : public CGoal<VisitTile>
|
||||
//tile, in conjunction with hero elementar; assumes tile is reachable
|
||||
@ -330,6 +350,7 @@ public:
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool operator== (VisitTile &g) {return g.tile == tile;}
|
||||
std::string completeMessage() const override;
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class ClearWayTo : public CGoal<ClearWayTo>
|
||||
{
|
||||
@ -338,6 +359,7 @@ public:
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool operator== (ClearWayTo &g) {return g.tile == tile;}
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
class DigAtTile : public CGoal<DigAtTile>
|
||||
//elementar with hero on tile
|
||||
@ -349,6 +371,7 @@ public:
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool operator== (DigAtTile &g) {return g.tile == tile;}
|
||||
float importanceWhenLocked() const override;
|
||||
};
|
||||
|
||||
class CIssueCommand : public CGoal<CIssueCommand>
|
||||
@ -359,6 +382,7 @@ class CIssueCommand : public CGoal<CIssueCommand>
|
||||
CIssueCommand(std::function<bool()> _command): CGoal(ISSUE_COMMAND), command(_command) {}
|
||||
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
//float importanceWhenLocked() const override; //unsupported yet, but shoudl be highest
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -311,6 +311,15 @@ const CGPathNode * CCallback::getPathInfo( int3 tile )
|
||||
return &cl->pathInfo->nodes[tile.x][tile.y][tile.z];
|
||||
}
|
||||
|
||||
const int CCallback::getDistance( int3 tile )
|
||||
{
|
||||
CGPath ret;
|
||||
if (getPath2 (tile, ret))
|
||||
return ret.nodes.size();
|
||||
else
|
||||
return 255;
|
||||
}
|
||||
|
||||
bool CCallback::getPath2( int3 dest, CGPath &ret )
|
||||
{
|
||||
if (!gs->map->isInTheMap(dest))
|
||||
|
@ -108,6 +108,7 @@ public:
|
||||
|
||||
//client-specific functionalities (pathfinding)
|
||||
virtual const CGPathNode *getPathInfo(int3 tile); //uses main, client pathfinder info
|
||||
virtual const int getDistance(int3 tile);
|
||||
virtual bool getPath2(int3 dest, CGPath &ret); //uses main, client pathfinder info
|
||||
|
||||
virtual void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src = int3(-1,-1,-1), int movement = -1);
|
||||
|
@ -227,8 +227,8 @@ struct DLL_LINKAGE CGPathNode
|
||||
|
||||
EAccessibility accessible;
|
||||
ui8 land;
|
||||
ui8 turns;
|
||||
ui32 moveRemains;
|
||||
ui8 turns; //how many turns we have to wait before reachng the tile - 0 means current turn
|
||||
ui32 moveRemains; //remaining tiles after hero reaches the tile
|
||||
CGPathNode * theNodeBefore;
|
||||
int3 coord; //coordinates
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user