AIUtility.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /*
  2. * AIUtility.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 "AIUtility.h"
  12. #include "VCAI.h"
  13. #include "Fuzzy.h"
  14. #include "../../lib/UnlockGuard.h"
  15. #include "../../lib/CConfigHandler.h"
  16. #include "../../lib/CHeroHandler.h"
  17. #include "../../lib/mapObjects/CBank.h"
  18. #include "../../lib/mapObjects/CGTownInstance.h"
  19. #include "../../lib/mapObjects/CQuest.h"
  20. #include "../../lib/CPathfinder.h"
  21. #include "../../lib/mapping/CMapDefines.h"
  22. extern boost::thread_specific_ptr<CCallback> cb;
  23. extern boost::thread_specific_ptr<VCAI> ai;
  24. extern FuzzyHelper * fh;
  25. //extern static const int3 dirs[8];
  26. const CGObjectInstance * ObjectIdRef::operator->() const
  27. {
  28. return cb->getObj(id, false);
  29. }
  30. ObjectIdRef::operator const CGObjectInstance *() const
  31. {
  32. return cb->getObj(id, false);
  33. }
  34. ObjectIdRef::ObjectIdRef(ObjectInstanceID _id)
  35. : id(_id)
  36. {
  37. }
  38. ObjectIdRef::ObjectIdRef(const CGObjectInstance * obj)
  39. : id(obj->id)
  40. {
  41. }
  42. bool ObjectIdRef::operator<(const ObjectIdRef & rhs) const
  43. {
  44. return id < rhs.id;
  45. }
  46. HeroPtr::HeroPtr(const CGHeroInstance * H)
  47. {
  48. if(!H)
  49. {
  50. //init from nullptr should equal to default init
  51. *this = HeroPtr();
  52. return;
  53. }
  54. h = H;
  55. name = h->name;
  56. hid = H->id;
  57. // infosCount[ai->playerID][hid]++;
  58. }
  59. HeroPtr::HeroPtr()
  60. {
  61. h = nullptr;
  62. hid = ObjectInstanceID();
  63. }
  64. HeroPtr::~HeroPtr()
  65. {
  66. // if(hid >= 0)
  67. // infosCount[ai->playerID][hid]--;
  68. }
  69. bool HeroPtr::operator<(const HeroPtr & rhs) const
  70. {
  71. return hid < rhs.hid;
  72. }
  73. const CGHeroInstance * HeroPtr::get(bool doWeExpectNull) const
  74. {
  75. //TODO? check if these all assertions every time we get info about hero affect efficiency
  76. //
  77. //behave terribly when attempting unauthorized access to hero that is not ours (or was lost)
  78. assert(doWeExpectNull || h);
  79. if(h)
  80. {
  81. auto obj = cb->getObj(hid);
  82. const bool owned = obj && obj->tempOwner == ai->playerID;
  83. if(doWeExpectNull && !owned)
  84. {
  85. return nullptr;
  86. }
  87. else
  88. {
  89. assert(obj);
  90. assert(owned);
  91. }
  92. }
  93. return h;
  94. }
  95. const CGHeroInstance * HeroPtr::operator->() const
  96. {
  97. return get();
  98. }
  99. bool HeroPtr::validAndSet() const
  100. {
  101. return get(true);
  102. }
  103. const CGHeroInstance * HeroPtr::operator*() const
  104. {
  105. return get();
  106. }
  107. void foreach_tile_pos(std::function<void(const int3 &pos)> foo)
  108. {
  109. // some micro-optimizations since this function gets called a LOT
  110. // callback pointer is thread-specific and slow to retrieve -> read map size only once
  111. int3 mapSize = cb->getMapSize();
  112. for(int i = 0; i < mapSize.x; i++)
  113. for(int j = 0; j < mapSize.y; j++)
  114. for(int k = 0; k < mapSize.z; k++)
  115. foo(int3(i, j, k));
  116. }
  117. void foreach_tile_pos(CCallback * cbp, std::function<void(CCallback * cbp, const int3 &pos)> foo)
  118. {
  119. int3 mapSize = cbp->getMapSize();
  120. for(int i = 0; i < mapSize.x; i++)
  121. for(int j = 0; j < mapSize.y; j++)
  122. for(int k = 0; k < mapSize.z; k++)
  123. foo(cbp, int3(i, j, k));
  124. }
  125. void foreach_neighbour(const int3 & pos, std::function<void(const int3 &pos)> foo)
  126. {
  127. CCallback * cbp = cb.get(); // avoid costly retrieval of thread-specific pointer
  128. for(const int3 & dir : int3::getDirs())
  129. {
  130. const int3 n = pos + dir;
  131. if(cbp->isInTheMap(n))
  132. foo(pos + dir);
  133. }
  134. }
  135. void foreach_neighbour(CCallback * cbp, const int3 & pos, std::function<void(CCallback * cbp, const int3 &pos)> foo)
  136. {
  137. for(const int3 & dir : int3::getDirs())
  138. {
  139. const int3 n = pos + dir;
  140. if(cbp->isInTheMap(n))
  141. foo(cbp, pos + dir);
  142. }
  143. }
  144. std::string strFromInt3(int3 pos)
  145. {
  146. std::ostringstream oss;
  147. oss << pos;
  148. return oss.str();
  149. }
  150. bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs)
  151. {
  152. const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos()),
  153. * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos());
  154. if(ln->turns != rn->turns)
  155. return ln->turns < rn->turns;
  156. return (ln->moveRemains > rn->moveRemains);
  157. }
  158. bool compareMovement(HeroPtr lhs, HeroPtr rhs)
  159. {
  160. return lhs->movement > rhs->movement;
  161. }
  162. ui64 evaluateDanger(crint3 tile)
  163. {
  164. const TerrainTile * t = cb->getTile(tile, false);
  165. if(!t) //we can know about guard but can't check its tile (the edge of fow)
  166. return 190000000; //MUCH
  167. ui64 objectDanger = 0, guardDanger = 0;
  168. auto visObjs = cb->getVisitableObjs(tile);
  169. if(visObjs.size())
  170. objectDanger = evaluateDanger(visObjs.back());
  171. int3 guardPos = cb->getGuardingCreaturePosition(tile);
  172. if(guardPos.x >= 0 && guardPos != tile)
  173. guardDanger = evaluateDanger(guardPos);
  174. //TODO mozna odwiedzic blockvis nie ruszajac straznika
  175. return std::max(objectDanger, guardDanger);
  176. }
  177. ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor)
  178. {
  179. const TerrainTile * t = cb->getTile(tile, false);
  180. if(!t) //we can know about guard but can't check its tile (the edge of fow)
  181. return 190000000; //MUCH
  182. ui64 objectDanger = 0, guardDanger = 0;
  183. auto visitableObjects = cb->getVisitableObjs(tile);
  184. // in some scenarios hero happens to be "under" the object (eg town). Then we consider ONLY the hero.
  185. if(vstd::contains_if(visitableObjects, objWithID<Obj::HERO>))
  186. vstd::erase_if(visitableObjects, [](const CGObjectInstance * obj)
  187. {
  188. return !objWithID<Obj::HERO>(obj);
  189. });
  190. if(const CGObjectInstance * dangerousObject = vstd::backOrNull(visitableObjects))
  191. {
  192. objectDanger = evaluateDanger(dangerousObject); //unguarded objects can also be dangerous or unhandled
  193. if(objectDanger)
  194. {
  195. //TODO: don't downcast objects AI shouldn't know about!
  196. auto armedObj = dynamic_cast<const CArmedInstance *>(dangerousObject);
  197. if(armedObj)
  198. {
  199. float tacticalAdvantage = fh->getTacticalAdvantage(visitor, armedObj);
  200. objectDanger *= tacticalAdvantage; //this line tends to go infinite for allied towns (?)
  201. }
  202. }
  203. if(dangerousObject->ID == Obj::SUBTERRANEAN_GATE)
  204. { //check guard on the other side of the gate
  205. auto it = ai->knownSubterraneanGates.find(dangerousObject);
  206. if(it != ai->knownSubterraneanGates.end())
  207. {
  208. auto guards = cb->getGuardingCreatures(it->second->visitablePos());
  209. for(auto cre : guards)
  210. {
  211. vstd::amax(guardDanger, evaluateDanger(cre) *
  212. fh->getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre)));
  213. }
  214. }
  215. }
  216. }
  217. auto guards = cb->getGuardingCreatures(tile);
  218. for(auto cre : guards)
  219. {
  220. vstd::amax(guardDanger, evaluateDanger(cre) * fh->getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre))); //we are interested in strongest monster around
  221. }
  222. //TODO mozna odwiedzic blockvis nie ruszajac straznika
  223. return std::max(objectDanger, guardDanger);
  224. }
  225. ui64 evaluateDanger(const CGObjectInstance * obj)
  226. {
  227. if(obj->tempOwner < PlayerColor::PLAYER_LIMIT && cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES) //owned or allied objects don't pose any threat
  228. return 0;
  229. switch(obj->ID)
  230. {
  231. case Obj::HERO:
  232. {
  233. InfoAboutHero iah;
  234. cb->getHeroInfo(obj, iah);
  235. return iah.army.getStrength();
  236. }
  237. case Obj::TOWN:
  238. case Obj::GARRISON:
  239. case Obj::GARRISON2: //garrison
  240. {
  241. InfoAboutTown iat;
  242. cb->getTownInfo(obj, iat);
  243. return iat.army.getStrength();
  244. }
  245. case Obj::MONSTER:
  246. {
  247. //TODO!!!!!!!!
  248. const CGCreature * cre = dynamic_cast<const CGCreature *>(obj);
  249. return cre->getArmyStrength();
  250. }
  251. case Obj::CREATURE_GENERATOR1:
  252. case Obj::CREATURE_GENERATOR4:
  253. {
  254. const CGDwelling * d = dynamic_cast<const CGDwelling *>(obj);
  255. return d->getArmyStrength();
  256. }
  257. case Obj::MINE:
  258. case Obj::ABANDONED_MINE:
  259. {
  260. const CArmedInstance * a = dynamic_cast<const CArmedInstance *>(obj);
  261. return a->getArmyStrength();
  262. }
  263. case Obj::CRYPT: //crypt
  264. case Obj::CREATURE_BANK: //crebank
  265. case Obj::DRAGON_UTOPIA:
  266. case Obj::SHIPWRECK: //shipwreck
  267. case Obj::DERELICT_SHIP: //derelict ship
  268. // case Obj::PYRAMID:
  269. return fh->estimateBankDanger(dynamic_cast<const CBank *>(obj));
  270. case Obj::PYRAMID:
  271. {
  272. if(obj->subID == 0)
  273. return fh->estimateBankDanger(dynamic_cast<const CBank *>(obj));
  274. else
  275. return 0;
  276. }
  277. default:
  278. return 0;
  279. }
  280. }
  281. bool compareDanger(const CGObjectInstance * lhs, const CGObjectInstance * rhs)
  282. {
  283. return evaluateDanger(lhs) < evaluateDanger(rhs);
  284. }
  285. bool isSafeToVisit(HeroPtr h, crint3 tile)
  286. {
  287. const ui64 heroStrength = h->getTotalStrength(),
  288. dangerStrength = evaluateDanger(tile, *h);
  289. if(dangerStrength)
  290. {
  291. if(heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength)
  292. {
  293. logAi->trace("It's safe for %s to visit tile %s", h->name, tile());
  294. return true;
  295. }
  296. else
  297. return false;
  298. }
  299. return true; //there's no danger
  300. }
  301. bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
  302. {
  303. //tile must be free of with unoccupied boat
  304. return !t->blocked
  305. || (!fromWater && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT);
  306. //do not try to board when in water sector
  307. }
  308. int3 whereToExplore(HeroPtr h)
  309. {
  310. TimeCheck tc("where to explore");
  311. int radius = h->getSightRadius();
  312. int3 hpos = h->visitablePos();
  313. auto sm = ai->getCachedSectorMap(h);
  314. //look for nearby objs -> visit them if they're close enouh
  315. const int DIST_LIMIT = 3;
  316. std::vector<const CGObjectInstance *> nearbyVisitableObjs;
  317. for(int x = hpos.x - DIST_LIMIT; x <= hpos.x + DIST_LIMIT; ++x) //get only local objects instead of all possible objects on the map
  318. {
  319. for(int y = hpos.y - DIST_LIMIT; y <= hpos.y + DIST_LIMIT; ++y)
  320. {
  321. for(auto obj : cb->getVisitableObjs(int3(x, y, hpos.z), false))
  322. {
  323. int3 op = obj->visitablePos();
  324. CGPath p;
  325. ai->myCb->getPathsInfo(h.get())->getPath(p, op);
  326. if(p.nodes.size() && p.endPos() == op && p.nodes.size() <= DIST_LIMIT)
  327. if(ai->isGoodForVisit(obj, h, *sm))
  328. nearbyVisitableObjs.push_back(obj);
  329. }
  330. }
  331. }
  332. vstd::removeDuplicates(nearbyVisitableObjs); //one object may occupy multiple tiles
  333. boost::sort(nearbyVisitableObjs, CDistanceSorter(h.get()));
  334. if(nearbyVisitableObjs.size())
  335. return nearbyVisitableObjs.back()->visitablePos();
  336. try //check if nearby tiles allow us to reveal anything - this is quick
  337. {
  338. return ai->explorationBestNeighbour(hpos, radius, h);
  339. }
  340. catch(cannotFulfillGoalException & e)
  341. {
  342. //perform exhaustive search
  343. return ai->explorationNewPoint(h);
  344. }
  345. }
  346. bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should be handled by pathfinder
  347. {
  348. return cb->getTile(tileToHit)->topVisitableId() == Obj::BORDER_GATE &&
  349. (dynamic_cast<const CGKeys *>(cb->getTile(tileToHit)->visitableObjects.back()))->wasMyColorVisited(ai->playerID);
  350. }
  351. bool isBlockVisitObj(const int3 & pos)
  352. {
  353. if(auto obj = cb->getTopObj(pos))
  354. if(obj->blockVisit) //we can't stand on that object
  355. return true;
  356. return false;
  357. }
  358. int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp)
  359. { //TODO: do not explore dead-end boundaries
  360. int ret = 0;
  361. for(int x = pos.x - radious; x <= pos.x + radious; x++)
  362. {
  363. for(int y = pos.y - radious; y <= pos.y + radious; y++)
  364. {
  365. int3 npos = int3(x, y, pos.z);
  366. if(cbp->isInTheMap(npos) && pos.dist2d(npos) - 0.5 < radious && !cbp->isVisible(npos))
  367. {
  368. if(!boundaryBetweenTwoPoints(pos, npos, cbp))
  369. ret++;
  370. }
  371. }
  372. }
  373. return ret;
  374. }
  375. bool boundaryBetweenTwoPoints(int3 pos1, int3 pos2, CCallback * cbp) //determines if two points are separated by known barrier
  376. {
  377. int xMin = std::min(pos1.x, pos2.x);
  378. int xMax = std::max(pos1.x, pos2.x);
  379. int yMin = std::min(pos1.y, pos2.y);
  380. int yMax = std::max(pos1.y, pos2.y);
  381. for(int x = xMin; x <= xMax; ++x)
  382. {
  383. for(int y = yMin; y <= yMax; ++y)
  384. {
  385. int3 tile = int3(x, y, pos1.z); //use only on same level, ofc
  386. if(std::abs(pos1.dist2d(tile) - pos2.dist2d(tile)) < 1.5)
  387. {
  388. if(!(cbp->isVisible(tile) && cbp->getTile(tile)->blocked)) //if there's invisible or unblocked tile between, it's good
  389. return false;
  390. }
  391. }
  392. }
  393. return true; //if all are visible and blocked, we're at dead end
  394. }
  395. int howManyTilesWillBeDiscovered(int radious, int3 pos, crint3 dir)
  396. {
  397. return howManyTilesWillBeDiscovered(pos + dir, radious, cb.get());
  398. }
  399. void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out)
  400. {
  401. for(const int3 & tile : tiles)
  402. {
  403. foreach_neighbour(tile, [&](int3 neighbour)
  404. {
  405. if(cb->isVisible(neighbour))
  406. out.push_back(neighbour);
  407. });
  408. }
  409. }
  410. ui64 howManyReinforcementsCanGet(HeroPtr h, const CGTownInstance * t)
  411. {
  412. ui64 ret = 0;
  413. int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount();
  414. std::vector<const CStackInstance *> toMove;
  415. for(auto const slot : t->Slots())
  416. {
  417. //can be merged woth another stack?
  418. SlotID dst = h->getSlotFor(slot.second->getCreatureID());
  419. if(h->hasStackAtSlot(dst))
  420. ret += t->getPower(slot.first);
  421. else
  422. toMove.push_back(slot.second);
  423. }
  424. boost::sort(toMove, [](const CStackInstance * lhs, const CStackInstance * rhs)
  425. {
  426. return lhs->getPower() < rhs->getPower();
  427. });
  428. for(auto & stack : boost::adaptors::reverse(toMove))
  429. {
  430. if(freeHeroSlots)
  431. {
  432. ret += stack->getPower();
  433. freeHeroSlots--;
  434. }
  435. else
  436. break;
  437. }
  438. return ret;
  439. }
  440. bool compareHeroStrength(HeroPtr h1, HeroPtr h2)
  441. {
  442. return h1->getTotalStrength() < h2->getTotalStrength();
  443. }
  444. bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2)
  445. {
  446. return a1->getArmyStrength() < a2->getArmyStrength();
  447. }
  448. bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2)
  449. {
  450. auto art1 = a1->artType;
  451. auto art2 = a2->artType;
  452. if(art1->price == art2->price)
  453. return art1->valOfBonuses(Bonus::PRIMARY_SKILL) > art2->valOfBonuses(Bonus::PRIMARY_SKILL);
  454. else if(art1->price > art2->price)
  455. return true;
  456. else
  457. return false;
  458. }