Win.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /*
  2. * Win.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. TSubgoal Win::whatToDoToAchieve()
  26. {
  27. auto toBool = [=](const EventCondition &)
  28. {
  29. // TODO: proper implementation
  30. // Right now even already fulfilled goals will be included into generated list
  31. // Proper check should test if event condition is already fulfilled
  32. // Easiest way to do this is to call CGameState::checkForVictory but this function should not be
  33. // used on client side or in AI code
  34. return false;
  35. };
  36. std::vector<EventCondition> goals;
  37. for(const TriggeredEvent & event : cb->getMapHeader()->triggeredEvents)
  38. {
  39. //TODO: try to eliminate human player(s) using loss conditions that have isHuman element
  40. if(event.effect.type == EventEffect::VICTORY)
  41. {
  42. boost::range::copy(event.trigger.getFulfillmentCandidates(toBool), std::back_inserter(goals));
  43. }
  44. }
  45. //TODO: instead of returning first encountered goal AI should generate list of possible subgoals
  46. for(const EventCondition & goal : goals)
  47. {
  48. switch(goal.condition)
  49. {
  50. case EventCondition::HAVE_ARTIFACT:
  51. return sptr(GetArtOfType(goal.objectType));
  52. case EventCondition::DESTROY:
  53. {
  54. if(goal.object)
  55. {
  56. auto obj = cb->getObj(goal.object->id);
  57. if(obj)
  58. if(obj->getOwner() == ai->playerID) //we can't capture our own object
  59. return sptr(Conquer());
  60. return sptr(VisitObj(goal.object->id.getNum()));
  61. }
  62. else
  63. {
  64. // TODO: destroy all objects of type goal.objectType
  65. // This situation represents "kill all creatures" condition from H3
  66. break;
  67. }
  68. }
  69. case EventCondition::HAVE_BUILDING:
  70. {
  71. // TODO build other buildings apart from Grail
  72. // goal.objectType = buidingID to build
  73. // goal.object = optional, town in which building should be built
  74. // Represents "Improve town" condition from H3 (but unlike H3 it consists from 2 separate conditions)
  75. if(goal.objectType == BuildingID::GRAIL)
  76. {
  77. if(auto h = ai->getHeroWithGrail())
  78. {
  79. //hero is in a town that can host Grail
  80. if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, BuildingID::GRAIL))
  81. {
  82. const CGTownInstance * t = h->visitedTown;
  83. return sptr(BuildThis(BuildingID::GRAIL, t).setpriority(10));
  84. }
  85. else
  86. {
  87. auto towns = cb->getTownsInfo();
  88. towns.erase(boost::remove_if(towns,
  89. [](const CGTownInstance * t) -> bool
  90. {
  91. return vstd::contains(t->forbiddenBuildings, BuildingID::GRAIL);
  92. }),
  93. towns.end());
  94. boost::sort(towns, CDistanceSorter(h.get()));
  95. if(towns.size())
  96. {
  97. return sptr(VisitTile(towns.front()->visitablePos()).sethero(h));
  98. }
  99. }
  100. }
  101. double ratio = 0;
  102. // maybe make this check a bit more complex? For example:
  103. // 0.75 -> dig randomly within 3 tiles radius
  104. // 0.85 -> radius now 2 tiles
  105. // 0.95 -> 1 tile radius, position is fully known
  106. // AFAIK H3 AI does something like this
  107. int3 grailPos = cb->getGrailPos(&ratio);
  108. if(ratio > 0.99)
  109. {
  110. return sptr(DigAtTile(grailPos));
  111. } //TODO: use FIND_OBJ
  112. else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
  113. return sptr(VisitObj(obj->id.getNum()));
  114. else
  115. return sptr(Explore());
  116. }
  117. break;
  118. }
  119. case EventCondition::CONTROL:
  120. {
  121. if(goal.object)
  122. {
  123. auto objRelations = cb->getPlayerRelations(ai->playerID, goal.object->tempOwner);
  124. if(objRelations == PlayerRelations::ENEMIES)
  125. {
  126. return sptr(VisitObj(goal.object->id.getNum()));
  127. }
  128. else
  129. {
  130. // TODO: Defance
  131. break;
  132. }
  133. }
  134. else
  135. {
  136. //TODO: control all objects of type "goal.objectType"
  137. // Represents H3 condition "Flag all mines"
  138. break;
  139. }
  140. }
  141. case EventCondition::HAVE_RESOURCES:
  142. //TODO mines? piles? marketplace?
  143. //save?
  144. return sptr(CollectRes(static_cast<Res::ERes>(goal.objectType), goal.value));
  145. case EventCondition::HAVE_CREATURES:
  146. return sptr(GatherTroops(goal.objectType, goal.value));
  147. case EventCondition::TRANSPORT:
  148. {
  149. //TODO. merge with bring Grail to town? So AI will first dig grail, then transport it using this goal and builds it
  150. // Represents "transport artifact" condition:
  151. // goal.objectType = type of artifact
  152. // goal.object = destination-town where artifact should be transported
  153. break;
  154. }
  155. case EventCondition::STANDARD_WIN:
  156. return sptr(Conquer());
  157. // Conditions that likely don't need any implementation
  158. case EventCondition::DAYS_PASSED:
  159. break; // goal.value = number of days for condition to trigger
  160. case EventCondition::DAYS_WITHOUT_TOWN:
  161. break; // goal.value = number of days to trigger this
  162. case EventCondition::IS_HUMAN:
  163. break; // Should be only used in calculation of candidates (see toBool lambda)
  164. case EventCondition::CONST_VALUE:
  165. break;
  166. case EventCondition::HAVE_0:
  167. case EventCondition::HAVE_BUILDING_0:
  168. case EventCondition::DESTROY_0:
  169. //TODO: support new condition format
  170. return sptr(Conquer());
  171. default:
  172. assert(0);
  173. }
  174. }
  175. return sptr(Invalid());
  176. }