Fuzzy.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. #include "StdInc.h"
  2. #include "Fuzzy.h"
  3. #include <limits>
  4. //#include "../../lib/CObjectHandler.h"
  5. /*
  6. * Fuzzy.cpp, part of VCMI engine
  7. *
  8. * Authors: listed in file AUTHORS in main folder
  9. *
  10. * License: GNU General Public License v2.0 or later
  11. * Full text of license available in license.txt file, in main folder
  12. *
  13. */
  14. #define MIN_AI_STRENGHT (0.5f) //lower when combat AI gets smarter
  15. struct BankConfig;
  16. class FuzzyEngine;
  17. class InputLVar;
  18. class CGTownInstance;
  19. using namespace boost::assign;
  20. using namespace vstd;
  21. FuzzyHelper *fh;
  22. struct armyStructure
  23. {
  24. float walkers, shooters, flyers;
  25. ui32 maxSpeed;
  26. };
  27. ui64 evaluateBankConfig (BankConfig * bc)
  28. {
  29. ui64 danger = 0;
  30. BOOST_FOREACH (auto opt, bc->guards)
  31. {
  32. danger += VLC->creh->creatures[opt.first]->fightValue * opt.second;
  33. }
  34. return danger;
  35. }
  36. armyStructure evaluateArmyStructure (const CArmedInstance * army)
  37. {
  38. ui64 totalStrenght = army->getArmyStrength();
  39. double walkersStrenght = 0;
  40. double flyersStrenght = 0;
  41. double shootersStrenght = 0;
  42. ui32 maxSpeed = 0;
  43. BOOST_FOREACH(auto s, army->Slots())
  44. {
  45. bool walker = true;
  46. if (s.second->type->hasBonusOfType(Bonus::SHOOTER))
  47. {
  48. shootersStrenght += s.second->getPower();
  49. walker = false;
  50. }
  51. if (s.second->type->hasBonusOfType(Bonus::FLYING))
  52. {
  53. flyersStrenght += s.second->getPower();
  54. walker = false;
  55. }
  56. if (walker)
  57. walkersStrenght += s.second->getPower();
  58. amax (maxSpeed, s.second->type->speed);
  59. }
  60. armyStructure as;
  61. as.walkers = walkersStrenght / totalStrenght;
  62. as.shooters = shootersStrenght / totalStrenght;
  63. as.flyers = flyersStrenght / totalStrenght;
  64. as.maxSpeed = maxSpeed;
  65. return as;
  66. }
  67. FuzzyHelper::FuzzyHelper()
  68. {
  69. initBank();
  70. initTacticalAdvantage();
  71. }
  72. void FuzzyHelper::initBank()
  73. {
  74. try
  75. {
  76. //Trivial bank estimation
  77. bankInput = new fl::InputLVar("BankInput");
  78. bankDanger = new fl::OutputLVar("BankDanger");
  79. bankInput->addTerm(new fl::SingletonTerm ("SET"));
  80. engine.addRuleBlock (&bankBlock); //have to be added before the rules are parsed
  81. engine.addInputLVar (bankInput);
  82. engine.addOutputLVar (bankDanger);
  83. for (int i = 0; i < 4; ++i)
  84. {
  85. bankDanger->addTerm(new fl::TriangularTerm ("Bank" + boost::lexical_cast<std::string>(i), 0, 1));
  86. bankBlock.addRule(new fl::MamdaniRule("if BankInput is SET then BankDanger is Bank" + boost::lexical_cast<std::string>(i), engine));
  87. }
  88. }
  89. catch (fl::FuzzyException fe)
  90. {
  91. tlog1 << "initBank " << fe.name() << ": " << fe.message() << '\n';
  92. }
  93. }
  94. void FuzzyHelper::initTacticalAdvantage()
  95. {
  96. try
  97. {
  98. //Tactical advantage calculation
  99. std::vector<fl::InputLVar*> helper;
  100. ourShooters = new fl::InputLVar("OurShooters");
  101. ourWalkers = new fl::InputLVar("OurWalkers");
  102. ourFlyers = new fl::InputLVar("OurFlyers");
  103. enemyShooters = new fl::InputLVar("EnemyShooters");
  104. enemyWalkers = new fl::InputLVar("EnemyWalkers");
  105. enemyFlyers = new fl::InputLVar("EnemyFlyers");
  106. helper += ourShooters, ourWalkers, ourFlyers, enemyShooters, enemyWalkers, enemyFlyers;
  107. BOOST_FOREACH (auto val, helper)
  108. {
  109. val->addTerm (new fl::ShoulderTerm("FEW", 0, 0.75, true));
  110. val->addTerm (new fl::ShoulderTerm("MANY", 0.25, 1, false));
  111. engine.addInputLVar(val);
  112. }
  113. helper.clear();
  114. ourSpeed = new fl::InputLVar("OurSpeed");
  115. enemySpeed = new fl::InputLVar("EnemySpeed");
  116. helper += ourSpeed, enemySpeed;
  117. BOOST_FOREACH (auto val, helper)
  118. {
  119. val->addTerm (new fl::ShoulderTerm("LOW", 3, 8.1, true));
  120. val->addTerm (new fl::TriangularTerm("MEDIUM", 6.9, 13.1));
  121. val->addTerm (new fl::ShoulderTerm("HIGH", 10.5, 16, false));
  122. engine.addInputLVar(val);
  123. }
  124. castleWalls = new fl::InputLVar("CastleWalls");
  125. castleWalls->addTerm(new fl::SingletonTerm("NONE", CGTownInstance::NONE));
  126. castleWalls->addTerm(new fl::TrapezoidalTerm("MEDIUM", CGTownInstance::FORT, 2.5));
  127. castleWalls->addTerm(new fl::ShoulderTerm("HIGH", CGTownInstance::CITADEL - 0.1, CGTownInstance::CASTLE));
  128. engine.addInputLVar(castleWalls);
  129. bankPresent = new fl::InputLVar("Bank");
  130. bankPresent->addTerm(new fl::SingletonTerm("FALSE", 0));
  131. bankPresent->addTerm(new fl::SingletonTerm("TRUE", 1));
  132. engine.addInputLVar(bankPresent);
  133. threat = new fl::OutputLVar("Threat");
  134. threat->addTerm (new fl::TriangularTerm("LOW", MIN_AI_STRENGHT, 1));
  135. threat->addTerm (new fl::TriangularTerm("MEDIUM", 0.8, 1.2));
  136. threat->addTerm (new fl::ShoulderTerm("HIGH", 1, 1.5, false));
  137. engine.addOutputLVar(threat);
  138. engine.hedgeSet().add(new fl::HedgeSomewhat());
  139. engine.hedgeSet().add(new fl::HedgeVery());
  140. tacticalAdvantage.addRule(new fl::MamdaniRule("if OurShooters is MANY and EnemySpeed is LOW then Threat is very LOW", engine));
  141. tacticalAdvantage.addRule(new fl::MamdaniRule("if OurSpeed is LOW and OurShooters is FEW and EnemyShooters is MANY then Threat is very HIGH", engine));
  142. tacticalAdvantage.addRule(new fl::MamdaniRule("if (OurShooters is MANY and OurFlyers is MANY) and EnemyShooters is MANY then Threat is LOW", engine));
  143. tacticalAdvantage.addRule(new fl::MamdaniRule("if OurShooters is MANY and EnemySpeed is HIGH then Threat is somewhat HIGH", engine));
  144. //tacticalAdvantage.addRule(new fl::MamdaniRule("if OurShooters is MANY and EnemyShooters is MANY then Threat is MEDIUM", engine));
  145. tacticalAdvantage.addRule(new fl::MamdaniRule("if Bank is TRUE and OurShooters is MANY then Threat is somewhat HIGH", engine));
  146. tacticalAdvantage.addRule(new fl::MamdaniRule("if Bank is TRUE and EnemyShooters is MANY then Threat is LOW", engine));
  147. tacticalAdvantage.addRule(new fl::MamdaniRule("if CastleWalls is HIGH and OurWalkers is MANY then Threat is very HIGH", engine));
  148. tacticalAdvantage.addRule(new fl::MamdaniRule("if CastleWalls is HIGH and OurFlyers is MANY and OurShooters is MANY then Threat is MEDIUM", engine));
  149. tacticalAdvantage.addRule(new fl::MamdaniRule("if CastleWalls is MEDIUM and OurShooters is MANY and EnemyWalkers is MANY then Threat is LOW", engine));
  150. engine.addRuleBlock (&tacticalAdvantage);
  151. }
  152. catch(fl::ParsingException pe)
  153. {
  154. tlog1 << "initTacticalAdvantage " << pe.name() << ": " << pe.message() << '\n';
  155. }
  156. catch (fl::FuzzyException fe)
  157. {
  158. tlog1 << "initTacticalAdvantage " << fe.name() << ": " << fe.message() << '\n';
  159. }
  160. }
  161. ui64 FuzzyHelper::estimateBankDanger (int ID)
  162. {
  163. std::vector <ConstTransitivePtr<BankConfig>> & configs = VLC->objh->banksInfo[ID];
  164. ui64 val = std::numeric_limits<ui64>::max();
  165. try
  166. {
  167. switch (configs.size())
  168. {
  169. case 4:
  170. try
  171. {
  172. int bankVal;
  173. for (int i = 0; i < 4; ++i)
  174. {
  175. bankVal = evaluateBankConfig (VLC->objh->banksInfo[ID][i]);
  176. bankDanger->term("Bank" + boost::lexical_cast<std::string>(i))->setMinimum(bankVal * 0.5f);
  177. bankDanger->term("Bank" + boost::lexical_cast<std::string>(i))->setMaximum(bankVal * 1.5f);
  178. }
  179. //comparison purposes
  180. //int averageValue = (evaluateBankConfig (VLC->objh->banksInfo[ID][0]) + evaluateBankConfig (VLC->objh->banksInfo[ID][3])) * 0.5;
  181. dynamic_cast<fl::SingletonTerm*>(bankInput->term("SET"))->setValue(0.5);
  182. bankInput->setInput (0.5);
  183. engine.process (BANK_DANGER);
  184. val = bankDanger->output().defuzzify(); //some expected value of this bank
  185. }
  186. catch (fl::FuzzyException fe)
  187. {
  188. tlog1 << fe.name() << ": " << fe.message() << '\n';
  189. }
  190. break;
  191. case 1: //rare case - Pyramid
  192. val = evaluateBankConfig (VLC->objh->banksInfo[ID][0]);
  193. break;
  194. default:
  195. tlog3 << ("Uhnandled bank config!\n");
  196. }
  197. }
  198. catch (fl::FuzzyException fe)
  199. {
  200. tlog1 << "estimateBankDanger " << fe.name() << ": " << fe.message() << '\n';
  201. }
  202. return val;
  203. }
  204. float FuzzyHelper::getTacticalAdvantage (const CArmedInstance *we, const CArmedInstance *enemy)
  205. {
  206. float output = 1;
  207. try
  208. {
  209. armyStructure ourStructure = evaluateArmyStructure(we);
  210. armyStructure enemyStructure = evaluateArmyStructure(enemy);
  211. ourWalkers->setInput(ourStructure.walkers);
  212. ourShooters->setInput(ourStructure.shooters);
  213. ourFlyers->setInput(ourStructure.flyers);
  214. ourSpeed->setInput(ourStructure.maxSpeed);
  215. enemyWalkers->setInput(enemyStructure.walkers);
  216. enemyShooters->setInput(enemyStructure.shooters);
  217. enemyFlyers->setInput(enemyStructure.flyers);
  218. enemySpeed->setInput(enemyStructure.maxSpeed);
  219. bool bank = dynamic_cast<const CBank*>(enemy);
  220. if (bank)
  221. bankPresent->setInput(1);
  222. else
  223. bankPresent->setInput(0);
  224. const CGTownInstance * fort = dynamic_cast<const CGTownInstance*>(enemy);
  225. if (fort)
  226. {
  227. castleWalls->setInput (fort->fortLevel());
  228. }
  229. else
  230. castleWalls->setInput(0);
  231. engine.process (TACTICAL_ADVANTAGE);
  232. output = threat->output().defuzzify();
  233. }
  234. catch (fl::FuzzyException fe)
  235. {
  236. tlog1 << "getTacticalAdvantage " << fe.name() << ": " << fe.message() << '\n';
  237. }
  238. return output;
  239. }