1
0
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:
DjWarmonger 2013-12-20 09:43:12 +00:00
parent 983c0496d0
commit d085f8eee8
7 changed files with 352 additions and 93 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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