Fuzzy.cpp 9.0 KB

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