Fuzzy.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. #include "StdInc.h"
  2. #include "Fuzzy.h"
  3. #include <limits>
  4. #include "../../lib/mapObjects/MapObjects.h"
  5. #include "../../lib/CCreatureHandler.h"
  6. #include "../../lib/VCMI_Lib.h"
  7. #include "../../CCallback.h"
  8. #include "VCAI.h"
  9. /*
  10. * Fuzzy.cpp, part of VCMI engine
  11. *
  12. * Authors: listed in file AUTHORS in main folder
  13. *
  14. * License: GNU General Public License v2.0 or later
  15. * Full text of license available in license.txt file, in main folder
  16. *
  17. */
  18. #define MIN_AI_STRENGHT (0.5f) //lower when combat AI gets smarter
  19. #define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 10 times weaker than us
  20. struct BankConfig;
  21. class FuzzyEngine;
  22. class InputLVar;
  23. class CGTownInstance;
  24. using namespace boost::assign;
  25. using namespace vstd;
  26. //using namespace Goals;
  27. FuzzyHelper *fh;
  28. extern boost::thread_specific_ptr<CCallback> cb;
  29. extern boost::thread_specific_ptr<VCAI> ai;
  30. struct armyStructure
  31. {
  32. float walkers, shooters, flyers;
  33. ui32 maxSpeed;
  34. };
  35. ui64 evaluateBankConfig (BankConfig * bc)
  36. {
  37. ui64 danger = 0;
  38. for (auto opt : bc->guards)
  39. {
  40. danger += VLC->creh->creatures[opt.first]->fightValue * opt.second;
  41. }
  42. return danger;
  43. }
  44. armyStructure evaluateArmyStructure (const CArmedInstance * army)
  45. {
  46. ui64 totalStrenght = army->getArmyStrength();
  47. double walkersStrenght = 0;
  48. double flyersStrenght = 0;
  49. double shootersStrenght = 0;
  50. ui32 maxSpeed = 0;
  51. for(auto s : army->Slots())
  52. {
  53. bool walker = true;
  54. if (s.second->type->hasBonusOfType(Bonus::SHOOTER))
  55. {
  56. shootersStrenght += s.second->getPower();
  57. walker = false;
  58. }
  59. if (s.second->type->hasBonusOfType(Bonus::FLYING))
  60. {
  61. flyersStrenght += s.second->getPower();
  62. walker = false;
  63. }
  64. if (walker)
  65. walkersStrenght += s.second->getPower();
  66. amax (maxSpeed, s.second->type->valOfBonuses(Bonus::STACKS_SPEED));
  67. }
  68. armyStructure as;
  69. as.walkers = walkersStrenght / totalStrenght;
  70. as.shooters = shootersStrenght / totalStrenght;
  71. as.flyers = flyersStrenght / totalStrenght;
  72. as.maxSpeed = maxSpeed;
  73. return as;
  74. }
  75. FuzzyHelper::FuzzyHelper()
  76. {
  77. engine.hedgeSet().add(new fl::HedgeSomewhat());
  78. engine.hedgeSet().add(new fl::HedgeVery());
  79. engine.fuzzyOperator().setAggregation(new fl::FuzzyOrSum()); //to consider all cases simultaneously
  80. initBank();
  81. initTacticalAdvantage();
  82. initVisitTile();
  83. logAi->infoStream() << engine.toString();
  84. }
  85. void FuzzyHelper::initBank()
  86. {
  87. try
  88. {
  89. //Trivial bank estimation
  90. bankInput = new fl::InputLVar("BankInput");
  91. bankDanger = new fl::OutputLVar("BankDanger");
  92. bankInput->addTerm(new fl::SingletonTerm ("SET", 0.5));
  93. engine.addInputLVar (bankInput);
  94. engine.addOutputLVar (bankDanger);
  95. engine.addRuleBlock (&bankBlock);
  96. for (int i = 0; i < 4; ++i)
  97. {
  98. bankDanger->addTerm(new fl::TriangularTerm ("Bank" + boost::lexical_cast<std::string>(i), 0, 1));
  99. bankBlock.addRule(new fl::MamdaniRule("if BankInput is SET then BankDanger is Bank" + boost::lexical_cast<std::string>(i), engine));
  100. }
  101. }
  102. catch (fl::FuzzyException & fe)
  103. {
  104. logAi->errorStream() << "initBank " << fe.name() << ": " << fe.message();
  105. }
  106. }
  107. void FuzzyHelper::initTacticalAdvantage()
  108. {
  109. try
  110. {
  111. //Tactical advantage calculation
  112. std::vector<fl::InputLVar*> helper;
  113. ta.ourShooters = new fl::InputLVar("OurShooters");
  114. ta.ourWalkers = new fl::InputLVar("OurWalkers");
  115. ta.ourFlyers = new fl::InputLVar("OurFlyers");
  116. ta.enemyShooters = new fl::InputLVar("EnemyShooters");
  117. ta.enemyWalkers = new fl::InputLVar("EnemyWalkers");
  118. ta.enemyFlyers = new fl::InputLVar("EnemyFlyers");
  119. helper += ta.ourShooters, ta.ourWalkers, ta.ourFlyers, ta.enemyShooters, ta.enemyWalkers, ta.enemyFlyers;
  120. for (auto val : helper)
  121. {
  122. engine.addInputLVar(val);
  123. val->addTerm (new fl::ShoulderTerm("FEW", 0, 0.6, true));
  124. val->addTerm (new fl::ShoulderTerm("MANY", 0.4, 1, false));
  125. }
  126. helper.clear();
  127. ta.ourSpeed = new fl::InputLVar("OurSpeed");
  128. ta.enemySpeed = new fl::InputLVar("EnemySpeed");
  129. helper += ta.ourSpeed, ta.enemySpeed;
  130. for (auto val : helper)
  131. {
  132. engine.addInputLVar(val);
  133. val->addTerm (new fl::ShoulderTerm("LOW", 3, 6.5, true));
  134. val->addTerm (new fl::TriangularTerm("MEDIUM", 5.5, 10.5));
  135. val->addTerm (new fl::ShoulderTerm("HIGH", 8.5, 16, false));
  136. }
  137. ta.castleWalls = new fl::InputLVar("CastleWalls");
  138. engine.addInputLVar(ta.castleWalls);
  139. ta.castleWalls->addTerm(new fl::SingletonTerm("NONE", CGTownInstance::NONE));
  140. ta.castleWalls->addTerm(new fl::TrapezoidalTerm("MEDIUM", CGTownInstance::FORT, 2.5));
  141. ta.castleWalls->addTerm(new fl::ShoulderTerm("HIGH", CGTownInstance::CITADEL - 0.1, CGTownInstance::CASTLE, false));
  142. ta.bankPresent = new fl::InputLVar("Bank");
  143. engine.addInputLVar(ta.bankPresent);
  144. ta.bankPresent->addTerm(new fl::SingletonTerm("FALSE", 0));
  145. ta.bankPresent->addTerm(new fl::SingletonTerm("TRUE", 1));
  146. ta.threat = new fl::OutputLVar("Threat");
  147. engine.addOutputLVar(ta.threat);
  148. ta.threat->addTerm (new fl::ShoulderTerm("LOW", MIN_AI_STRENGHT, 1, true));
  149. ta.threat->addTerm (new fl::TriangularTerm("MEDIUM", 0.8, 1.2));
  150. ta.threat->addTerm (new fl::ShoulderTerm("HIGH", 1, 1.5, false));
  151. engine.addRuleBlock (&ta.tacticalAdvantage);
  152. ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if OurShooters is MANY and EnemySpeed is LOW then Threat is LOW", engine));
  153. ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if OurShooters is MANY and EnemyShooters is FEW then Threat is LOW", engine));
  154. ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if OurSpeed is LOW and EnemyShooters is MANY then Threat is HIGH", engine));
  155. ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if OurSpeed is HIGH and EnemyShooters is MANY then Threat is LOW", engine));
  156. ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if OurWalkers is FEW and EnemyShooters is MANY then Threat is somewhat LOW", engine));
  157. ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if OurShooters is MANY and EnemySpeed is HIGH then Threat is somewhat HIGH", engine));
  158. //just to cover all cases
  159. ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if EnemySpeed is MEDIUM then Threat is MEDIUM", engine));
  160. ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if EnemySpeed is LOW and OurShooters is FEW then Threat is MEDIUM", engine));
  161. ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if Bank is TRUE and OurShooters is MANY then Threat is somewhat HIGH", engine));
  162. ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if Bank is TRUE and EnemyShooters is MANY then Threat is LOW", engine));
  163. ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if CastleWalls is HIGH and OurWalkers is MANY then Threat is very HIGH", engine));
  164. ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if CastleWalls is HIGH and OurFlyers is MANY and OurShooters is MANY then Threat is MEDIUM", engine));
  165. ta.tacticalAdvantage.addRule(new fl::MamdaniRule("if CastleWalls is MEDIUM and OurShooters is MANY and EnemyWalkers is MANY then Threat is LOW", engine));
  166. }
  167. catch(fl::ParsingException & pe)
  168. {
  169. logAi->errorStream() << "initTacticalAdvantage " << pe.name() << ": " << pe.message();
  170. }
  171. catch (fl::FuzzyException & fe)
  172. {
  173. logAi->errorStream() << "initTacticalAdvantage " << fe.name() << ": " << fe.message();
  174. }
  175. }
  176. ui64 FuzzyHelper::estimateBankDanger (int ID)
  177. {
  178. std::vector <ConstTransitivePtr<BankConfig>> & configs = VLC->objh->banksInfo[ID];
  179. ui64 val = std::numeric_limits<ui64>::max();
  180. try
  181. {
  182. switch (configs.size())
  183. {
  184. case 4:
  185. try
  186. {
  187. for (int i = 0; i < 4; ++i)
  188. {
  189. int bankVal = evaluateBankConfig (VLC->objh->banksInfo[ID][i]);
  190. bankDanger->term("Bank" + boost::lexical_cast<std::string>(i))->setMinimum(bankVal * 0.5f);
  191. bankDanger->term("Bank" + boost::lexical_cast<std::string>(i))->setMaximum(bankVal * 1.5f);
  192. }
  193. //comparison purposes
  194. //int averageValue = (evaluateBankConfig (VLC->objh->banksInfo[ID][0]) + evaluateBankConfig (VLC->objh->banksInfo[ID][3])) * 0.5;
  195. //dynamic_cast<fl::SingletonTerm*>(bankInput->term("SET"))->setValue(0.5);
  196. bankInput->setInput (0.5);
  197. engine.process (BANK_DANGER);
  198. //engine.process();
  199. val = bankDanger->output().defuzzify(); //some expected value of this bank
  200. }
  201. catch (fl::FuzzyException & fe)
  202. {
  203. logAi->errorStream() << fe.name() << ": " << fe.message();
  204. }
  205. break;
  206. case 1: //rare case - Pyramid
  207. val = evaluateBankConfig (VLC->objh->banksInfo[ID][0]);
  208. break;
  209. default:
  210. logAi->warnStream() << ("Uhnandled bank config!");
  211. }
  212. }
  213. catch (fl::FuzzyException & fe)
  214. {
  215. logAi->errorStream() << "estimateBankDanger " << fe.name() << ": " << fe.message();
  216. }
  217. return val;
  218. }
  219. float FuzzyHelper::getTacticalAdvantage (const CArmedInstance *we, const CArmedInstance *enemy)
  220. {
  221. float output = 1;
  222. try
  223. {
  224. armyStructure ourStructure = evaluateArmyStructure(we);
  225. armyStructure enemyStructure = evaluateArmyStructure(enemy);
  226. ta.ourWalkers->setInput(ourStructure.walkers);
  227. ta.ourShooters->setInput(ourStructure.shooters);
  228. ta.ourFlyers->setInput(ourStructure.flyers);
  229. ta.ourSpeed->setInput(ourStructure.maxSpeed);
  230. ta.enemyWalkers->setInput(enemyStructure.walkers);
  231. ta.enemyShooters->setInput(enemyStructure.shooters);
  232. ta.enemyFlyers->setInput(enemyStructure.flyers);
  233. ta.enemySpeed->setInput(enemyStructure.maxSpeed);
  234. bool bank = dynamic_cast<const CBank*>(enemy);
  235. if (bank)
  236. ta.bankPresent->setInput(1);
  237. else
  238. ta.bankPresent->setInput(0);
  239. const CGTownInstance * fort = dynamic_cast<const CGTownInstance*>(enemy);
  240. if (fort)
  241. {
  242. ta.castleWalls->setInput (fort->fortLevel());
  243. }
  244. else
  245. ta.castleWalls->setInput(0);
  246. engine.process (TACTICAL_ADVANTAGE);
  247. //engine.process();
  248. output = ta.threat->output().defuzzify();
  249. }
  250. catch (fl::FuzzyException & fe)
  251. {
  252. logAi->errorStream() << "getTacticalAdvantage " << fe.name() << ": " << fe.message();
  253. }
  254. return output;
  255. }
  256. FuzzyHelper::TacticalAdvantage::~TacticalAdvantage()
  257. {
  258. //TODO: smart pointers?
  259. delete ourWalkers;
  260. delete ourShooters;
  261. delete ourFlyers;
  262. delete enemyWalkers;
  263. delete enemyShooters;
  264. delete enemyFlyers;
  265. delete ourSpeed;
  266. delete enemySpeed;
  267. delete bankPresent;
  268. delete castleWalls;
  269. delete threat;
  270. }
  271. //shared_ptr<AbstractGoal> chooseSolution (std::vector<shared_ptr<AbstractGoal>> & vec)
  272. Goals::TSubgoal FuzzyHelper::chooseSolution (Goals::TGoalVec vec)
  273. {
  274. if (vec.empty()) //no possibilities found
  275. return sptr(Goals::Invalid());
  276. //a trick to switch between heroes less often - calculatePaths is costly
  277. auto sortByHeroes = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
  278. {
  279. return lhs->hero.h < rhs->hero.h;
  280. };
  281. boost::sort (vec, sortByHeroes);
  282. for (auto g : vec)
  283. {
  284. setPriority(g);
  285. }
  286. auto compareGoals = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
  287. {
  288. return lhs->priority < rhs->priority;
  289. };
  290. boost::sort (vec, compareGoals);
  291. return vec.back();
  292. }
  293. float FuzzyHelper::evaluate (Goals::Explore & g)
  294. {
  295. return 1;
  296. }
  297. float FuzzyHelper::evaluate (Goals::RecruitHero & g)
  298. {
  299. return 1; //just try to recruit hero as one of options
  300. }
  301. FuzzyHelper::EvalVisitTile::~EvalVisitTile()
  302. {
  303. delete strengthRatio;
  304. delete heroStrength;
  305. delete turnDistance;
  306. delete missionImportance;
  307. }
  308. void FuzzyHelper::initVisitTile()
  309. {
  310. try
  311. {
  312. std::vector<fl::InputLVar*> helper;
  313. vt.strengthRatio = new fl::InputLVar("strengthRatio"); //hero must be strong enough to defeat guards
  314. vt.heroStrength = new fl::InputLVar("heroStrength"); //we want to use weakest possible hero
  315. vt.turnDistance = new fl::InputLVar("turnDistance"); //we want to use hero who is near
  316. vt.missionImportance = new fl::InputLVar("lockedMissionImportance"); //we may want to preempt hero with low-priority mission
  317. vt.value = new fl::OutputLVar("Value");
  318. helper += vt.strengthRatio, vt.heroStrength, vt.turnDistance, vt.missionImportance;
  319. for (auto val : helper)
  320. {
  321. engine.addInputLVar(val);
  322. }
  323. engine.addOutputLVar (vt.value);
  324. vt.strengthRatio->addTerm (new fl::ShoulderTerm("LOW", 0, SAFE_ATTACK_CONSTANT, true));
  325. vt.strengthRatio->addTerm (new fl::ShoulderTerm("HIGH", SAFE_ATTACK_CONSTANT, SAFE_ATTACK_CONSTANT * 3, false));
  326. //strength compared to our main hero
  327. vt.heroStrength->addTerm (new fl::ShoulderTerm("LOW", 0, 0.2, true));
  328. vt.heroStrength->addTerm (new fl::TriangularTerm("MEDIUM", 0.2, 0.8));
  329. vt.heroStrength->addTerm (new fl::ShoulderTerm("HIGH", 0.5, 1, false));
  330. vt.turnDistance->addTerm (new fl::ShoulderTerm("SMALL", 0, 0.5, true));
  331. vt.turnDistance->addTerm (new fl::TriangularTerm("MEDIUM", 0.1, 0.8));
  332. vt.turnDistance->addTerm (new fl::ShoulderTerm("LONG", 0.5, 3, false));
  333. vt.missionImportance->addTerm (new fl::ShoulderTerm("LOW", 0, 2.5, true));
  334. vt.missionImportance->addTerm (new fl::TriangularTerm("MEDIUM", 2, 3));
  335. vt.missionImportance->addTerm (new fl::ShoulderTerm("HIGH", 2.5, 5, false));
  336. //an issue: in 99% cases this outputs center of mass (2.5) regardless of actual input :/
  337. //should be same as "mission Importance" to keep consistency
  338. vt.value->addTerm (new fl::ShoulderTerm("LOW", 0, 2.5, true));
  339. vt.value->addTerm (new fl::TriangularTerm("MEDIUM", 2, 3)); //can't be center of mass :/
  340. vt.value->addTerm (new fl::ShoulderTerm("HIGH", 2.5, 5, false));
  341. engine.addRuleBlock (&vt.rules);
  342. //use unarmed scouts if possible
  343. vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is HIGH and heroStrength is LOW then Value is very HIGH", engine));
  344. //we may want to use secondary hero(es) rather than main hero
  345. vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is HIGH and heroStrength is MEDIUM then Value is somewhat HIGH", engine));
  346. vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is HIGH and heroStrength is HIGH then Value is somewhat LOW", engine));
  347. //don't assign targets to heroes who are too weak, but prefer targets of our main hero (in case we need to gather army)
  348. vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is LOW and heroStrength is LOW then Value is very LOW", engine));
  349. //attempt to arm secondary heroes is not stupid
  350. vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is LOW and heroStrength is MEDIUM then Value is somewhat HIGH", engine));
  351. vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is LOW and heroStrength is HIGH then Value is LOW", engine));
  352. //do not cancel important goals
  353. vt.rules.addRule (new fl::MamdaniRule("if lockedMissionImportance is HIGH then Value is very LOW", engine));
  354. vt.rules.addRule (new fl::MamdaniRule("if lockedMissionImportance is MEDIUM then Value is somewhat LOW", engine));
  355. vt.rules.addRule (new fl::MamdaniRule("if lockedMissionImportance is LOW then Value is HIGH", engine));
  356. //pick nearby objects if it's easy, avoid long walks
  357. vt.rules.addRule (new fl::MamdaniRule("if turnDistance is SMALL then Value is HIGH", engine));
  358. vt.rules.addRule (new fl::MamdaniRule("if turnDistance is MEDIUM then Value is MEDIUM", engine));
  359. vt.rules.addRule (new fl::MamdaniRule("if turnDistance is LONG then Value is LOW", engine));
  360. }
  361. catch (fl::FuzzyException & fe)
  362. {
  363. logAi->errorStream() << "visitTile " << fe.name() << ": " << fe.message();
  364. }
  365. }
  366. float FuzzyHelper::evaluate (Goals::VisitTile & g)
  367. {
  368. //we assume that hero is already set and we want to choose most suitable one for the mission
  369. if (!g.hero)
  370. return 0;
  371. //assert(cb->isInTheMap(g.tile));
  372. cb->setSelection (g.hero.h);
  373. float turns = 0;
  374. float distance = cb->getMovementCost(g.hero.h, g.tile);
  375. if (!distance) //we stand on that tile
  376. turns = 0;
  377. else
  378. {
  379. if (distance < g.hero->movement) //we can move there within one turn
  380. turns = (fl::flScalar)distance /g.hero->movement;
  381. else
  382. turns = 1 + (fl::flScalar)(distance - g.hero->movement)/g.hero->maxMovePoints(true); //bool on land?
  383. }
  384. float missionImportance = 0;
  385. if (vstd::contains(ai->lockedHeroes, g.hero))
  386. missionImportance = ai->lockedHeroes[g.hero]->priority;
  387. float strengthRatio = 10.0f; //we are much stronger than enemy
  388. ui64 danger = evaluateDanger (g.tile, g.hero.h);
  389. if (danger)
  390. strengthRatio = (fl::flScalar)g.hero.h->getTotalStrength() / danger;
  391. try
  392. {
  393. vt.strengthRatio->setInput (strengthRatio);
  394. vt.heroStrength->setInput ((fl::flScalar)g.hero->getTotalStrength()/ai->primaryHero()->getTotalStrength());
  395. vt.turnDistance->setInput (turns);
  396. vt.missionImportance->setInput (missionImportance);
  397. //engine.process();
  398. engine.process (VISIT_TILE);
  399. g.priority = vt.value->output().defuzzify();
  400. }
  401. catch (fl::FuzzyException & fe)
  402. {
  403. logAi->errorStream() << "evaluate VisitTile " << fe.name() << ": " << fe.message();
  404. }
  405. return g.priority;
  406. }
  407. float FuzzyHelper::evaluate (Goals::VisitHero & g)
  408. {
  409. auto obj = cb->getObj(ObjectInstanceID(g.objid)); //we assume for now that these goals are similar
  410. if (!obj)
  411. return -100; //hero died in the meantime
  412. //TODO: consider direct copy (constructor?)
  413. g.setpriority(Goals::VisitTile(obj->visitablePos()).sethero(g.hero).setisAbstract(g.isAbstract).accept(this));
  414. return g.priority;
  415. }
  416. float FuzzyHelper::evaluate (Goals::GatherArmy & g)
  417. {
  418. //the more army we need, the more important goal
  419. //the more army we lack, the less important goal
  420. float army = g.hero->getArmyStrength();
  421. return g.value / std::max(g.value - army, 1000.0f);
  422. }
  423. float FuzzyHelper::evaluate (Goals::ClearWayTo & g)
  424. {
  425. if (!g.hero.h)
  426. throw cannotFulfillGoalException("ClearWayTo called without hero!");
  427. SectorMap sm(g.hero);
  428. int3 t = sm.firstTileToGet(g.hero, g.tile);
  429. if (t.valid())
  430. {
  431. if (isSafeToVisit(g.hero, t))
  432. {
  433. g.setpriority(Goals::VisitTile(g.tile).sethero(g.hero).setisAbstract(g.isAbstract).accept(this));
  434. }
  435. else
  436. {
  437. g.setpriority (Goals::GatherArmy(evaluateDanger(t, g.hero.h)*SAFE_ATTACK_CONSTANT).
  438. sethero(g.hero).setisAbstract(true).accept(this));
  439. }
  440. return g.priority;
  441. }
  442. else
  443. return -1;
  444. }
  445. float FuzzyHelper::evaluate (Goals::BuildThis & g)
  446. {
  447. return 1;
  448. }
  449. float FuzzyHelper::evaluate (Goals::DigAtTile & g)
  450. {
  451. return 0;
  452. }
  453. float FuzzyHelper::evaluate (Goals::CollectRes & g)
  454. {
  455. return 0;
  456. }
  457. float FuzzyHelper::evaluate (Goals::Build & g)
  458. {
  459. return 0;
  460. }
  461. float FuzzyHelper::evaluate (Goals::Invalid & g)
  462. {
  463. return -1e10;
  464. }
  465. float FuzzyHelper::evaluate (Goals::AbstractGoal & g)
  466. {
  467. logAi->warnStream() << boost::format("Cannot evaluate goal %s") % g.name();
  468. return g.priority;
  469. }
  470. void FuzzyHelper::setPriority (Goals::TSubgoal & g)
  471. {
  472. g->setpriority(g->accept(this)); //this enforces returned value is set
  473. }