Fuzzy.cpp 21 KB

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