AIUtility.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  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. {
  114. for(int j = 0; j < mapSize.y; j++)
  115. {
  116. for(int k = 0; k < mapSize.z; k++)
  117. foo(int3(i, j, k));
  118. }
  119. }
  120. }
  121. void foreach_tile_pos(CCallback * cbp, std::function<void(CCallback * cbp, const int3 & pos)> foo)
  122. {
  123. int3 mapSize = cbp->getMapSize();
  124. for(int i = 0; i < mapSize.x; i++)
  125. {
  126. for(int j = 0; j < mapSize.y; j++)
  127. {
  128. for(int k = 0; k < mapSize.z; k++)
  129. foo(cbp, int3(i, j, k));
  130. }
  131. }
  132. }
  133. void foreach_neighbour(const int3 & pos, std::function<void(const int3 & pos)> foo)
  134. {
  135. CCallback * cbp = cb.get(); // avoid costly retrieval of thread-specific pointer
  136. for(const int3 & dir : int3::getDirs())
  137. {
  138. const int3 n = pos + dir;
  139. if(cbp->isInTheMap(n))
  140. foo(pos + dir);
  141. }
  142. }
  143. void foreach_neighbour(CCallback * cbp, const int3 & pos, std::function<void(CCallback * cbp, const int3 & pos)> foo)
  144. {
  145. for(const int3 & dir : int3::getDirs())
  146. {
  147. const int3 n = pos + dir;
  148. if(cbp->isInTheMap(n))
  149. foo(cbp, pos + dir);
  150. }
  151. }
  152. bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs)
  153. {
  154. const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos());
  155. const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos());
  156. if(ln->turns != rn->turns)
  157. return ln->turns < rn->turns;
  158. return (ln->moveRemains > rn->moveRemains);
  159. }
  160. bool compareMovement(HeroPtr lhs, HeroPtr rhs)
  161. {
  162. return lhs->movement > rhs->movement;
  163. }
  164. ui64 evaluateDanger(crint3 tile)
  165. {
  166. const TerrainTile * t = cb->getTile(tile, false);
  167. if(!t) //we can know about guard but can't check its tile (the edge of fow)
  168. return 190000000; //MUCH
  169. ui64 objectDanger = 0;
  170. ui64 guardDanger = 0;
  171. auto visObjs = cb->getVisitableObjs(tile);
  172. if(visObjs.size())
  173. objectDanger = evaluateDanger(visObjs.back());
  174. int3 guardPos = cb->getGuardingCreaturePosition(tile);
  175. if(guardPos.x >= 0 && guardPos != tile)
  176. guardDanger = evaluateDanger(guardPos);
  177. //TODO mozna odwiedzic blockvis nie ruszajac straznika
  178. return std::max(objectDanger, guardDanger);
  179. }
  180. ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor)
  181. {
  182. const TerrainTile * t = cb->getTile(tile, false);
  183. if(!t) //we can know about guard but can't check its tile (the edge of fow)
  184. return 190000000; //MUCH
  185. ui64 objectDanger = 0;
  186. ui64 guardDanger = 0;
  187. auto visitableObjects = cb->getVisitableObjs(tile);
  188. // in some scenarios hero happens to be "under" the object (eg town). Then we consider ONLY the hero.
  189. if(vstd::contains_if(visitableObjects, objWithID<Obj::HERO>))
  190. {
  191. vstd::erase_if(visitableObjects, [](const CGObjectInstance * obj)
  192. {
  193. return !objWithID<Obj::HERO>(obj);
  194. });
  195. }
  196. if(const CGObjectInstance * dangerousObject = vstd::backOrNull(visitableObjects))
  197. {
  198. objectDanger = evaluateDanger(dangerousObject); //unguarded objects can also be dangerous or unhandled
  199. if(objectDanger)
  200. {
  201. //TODO: don't downcast objects AI shouldn't know about!
  202. auto armedObj = dynamic_cast<const CArmedInstance *>(dangerousObject);
  203. if(armedObj)
  204. {
  205. float tacticalAdvantage = fh->getTacticalAdvantage(visitor, armedObj);
  206. objectDanger *= tacticalAdvantage; //this line tends to go infinite for allied towns (?)
  207. }
  208. }
  209. if(dangerousObject->ID == Obj::SUBTERRANEAN_GATE)
  210. {
  211. //check guard on the other side of the gate
  212. auto it = ai->knownSubterraneanGates.find(dangerousObject);
  213. if(it != ai->knownSubterraneanGates.end())
  214. {
  215. auto guards = cb->getGuardingCreatures(it->second->visitablePos());
  216. for(auto cre : guards)
  217. {
  218. vstd::amax(guardDanger, evaluateDanger(cre) * fh->getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre)));
  219. }
  220. }
  221. }
  222. }
  223. auto guards = cb->getGuardingCreatures(tile);
  224. for(auto cre : guards)
  225. {
  226. vstd::amax(guardDanger, evaluateDanger(cre) * fh->getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre))); //we are interested in strongest monster around
  227. }
  228. //TODO mozna odwiedzic blockvis nie ruszajac straznika
  229. return std::max(objectDanger, guardDanger);
  230. }
  231. ui64 evaluateDanger(const CGObjectInstance * obj)
  232. {
  233. if(obj->tempOwner < PlayerColor::PLAYER_LIMIT && cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES) //owned or allied objects don't pose any threat
  234. return 0;
  235. switch(obj->ID)
  236. {
  237. case Obj::HERO:
  238. {
  239. InfoAboutHero iah;
  240. cb->getHeroInfo(obj, iah);
  241. return iah.army.getStrength();
  242. }
  243. case Obj::TOWN:
  244. case Obj::GARRISON:
  245. case Obj::GARRISON2:
  246. {
  247. InfoAboutTown iat;
  248. cb->getTownInfo(obj, iat);
  249. return iat.army.getStrength();
  250. }
  251. case Obj::MONSTER:
  252. {
  253. //TODO!!!!!!!!
  254. const CGCreature * cre = dynamic_cast<const CGCreature *>(obj);
  255. return cre->getArmyStrength();
  256. }
  257. case Obj::CREATURE_GENERATOR1:
  258. case Obj::CREATURE_GENERATOR4:
  259. {
  260. const CGDwelling * d = dynamic_cast<const CGDwelling *>(obj);
  261. return d->getArmyStrength();
  262. }
  263. case Obj::MINE:
  264. case Obj::ABANDONED_MINE:
  265. {
  266. const CArmedInstance * a = dynamic_cast<const CArmedInstance *>(obj);
  267. return a->getArmyStrength();
  268. }
  269. case Obj::CRYPT: //crypt
  270. case Obj::CREATURE_BANK: //crebank
  271. case Obj::DRAGON_UTOPIA:
  272. case Obj::SHIPWRECK: //shipwreck
  273. case Obj::DERELICT_SHIP: //derelict ship
  274. // case Obj::PYRAMID:
  275. return fh->estimateBankDanger(dynamic_cast<const CBank *>(obj));
  276. case Obj::PYRAMID:
  277. {
  278. if(obj->subID == 0)
  279. return fh->estimateBankDanger(dynamic_cast<const CBank *>(obj));
  280. else
  281. return 0;
  282. }
  283. default:
  284. return 0;
  285. }
  286. }
  287. bool compareDanger(const CGObjectInstance * lhs, const CGObjectInstance * rhs)
  288. {
  289. return evaluateDanger(lhs) < evaluateDanger(rhs);
  290. }
  291. bool isSafeToVisit(HeroPtr h, crint3 tile)
  292. {
  293. const ui64 heroStrength = h->getTotalStrength();
  294. const ui64 dangerStrength = evaluateDanger(tile, *h);
  295. if(dangerStrength)
  296. {
  297. if(heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength)
  298. {
  299. logAi->trace("It's safe for %s to visit tile %s", h->name, tile.toString());
  300. return true;
  301. }
  302. else
  303. {
  304. return false;
  305. }
  306. }
  307. return true; //there's no danger
  308. }
  309. bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
  310. {
  311. // TODO: Such information should be provided by pathfinder
  312. // Tile must be free or with unoccupied boat
  313. if(!t->blocked)
  314. {
  315. return true;
  316. }
  317. else if(!fromWater) // do not try to board when in water sector
  318. {
  319. if(t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
  320. return true;
  321. }
  322. return false;
  323. }
  324. int3 whereToExplore(HeroPtr h)
  325. {
  326. TimeCheck tc("where to explore");
  327. int radius = h->getSightRadius();
  328. int3 hpos = h->visitablePos();
  329. auto sm = ai->getCachedSectorMap(h);
  330. //look for nearby objs -> visit them if they're close enouh
  331. const int DIST_LIMIT = 3;
  332. std::vector<const CGObjectInstance *> nearbyVisitableObjs;
  333. 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
  334. {
  335. for(int y = hpos.y - DIST_LIMIT; y <= hpos.y + DIST_LIMIT; ++y)
  336. {
  337. for(auto obj : cb->getVisitableObjs(int3(x, y, hpos.z), false))
  338. {
  339. int3 op = obj->visitablePos();
  340. CGPath p;
  341. ai->myCb->getPathsInfo(h.get())->getPath(p, op);
  342. if(p.nodes.size() && p.endPos() == op && p.nodes.size() <= DIST_LIMIT)
  343. {
  344. if(ai->isGoodForVisit(obj, h, *sm))
  345. nearbyVisitableObjs.push_back(obj);
  346. }
  347. }
  348. }
  349. }
  350. vstd::removeDuplicates(nearbyVisitableObjs); //one object may occupy multiple tiles
  351. boost::sort(nearbyVisitableObjs, CDistanceSorter(h.get()));
  352. if(nearbyVisitableObjs.size())
  353. return nearbyVisitableObjs.back()->visitablePos();
  354. try //check if nearby tiles allow us to reveal anything - this is quick
  355. {
  356. return ai->explorationBestNeighbour(hpos, radius, h);
  357. }
  358. catch(cannotFulfillGoalException & e)
  359. {
  360. //perform exhaustive search
  361. return ai->explorationNewPoint(h);
  362. }
  363. }
  364. bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should be handled by pathfinder
  365. {
  366. if(cb->getTile(tileToHit)->topVisitableId() != Obj::BORDER_GATE)
  367. return false;
  368. auto gate = dynamic_cast<const CGKeys *>(cb->getTile(tileToHit)->topVisitableObj());
  369. return !gate->wasMyColorVisited(ai->playerID);
  370. }
  371. bool isBlockVisitObj(const int3 & pos)
  372. {
  373. if(auto obj = cb->getTopObj(pos))
  374. {
  375. if(obj->blockVisit) //we can't stand on that object
  376. return true;
  377. }
  378. return false;
  379. }
  380. int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp)
  381. {
  382. //TODO: do not explore dead-end boundaries
  383. int ret = 0;
  384. for(int x = pos.x - radious; x <= pos.x + radious; x++)
  385. {
  386. for(int y = pos.y - radious; y <= pos.y + radious; y++)
  387. {
  388. int3 npos = int3(x, y, pos.z);
  389. if(cbp->isInTheMap(npos) && pos.dist2d(npos) - 0.5 < radious && !cbp->isVisible(npos))
  390. {
  391. if(!boundaryBetweenTwoPoints(pos, npos, cbp))
  392. ret++;
  393. }
  394. }
  395. }
  396. return ret;
  397. }
  398. bool boundaryBetweenTwoPoints(int3 pos1, int3 pos2, CCallback * cbp) //determines if two points are separated by known barrier
  399. {
  400. int xMin = std::min(pos1.x, pos2.x);
  401. int xMax = std::max(pos1.x, pos2.x);
  402. int yMin = std::min(pos1.y, pos2.y);
  403. int yMax = std::max(pos1.y, pos2.y);
  404. for(int x = xMin; x <= xMax; ++x)
  405. {
  406. for(int y = yMin; y <= yMax; ++y)
  407. {
  408. int3 tile = int3(x, y, pos1.z); //use only on same level, ofc
  409. if(std::abs(pos1.dist2d(tile) - pos2.dist2d(tile)) < 1.5)
  410. {
  411. if(!(cbp->isVisible(tile) && cbp->getTile(tile)->blocked)) //if there's invisible or unblocked tile between, it's good
  412. return false;
  413. }
  414. }
  415. }
  416. return true; //if all are visible and blocked, we're at dead end
  417. }
  418. int howManyTilesWillBeDiscovered(int radious, int3 pos, crint3 dir)
  419. {
  420. return howManyTilesWillBeDiscovered(pos + dir, radious, cb.get());
  421. }
  422. void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out)
  423. {
  424. for(const int3 & tile : tiles)
  425. {
  426. foreach_neighbour(tile, [&](int3 neighbour)
  427. {
  428. if(cb->isVisible(neighbour))
  429. out.push_back(neighbour);
  430. });
  431. }
  432. }
  433. ui64 howManyReinforcementsCanGet(HeroPtr h, const CGTownInstance * t)
  434. {
  435. ui64 ret = 0;
  436. int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount();
  437. std::vector<const CStackInstance *> toMove;
  438. for(auto const slot : t->Slots())
  439. {
  440. //can be merged woth another stack?
  441. SlotID dst = h->getSlotFor(slot.second->getCreatureID());
  442. if(h->hasStackAtSlot(dst))
  443. ret += t->getPower(slot.first);
  444. else
  445. toMove.push_back(slot.second);
  446. }
  447. boost::sort(toMove, [](const CStackInstance * lhs, const CStackInstance * rhs)
  448. {
  449. return lhs->getPower() < rhs->getPower();
  450. });
  451. for(auto & stack : boost::adaptors::reverse(toMove))
  452. {
  453. if(freeHeroSlots)
  454. {
  455. ret += stack->getPower();
  456. freeHeroSlots--;
  457. }
  458. else
  459. break;
  460. }
  461. return ret;
  462. }
  463. bool compareHeroStrength(HeroPtr h1, HeroPtr h2)
  464. {
  465. return h1->getTotalStrength() < h2->getTotalStrength();
  466. }
  467. bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2)
  468. {
  469. return a1->getArmyStrength() < a2->getArmyStrength();
  470. }
  471. bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2)
  472. {
  473. auto art1 = a1->artType;
  474. auto art2 = a2->artType;
  475. if(art1->price == art2->price)
  476. return art1->valOfBonuses(Bonus::PRIMARY_SKILL) > art2->valOfBonuses(Bonus::PRIMARY_SKILL);
  477. else if(art1->price > art2->price)
  478. return true;
  479. else
  480. return false;
  481. }