GatherArmy.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. * GatherArmy.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "Goals.h"
  12. #include "../VCAI.h"
  13. #include "../AIUtility.h"
  14. #include "../AIhelper.h"
  15. #include "../FuzzyHelper.h"
  16. #include "../ResourceManager.h"
  17. #include "../BuildingManager.h"
  18. #include "../../../lib/mapping/CMap.h" //for victory conditions
  19. #include "../../../lib/CPathfinder.h"
  20. #include "../../../lib/StringConstants.h"
  21. extern boost::thread_specific_ptr<CCallback> cb;
  22. extern boost::thread_specific_ptr<VCAI> ai;
  23. extern FuzzyHelper * fh;
  24. using namespace Goals;
  25. bool GatherArmy::operator==(const GatherArmy & other) const
  26. {
  27. return other.hero.h == hero.h || town == other.town;
  28. }
  29. std::string GatherArmy::completeMessage() const
  30. {
  31. return "Hero " + hero.get()->name + " gathered army of value " + boost::lexical_cast<std::string>(value);
  32. }
  33. TSubgoal GatherArmy::whatToDoToAchieve()
  34. {
  35. //TODO: find hero if none set
  36. assert(hero.h);
  37. return fh->chooseSolution(getAllPossibleSubgoals()); //find dwelling. use current hero to prevent him from doing nothing.
  38. }
  39. static const BuildingID unitsSource[] = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
  40. BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7};
  41. TGoalVec GatherArmy::getAllPossibleSubgoals()
  42. {
  43. //get all possible towns, heroes and dwellings we may use
  44. TGoalVec ret;
  45. if(!hero.validAndSet())
  46. {
  47. return ret;
  48. }
  49. //TODO: include evaluation of monsters gather in calculation
  50. for(auto t : cb->getTownsInfo())
  51. {
  52. auto pos = t->visitablePos();
  53. if(ai->isAccessibleForHero(pos, hero))
  54. {
  55. //grab army from town
  56. if(!t->visitingHero && howManyReinforcementsCanGet(hero.get(), t))
  57. {
  58. if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
  59. ret.push_back(sptr(VisitTile(pos).sethero(hero)));
  60. }
  61. //buy army in town
  62. if (!t->visitingHero || t->visitingHero == hero.get(true))
  63. {
  64. std::vector<int> values = {
  65. value,
  66. (int)howManyReinforcementsCanBuy(t->getUpperArmy(), t),
  67. (int)howManyReinforcementsCanBuy(hero.get(), t) };
  68. int val = *std::min_element(values.begin(), values.end());
  69. if (val)
  70. {
  71. auto goal = sptr(BuyArmy(t, val).sethero(hero));
  72. if(!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
  73. ret.push_back(goal);
  74. else
  75. logAi->debug("Can not buy army, because of ai->ah->containsObjective");
  76. }
  77. }
  78. //build dwelling
  79. //TODO: plan building over multiple turns?
  80. //auto bid = ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));
  81. //Do not use below code for now, rely on generic Build. Code below needs to know a lot of town/resource context to do more good than harm
  82. /*auto bid = ai->ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 1);
  83. if (bid.is_initialized())
  84. {
  85. auto goal = sptr(BuildThis(bid.get(), t).setpriority(priority));
  86. if (!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
  87. ret.push_back(goal);
  88. else
  89. logAi->debug("Can not build a structure, because of ai->ah->containsObjective");
  90. }*/
  91. }
  92. }
  93. auto otherHeroes = cb->getHeroesInfo();
  94. auto heroDummy = hero;
  95. vstd::erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h)
  96. {
  97. if(h == heroDummy.h)
  98. return true;
  99. else if(!ai->isAccessibleForHero(heroDummy->visitablePos(), h, true))
  100. return true;
  101. else if(!ai->canGetArmy(heroDummy.h, h)) //TODO: return actual aiValue
  102. return true;
  103. else if(ai->getGoal(h)->goalType == GATHER_ARMY)
  104. return true;
  105. else
  106. return false;
  107. });
  108. for(auto h : otherHeroes)
  109. {
  110. // Go to the other hero if we are faster
  111. if (!vstd::contains(ai->visitedHeroes[hero], h)
  112. && ai->isAccessibleForHero(h->visitablePos(), hero, true)) //visit only once each turn //FIXME: this is only bug workaround
  113. ret.push_back(sptr(VisitHero(h->id.getNum()).setisAbstract(true).sethero(hero)));
  114. // Let the other hero come to us
  115. if (!vstd::contains(ai->visitedHeroes[h], hero))
  116. ret.push_back(sptr(VisitHero(hero->id.getNum()).setisAbstract(true).sethero(h)));
  117. }
  118. std::vector<const CGObjectInstance *> objs;
  119. for(auto obj : ai->visitableObjs)
  120. {
  121. if(obj->ID == Obj::CREATURE_GENERATOR1)
  122. {
  123. auto relationToOwner = cb->getPlayerRelations(obj->getOwner(), ai->playerID);
  124. //Use flagged dwellings only when there are available creatures that we can afford
  125. if(relationToOwner == PlayerRelations::SAME_PLAYER)
  126. {
  127. auto dwelling = dynamic_cast<const CGDwelling *>(obj);
  128. ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero.get(), dwelling));
  129. if(val)
  130. {
  131. for(auto & creLevel : dwelling->creatures)
  132. {
  133. if(creLevel.first)
  134. {
  135. for(auto & creatureID : creLevel.second)
  136. {
  137. auto creature = VLC->creh->creatures[creatureID];
  138. if(ai->ah->freeResources().canAfford(creature->cost))
  139. objs.push_back(obj); //TODO: reserve resources?
  140. }
  141. }
  142. }
  143. }
  144. }
  145. }
  146. }
  147. for(auto h : cb->getHeroesInfo())
  148. {
  149. for(auto obj : objs)
  150. {
  151. //find safe dwelling
  152. if(ai->isGoodForVisit(obj, h))
  153. {
  154. vstd::concatenate(ret, ai->ah->howToVisitObj(h, obj));
  155. }
  156. }
  157. }
  158. if(ai->canRecruitAnyHero() && ai->ah->freeGold() > GameConstants::HERO_GOLD_COST) //this is not stupid in early phase of game
  159. {
  160. if(auto t = ai->findTownWithTavern())
  161. {
  162. for(auto h : cb->getAvailableHeroes(t)) //we assume that all towns have same set of heroes
  163. {
  164. if(h && h->getTotalStrength() > 500) //do not buy heroes with single creatures for GatherArmy
  165. {
  166. ret.push_back(sptr(RecruitHero()));
  167. break;
  168. }
  169. }
  170. }
  171. }
  172. if(ret.empty())
  173. {
  174. if(hero == ai->primaryHero())
  175. ret.push_back(sptr(Explore()));
  176. else
  177. throw cannotFulfillGoalException("No ways to gather army");
  178. }
  179. return ret;
  180. }