CGeniusAI.cpp 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103
  1. #include "CGeniusAI.h"
  2. #include <iostream>
  3. #include <fstream>
  4. #include <math.h>
  5. #include <boost/lexical_cast.hpp>
  6. #include <boost/lambda/lambda.hpp>
  7. #include <boost/lambda/bind.hpp>
  8. #include <boost/lambda/if.hpp>
  9. #include <boost/algorithm/string/split.hpp>
  10. #include <boost/algorithm/string/classification.hpp>
  11. #include <boost/algorithm/string.hpp>
  12. using namespace boost::lambda;
  13. using namespace std;
  14. using namespace GeniusAI;
  15. #if defined (_MSC_VER) && (_MSC_VER >= 1020) || (__MINGW32__)
  16. #include <windows.h>
  17. #endif
  18. void MsgBox(const char *msg, bool messageBox = false)
  19. {
  20. #if defined _DEBUG
  21. # if defined (_MSC_VER) && (_MSC_VER >= 1020)
  22. if (messageBox)
  23. {
  24. MessageBoxA(NULL, msg, "Debug message", MB_OK | MB_ICONASTERISK);
  25. }
  26. # endif
  27. std::cout << msg << std::endl;
  28. #endif
  29. }
  30. void CGeniusAI::init(ICallback *CB)
  31. {
  32. m_cb = CB;
  33. human = false;
  34. playerID = m_cb->getMyColor();
  35. serialID = m_cb->getMySerial();
  36. std::string info = std::string("GeniusAI initialized for player ") + boost::lexical_cast<std::string>(playerID);
  37. m_battleLogic = NULL;
  38. MsgBox(info.c_str());
  39. }
  40. void CGeniusAI::yourTurn()
  41. {
  42. m_cb->endTurn();
  43. }
  44. void CGeniusAI::heroKilled(const CGHeroInstance *)
  45. {
  46. }
  47. void CGeniusAI::heroCreated(const CGHeroInstance *)
  48. {
  49. }
  50. void CGeniusAI::heroMoved(const HeroMoveDetails &)
  51. {
  52. }
  53. void CGeniusAI::heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector<ui16> &skills, boost::function<void(ui32)> &callback)
  54. {
  55. callback(rand() % skills.size());
  56. }
  57. void GeniusAI::CGeniusAI::showGarrisonDialog( const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd )
  58. {
  59. onEnd();
  60. }
  61. void CGeniusAI::showBlockingDialog( const std::string &text, const std::vector<Component> &components, ui32 askID, int soundID, bool selection, bool cancel )
  62. {
  63. m_cb->selectionMade(cancel ? 0 : 1, askID);
  64. }
  65. /**
  66. * occurs AFTER every action taken by any stack or by the hero
  67. */
  68. void CGeniusAI::actionFinished(const BattleAction *action)
  69. {
  70. std::string message("\t\tCGeniusAI::actionFinished - type(");
  71. message += boost::lexical_cast<std::string>((unsigned)action->actionType);
  72. message += "), side(";
  73. message += boost::lexical_cast<std::string>((unsigned)action->side);
  74. message += ")";
  75. MsgBox(message.c_str());
  76. }
  77. /**
  78. * occurs BEFORE every action taken by any stack or by the hero
  79. */
  80. void CGeniusAI::actionStarted(const BattleAction *action)
  81. {
  82. std::string message("\t\tCGeniusAI::actionStarted - type(");
  83. message += boost::lexical_cast<std::string>((unsigned)action->actionType);
  84. message += "), side(";
  85. message += boost::lexical_cast<std::string>((unsigned)action->side);
  86. message += ")";
  87. MsgBox(message.c_str());
  88. }
  89. /**
  90. * called when stack is performing attack
  91. */
  92. void CGeniusAI::battleAttack(BattleAttack *ba)
  93. {
  94. MsgBox("\t\t\tCGeniusAI::battleAttack");
  95. }
  96. /**
  97. * called when stack receives damage (after battleAttack())
  98. */
  99. void CGeniusAI::battleStacksAttacked(std::set<BattleStackAttacked> & bsa)
  100. {
  101. MsgBox("\t\t\tCGeniusAI::battleStacksAttacked");
  102. }
  103. /**
  104. * called by engine when battle starts; side=0 - left, side=1 - right
  105. */
  106. void CGeniusAI::battleStart(CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side)
  107. {
  108. assert(!m_battleLogic);
  109. m_battleLogic = new CBattleLogic(m_cb, army1, army2, tile, hero1, hero2, side);
  110. assert(m_battleLogic);
  111. MsgBox("** CGeniusAI::battleStart **");
  112. }
  113. /**
  114. *
  115. */
  116. void CGeniusAI::battleEnd(BattleResult *br)
  117. {
  118. delete m_battleLogic;
  119. m_battleLogic = NULL;
  120. MsgBox("** CGeniusAI::battleEnd **");
  121. }
  122. /**
  123. * called at the beggining of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
  124. */
  125. void CGeniusAI::battleNewRound(int round)
  126. {
  127. std::string message("\tCGeniusAI::battleNewRound - ");
  128. message += boost::lexical_cast<std::string>(round);
  129. MsgBox(message.c_str());
  130. m_battleLogic->SetCurrentTurn(round);
  131. }
  132. /**
  133. *
  134. */
  135. void CGeniusAI::battleStackMoved(int ID, int dest, int distance, bool end)
  136. {
  137. std::string message("\t\t\tCGeniusAI::battleStackMoved ID(");
  138. message += boost::lexical_cast<std::string>(ID);
  139. message += "), dest(";
  140. message += boost::lexical_cast<std::string>(dest);
  141. message += ")";
  142. MsgBox(message.c_str());
  143. }
  144. /**
  145. *
  146. */
  147. void CGeniusAI::battleSpellCasted(SpellCasted *sc)
  148. {
  149. MsgBox("\t\t\tCGeniusAI::battleSpellCasted");
  150. }
  151. /**
  152. * called when battlefield is prepared, prior the battle beginning
  153. */
  154. void CGeniusAI::battlefieldPrepared(int battlefieldType, std::vector<CObstacle*> obstacles)
  155. {
  156. MsgBox("CGeniusAI::battlefieldPrepared");
  157. }
  158. /**
  159. *
  160. */
  161. void CGeniusAI::battleStackMoved(int ID, int dest, bool startMoving, bool endMoving)
  162. {
  163. MsgBox("\t\t\tCGeniusAI::battleStackMoved");
  164. }
  165. /**
  166. *
  167. */
  168. void CGeniusAI::battleStackAttacking(int ID, int dest)
  169. {
  170. MsgBox("\t\t\tCGeniusAI::battleStackAttacking");
  171. }
  172. /**
  173. *
  174. */
  175. void CGeniusAI::battleStackIsAttacked(int ID, int dmg, int killed, int IDby, bool byShooting)
  176. {
  177. MsgBox("\t\t\tCGeniusAI::battleStackIsAttacked");
  178. }
  179. /**
  180. * called when it's turn of that stack
  181. */
  182. BattleAction CGeniusAI::activeStack(int stackID)
  183. {
  184. std::string message("\t\t\tCGeniusAI::activeStack stackID(");
  185. message += boost::lexical_cast<std::string>(stackID);
  186. message += ")";
  187. MsgBox(message.c_str());
  188. return m_battleLogic->MakeDecision(stackID);
  189. };
  190. /*
  191. ui8 side; //who made this action: false - left, true - right player
  192. ui32 stackNumber;//stack ID, -1 left hero, -2 right hero,
  193. ui8 actionType; //
  194. 0 = Cancel BattleAction
  195. 1 = Hero cast a spell
  196. 2 = Walk
  197. 3 = Defend
  198. 4 = Retreat from the battle
  199. 5 = Surrender
  200. 6 = Walk and Attack
  201. 7 = Shoot
  202. 8 = Wait
  203. 9 = Catapult
  204. 10 = Monster casts a spell (i.e. Faerie Dragons)
  205. ui16 destinationTile;
  206. si32 additionalInfo; // e.g. spell number if type is 1 || 10; tile to attack if type is 6
  207. */
  208. /**
  209. * Implementation of CBattleHelper class.
  210. */
  211. CBattleHelper::CBattleHelper():
  212. InfiniteDistance(0xffff),
  213. BattlefieldWidth(15),
  214. BattlefieldHeight(11),
  215. m_voteForMaxDamage(10),
  216. m_voteForMinDamage(10),
  217. m_voteForMaxSpeed(10),
  218. m_voteForDistance(10),
  219. m_voteForDistanceFromShooters(20),
  220. m_voteForHitPoints(10)
  221. {
  222. // loads votes
  223. std::fstream f;
  224. f.open("AI\\CBattleHelper.txt", std::ios::in);
  225. if (f)
  226. {
  227. //char c_line[512];
  228. std::string line;
  229. while (std::getline(f, line, '\n'))
  230. {
  231. //f.getline(c_line, sizeof(c_line), '\n');
  232. //std::string line(c_line);
  233. //std::getline(f, line);
  234. std::vector<std::string> parts;
  235. boost::algorithm::split(parts, line, boost::algorithm::is_any_of("="));
  236. if (parts.size() >= 2)
  237. {
  238. boost::algorithm::trim(parts[0]);
  239. boost::algorithm::trim(parts[1]);
  240. if (parts[0].compare("m_voteForDistance") == 0)
  241. {
  242. try
  243. {
  244. m_voteForDistance = boost::lexical_cast<int>(parts[1]);
  245. }
  246. catch (boost::bad_lexical_cast &)
  247. {}
  248. }
  249. else if (parts[0].compare("m_voteForDistanceFromShooters") == 0)
  250. {
  251. try
  252. {
  253. m_voteForDistanceFromShooters = boost::lexical_cast<int>(parts[1]);
  254. }
  255. catch (boost::bad_lexical_cast &)
  256. {}
  257. }
  258. else if (parts[0].compare("m_voteForHitPoints") == 0)
  259. {
  260. try
  261. {
  262. m_voteForHitPoints = boost::lexical_cast<int>(parts[1]);
  263. }
  264. catch (boost::bad_lexical_cast &)
  265. {}
  266. }
  267. else if (parts[0].compare("m_voteForMaxDamage") == 0)
  268. {
  269. try
  270. {
  271. m_voteForMaxDamage = boost::lexical_cast<int>(parts[1]);
  272. }
  273. catch (boost::bad_lexical_cast &)
  274. {}
  275. }
  276. else if (parts[0].compare("m_voteForMaxSpeed") == 0)
  277. {
  278. try
  279. {
  280. m_voteForMaxSpeed = boost::lexical_cast<int>(parts[1]);
  281. }
  282. catch (boost::bad_lexical_cast &)
  283. {}
  284. }
  285. else if (parts[0].compare("m_voteForMinDamage") == 0)
  286. {
  287. try
  288. {
  289. m_voteForMinDamage = boost::lexical_cast<int>(parts[1]);
  290. }
  291. catch (boost::bad_lexical_cast &)
  292. {}
  293. }
  294. }
  295. }
  296. f.close();
  297. }
  298. }
  299. CBattleHelper::~CBattleHelper()
  300. {}
  301. int CBattleHelper::GetBattleFieldPosition(int x, int y)
  302. {
  303. return x + 17 * (y - 1);
  304. }
  305. int CBattleHelper::DecodeXPosition(int battleFieldPosition)
  306. {
  307. int pos = battleFieldPosition - (DecodeYPosition(battleFieldPosition) - 1) * 17;
  308. assert(pos > 0 && pos < 16);
  309. return pos;
  310. }
  311. int CBattleHelper::DecodeYPosition(int battleFieldPosition)
  312. {
  313. double y = (double)battleFieldPosition / 17.0;
  314. if (y - (int)y > 0.0)
  315. {
  316. return (int)y + 1;
  317. }
  318. assert((int)y > 0 && (int)y <= 11);
  319. return (int)y;
  320. }
  321. int CBattleHelper::GetShortestDistance(int pointA, int pointB)
  322. {
  323. /**
  324. * TODO: function hasn't been checked!
  325. */
  326. int x1 = DecodeXPosition(pointA);
  327. int y1 = DecodeYPosition(pointA);
  328. //
  329. int x2 = DecodeXPosition(pointB);
  330. //x2 += (x2 % 2)? 0 : 1;
  331. int y2 = DecodeYPosition(pointB);
  332. //
  333. double dx = x1 - x2;
  334. double dy = y1 - y2;
  335. return (int)sqrt(dx * dx + dy * dy);
  336. }
  337. int CBattleHelper::GetDistanceWithObstacles(int pointA, int pointB)
  338. {
  339. // TODO - implement this!
  340. return GetShortestDistance(pointA, pointB);
  341. }
  342. /**
  343. * Implementation of CBattleLogic class.
  344. */
  345. CBattleLogic::CBattleLogic(ICallback *cb, CCreatureSet *army1, CCreatureSet *army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, bool side) :
  346. m_iCurrentTurn(-2),
  347. m_bIsAttacker(false),
  348. m_cb(cb),
  349. m_army1(army1),
  350. m_army2(army2),
  351. m_tile(tile),
  352. m_hero1(hero1),
  353. m_hero2(hero2),
  354. m_side(side)
  355. {
  356. const int max_enemy_creatures = 12;
  357. m_statMaxDamage.reserve(max_enemy_creatures);
  358. m_statMinDamage.reserve(max_enemy_creatures);
  359. m_statMaxSpeed.reserve(max_enemy_creatures);
  360. m_statDistance.reserve(max_enemy_creatures);
  361. m_statDistanceFromShooters.reserve(max_enemy_creatures);
  362. m_statHitPoints.reserve(max_enemy_creatures);
  363. }
  364. CBattleLogic::~CBattleLogic()
  365. {}
  366. void CBattleLogic::SetCurrentTurn(int turn)
  367. {
  368. m_iCurrentTurn = turn;
  369. }
  370. void CBattleLogic::MakeStatistics(int currentCreatureId)
  371. {
  372. typedef std::map<int, CStack> map_stacks;
  373. map_stacks allStacks = m_cb->battleGetStacks();
  374. const CStack *currentStack = m_cb->battleGetStackByID(currentCreatureId);
  375. /*
  376. // find all creatures belong to the enemy
  377. std::for_each(allStacks.begin(), allStacks.end(),
  378. if_(bind<ui8>(&CStack::attackerOwned, bind<CStack>(&map_stacks::value_type::second, _1)) == m_bIsAttacker)
  379. [
  380. var(enemy)[ret<int>(bind<int>(&map_stacks::value_type::first, _1))] =
  381. ret<CStack>(bind<CStack>(&map_stacks::value_type::second, _1))
  382. ]
  383. );
  384. // fill other containers
  385. // max damage
  386. std::for_each(enemy.begin(), enemy.end(),
  387. var(m_statMaxDamage)[ret<int>(bind<int>(&map_stacks::value_type::first, _1))] =
  388. ret<int>(bind<int>(&CCreature::damageMax, bind<CCreature*>(&CStack::creature,
  389. bind<CStack>(&map_stacks::value_type::second, _1))))
  390. );
  391. // min damage
  392. std::for_each(enemy.begin(), enemy.end(),
  393. var(m_statMinDamage)[ret<int>(bind<int>(&map_stacks::value_type::first, _1))] =
  394. ret<int>(bind<int>(&CCreature::damageMax, bind<CCreature*>(&CStack::creature,
  395. bind<CStack>(&map_stacks::value_type::second, _1))))
  396. );
  397. */
  398. m_statMaxDamage.clear();
  399. m_statMinDamage.clear();
  400. m_statHitPoints.clear();
  401. m_statMaxSpeed.clear();
  402. m_statDistanceFromShooters.clear();
  403. m_statDistance.clear();
  404. m_statDistance.clear();
  405. m_statCasualties.clear();
  406. int totalEnemyDamage = 0;
  407. int totalEnemyHitPoints = 0;
  408. int totalDamage = 0;
  409. int totalHitPoints = 0;
  410. for (map_stacks::const_iterator it = allStacks.begin(); it != allStacks.end(); ++it)
  411. {
  412. const CStack *st = &it->second;
  413. if ((it->second.attackerOwned != 0) != m_bIsAttacker)
  414. {
  415. int id = it->first;
  416. if (st->amount < 1)
  417. {
  418. continue;
  419. }
  420. // make stats
  421. int hitPoints = st->amount * st->creature->hitPoints - (st->creature->hitPoints - st->firstHPleft);
  422. m_statMaxDamage.push_back(std::pair<int, int>(id, st->creature->damageMax * st->amount));
  423. m_statMinDamage.push_back(std::pair<int, int>(id, st->creature->damageMin * st->amount));
  424. m_statHitPoints.push_back(std::pair<int, int>(id, hitPoints));
  425. m_statMaxSpeed.push_back(std::pair<int, int>(id, st->creature->speed));
  426. totalEnemyDamage += (st->creature->damageMax + st->creature->damageMin) * st->amount / 2;
  427. totalEnemyHitPoints += hitPoints;
  428. // calculate casualties
  429. SCreatureCasualties cs;
  430. // hp * amount - damage * ( (att - def)>=0 )
  431. // hit poionts
  432. assert(hitPoints >= 0 && "CGeniusAI - creature cannot have hit points less than zero");
  433. CGHeroInstance *attackerHero = (m_side)? m_hero1 : m_hero2;
  434. CGHeroInstance *defendingHero = (m_side)? m_hero2 : m_hero1;
  435. int attackDefenseBonus = currentStack->Attack() - st->Defense();
  436. float damageFactor = 1.0f;
  437. if(attackDefenseBonus < 0) //decreasing dmg
  438. {
  439. if(0.02f * (-attackDefenseBonus) > 0.3f)
  440. {
  441. damageFactor += -0.3f;
  442. }
  443. else
  444. {
  445. damageFactor += 0.02f * attackDefenseBonus;
  446. }
  447. }
  448. else //increasing dmg
  449. {
  450. if(0.05f * attackDefenseBonus > 4.0f)
  451. {
  452. damageFactor += 4.0f;
  453. }
  454. else
  455. {
  456. damageFactor += 0.05f * attackDefenseBonus;
  457. }
  458. }
  459. cs.damage_max = (int)(currentStack->creature->damageMax * currentStack->amount * damageFactor);
  460. if (cs.damage_max > hitPoints)
  461. {
  462. cs.damage_max = hitPoints;
  463. }
  464. cs.damage_min = (int)(currentStack->creature->damageMin * currentStack->amount * damageFactor);
  465. if (cs.damage_min > hitPoints)
  466. {
  467. cs.damage_min = hitPoints;
  468. }
  469. cs.amount_max = cs.damage_max / st->creature->hitPoints;
  470. cs.amount_min = cs.damage_min / st->creature->hitPoints;
  471. cs.leftHitPoints_for_max = (hitPoints - cs.damage_max) % st->creature->hitPoints;
  472. cs.leftHitPoint_for_min = (hitPoints - cs.damage_min) % st->creature->hitPoints;
  473. m_statCasualties.push_back(std::pair<int, SCreatureCasualties>(id, cs));
  474. if (st->creature->isShooting() && st->shots > 0)
  475. {
  476. m_statDistanceFromShooters.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
  477. }
  478. if (currentStack->creature->isFlying() || (currentStack->creature->isShooting() && currentStack->shots > 0))
  479. {
  480. m_statDistance.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
  481. }
  482. else
  483. {
  484. m_statDistance.push_back(std::pair<int, int>(id, m_battleHelper.GetDistanceWithObstacles(currentStack->position, st->position)));
  485. }
  486. }
  487. else
  488. {
  489. if (st->amount < 1)
  490. {
  491. continue;
  492. }
  493. int hitPoints = st->amount * st->creature->hitPoints - (st->creature->hitPoints - st->firstHPleft);
  494. totalDamage += (st->creature->damageMax + st->creature->damageMin) * st->amount / 2;
  495. totalHitPoints += hitPoints;
  496. }
  497. }
  498. if ((float)totalDamage / (float)totalEnemyDamage < 0.5f &&
  499. (float)totalHitPoints / (float)totalEnemyHitPoints < 0.5f)
  500. {
  501. m_bEnemyDominates = true;
  502. MsgBox("** EnemyDominates!");
  503. }
  504. else
  505. {
  506. m_bEnemyDominates = false;
  507. }
  508. // sort max damage
  509. std::sort(m_statMaxDamage.begin(), m_statMaxDamage.end(),
  510. bind(&creature_stat::value_type::second, _1) > bind(&creature_stat::value_type::second, _2));
  511. // sort min damage
  512. std::sort(m_statMinDamage.begin(), m_statMinDamage.end(),
  513. bind(&creature_stat::value_type::second, _1) > bind(&creature_stat::value_type::second, _2));
  514. // sort max speed
  515. std::sort(m_statMaxSpeed.begin(), m_statMaxSpeed.end(),
  516. bind(&creature_stat::value_type::second, _1) > bind(&creature_stat::value_type::second, _2));
  517. // sort distance
  518. std::sort(m_statDistance.begin(), m_statDistance.end(),
  519. bind(&creature_stat::value_type::second, _1) < bind(&creature_stat::value_type::second, _2));
  520. // sort distance from shooters
  521. std::sort(m_statDistanceFromShooters.begin(), m_statDistanceFromShooters.end(),
  522. bind(&creature_stat::value_type::second, _1) < bind(&creature_stat::value_type::second, _2));
  523. // sort hit points
  524. std::sort(m_statHitPoints.begin(), m_statHitPoints.end(),
  525. bind(&creature_stat::value_type::second, _1) > bind(&creature_stat::value_type::second, _2));
  526. // sort casualties
  527. std::sort(m_statCasualties.begin(), m_statCasualties.end(),
  528. bind(&creature_stat_casualties::value_type::second_type::damage_max, bind(&creature_stat_casualties::value_type::second, _1))
  529. >
  530. bind(&creature_stat_casualties::value_type::second_type::damage_max, bind(&creature_stat_casualties::value_type::second, _2)));
  531. }
  532. BattleAction CBattleLogic::MakeDecision(int stackID)
  533. {
  534. MakeStatistics(stackID);
  535. list<int> creatures;
  536. int additionalInfo;
  537. if (m_bEnemyDominates)
  538. {
  539. creatures = PerformBerserkAttack(stackID, additionalInfo);
  540. }
  541. else
  542. {
  543. creatures = PerformDefaultAction(stackID, additionalInfo);
  544. }
  545. /*
  546. std::string message("Creature will be attacked - ");
  547. message += boost::lexical_cast<std::string>(creature_to_attack);
  548. MsgBox(message.c_str());
  549. */
  550. if (additionalInfo == -1 || creatures.empty())
  551. {
  552. // defend
  553. return MakeDefend(stackID);
  554. }
  555. else if (additionalInfo == -2)
  556. {
  557. return MakeWait(stackID);
  558. }
  559. list<int>::iterator it, eit;
  560. eit = creatures.end();
  561. for (it = creatures.begin(); it != eit; ++it)
  562. {
  563. BattleAction ba = MakeAttack(stackID, *it);
  564. if (ba.actionType != action_walk_and_attack)
  565. {
  566. continue;
  567. }
  568. else
  569. {
  570. PrintBattleAction(ba);
  571. return ba;
  572. }
  573. }
  574. BattleAction ba = MakeAttack(stackID, *creatures.begin());
  575. return ba;
  576. }
  577. std::vector<int> CBattleLogic::GetAvailableHexesForAttacker(CStack *defender, CStack *attacker)
  578. {
  579. int x = m_battleHelper.DecodeXPosition(defender->position);
  580. int y = m_battleHelper.DecodeYPosition(defender->position);
  581. bool defenderIsDW = defender->creature->isDoubleWide();
  582. bool attackerIsDW = attacker->creature->isDoubleWide();
  583. // TOTO: should be std::vector<int> but for debug purpose std::pair is used
  584. typedef std::pair<int, int> hexPoint;
  585. std::list<hexPoint> candidates;
  586. std::vector<int> fields;
  587. if (defenderIsDW)
  588. {
  589. if (defender->attackerOwned)
  590. {
  591. // from left side
  592. if (!(y % 2))
  593. {
  594. // up
  595. candidates.push_back(hexPoint(x - 2, y - 1));
  596. candidates.push_back(hexPoint(x - 1, y - 1));
  597. candidates.push_back(hexPoint(x, y - 1));
  598. // down
  599. candidates.push_back(hexPoint(x - 2, y + 1));
  600. candidates.push_back(hexPoint(x - 1, y + 1));
  601. candidates.push_back(hexPoint(x, y + 1));
  602. }
  603. else
  604. {
  605. // up
  606. candidates.push_back(hexPoint(x - 1, y - 1));
  607. candidates.push_back(hexPoint(x, y - 1));
  608. candidates.push_back(hexPoint(x + 1, y - 1));
  609. // down
  610. candidates.push_back(hexPoint(x - 1, y + 1));
  611. candidates.push_back(hexPoint(x, y + 1));
  612. candidates.push_back(hexPoint(x + 1, y + 1));
  613. }
  614. candidates.push_back(hexPoint(x - 2, y));
  615. candidates.push_back(hexPoint(x + 1, y));
  616. }
  617. else
  618. {
  619. // from right
  620. if (!(y % 2))
  621. {
  622. // up
  623. candidates.push_back(hexPoint(x - 1, y - 1));
  624. candidates.push_back(hexPoint(x, y - 1));
  625. candidates.push_back(hexPoint(x + 1, y - 1));
  626. // down
  627. candidates.push_back(hexPoint(x - 1, y + 1));
  628. candidates.push_back(hexPoint(x, y + 1));
  629. candidates.push_back(hexPoint(x + 1, y + 1));
  630. }
  631. else
  632. {
  633. // up
  634. candidates.push_back(hexPoint(x, y - 1));
  635. candidates.push_back(hexPoint(x + 1, y - 1));
  636. candidates.push_back(hexPoint(x + 2, y - 1));
  637. // down
  638. candidates.push_back(hexPoint(x, y + 1));
  639. candidates.push_back(hexPoint(x + 1, y + 1));
  640. candidates.push_back(hexPoint(x + 2, y + 1));
  641. }
  642. candidates.push_back(hexPoint(x - 1, y));
  643. candidates.push_back(hexPoint(x + 2, y));
  644. }
  645. }
  646. else
  647. {
  648. if (!(y % 2)) // even line
  649. {
  650. // up
  651. candidates.push_back(hexPoint(x - 1, y - 1));
  652. candidates.push_back(hexPoint(x, y - 1));
  653. // down
  654. candidates.push_back(hexPoint(x - 1, y + 1));
  655. candidates.push_back(hexPoint(x, y + 1));
  656. }
  657. else // odd line
  658. {
  659. // up
  660. candidates.push_back(hexPoint(x, y - 1));
  661. candidates.push_back(hexPoint(x + 1, y - 1));
  662. // down
  663. candidates.push_back(hexPoint(x, y + 1));
  664. candidates.push_back(hexPoint(x + 1, y + 1));
  665. }
  666. candidates.push_back(hexPoint(x + 1, y));
  667. candidates.push_back(hexPoint(x - 1, y));
  668. }
  669. // remove fields which are out of bounds or obstacles
  670. for (std::list<hexPoint>::iterator it = candidates.begin(); it != candidates.end(); ++it)
  671. {
  672. if (it->first < 1 || it->first > m_battleHelper.BattlefieldWidth ||
  673. it->second < 1 || it->second > m_battleHelper.BattlefieldHeight)
  674. {
  675. // field is out of bounds
  676. //it = candidates.erase(it);
  677. continue;
  678. }
  679. int new_pos = m_battleHelper.GetBattleFieldPosition(it->first, it->second);
  680. CStack *st = m_cb->battleGetStackByPos(new_pos);
  681. if (st == NULL || st->amount < 1)
  682. {
  683. if (attackerIsDW)
  684. {
  685. int tail_pos = -1;
  686. if (attacker->attackerOwned) // left side
  687. {
  688. int tail_pos_x = it->first - 1;
  689. if (tail_pos_x < 1)
  690. {
  691. continue;
  692. }
  693. tail_pos = m_battleHelper.GetBattleFieldPosition(it->first, it->second);
  694. }
  695. else // right side
  696. {
  697. int tail_pos_x = it->first + 1;
  698. if (tail_pos_x > m_battleHelper.BattlefieldWidth)
  699. {
  700. continue;
  701. }
  702. tail_pos = m_battleHelper.GetBattleFieldPosition(it->first, it->second);
  703. }
  704. assert(tail_pos >= 0 && "Error during calculation position of double wide creature");
  705. CStack *tailStack = m_cb->battleGetStackByPos(tail_pos);
  706. if (st != NULL && st->amount >= 1)
  707. {
  708. continue;
  709. }
  710. }
  711. fields.push_back(new_pos);
  712. }
  713. else if (attacker)
  714. {
  715. if (attacker->ID == st->ID)
  716. {
  717. fields.push_back(new_pos);
  718. }
  719. }
  720. //
  721. //++it;
  722. }
  723. return fields;
  724. }
  725. BattleAction CBattleLogic::MakeDefend(int stackID)
  726. {
  727. BattleAction ba;
  728. ba.side = 1;
  729. ba.actionType = action_defend;
  730. ba.stackNumber = stackID;
  731. ba.additionalInfo = -1;
  732. return ba;
  733. }
  734. BattleAction CBattleLogic::MakeWait(int stackID)
  735. {
  736. BattleAction ba;
  737. ba.side = 1;
  738. ba.actionType = action_wait;
  739. ba.stackNumber = stackID;
  740. ba.additionalInfo = -1;
  741. return ba;
  742. }
  743. BattleAction CBattleLogic::MakeAttack(int attackerID, int destinationID)
  744. {
  745. if (m_cb->battleCanShoot(attackerID, m_cb->battleGetPos(destinationID)))
  746. {
  747. // shoot
  748. BattleAction ba;
  749. ba.side = 1;
  750. ba.additionalInfo = -1;
  751. ba.actionType = action_shoot; // shoot
  752. ba.stackNumber = attackerID;
  753. ba.destinationTile = (ui16)m_cb->battleGetPos(destinationID);
  754. return ba;
  755. }
  756. else
  757. {
  758. // go or go&attack
  759. int dest_tile = -1;
  760. std::vector<int> av_tiles = GetAvailableHexesForAttacker(m_cb->battleGetStackByID(destinationID), m_cb->battleGetStackByID(attackerID));
  761. if (av_tiles.size() < 1)
  762. {
  763. return MakeDefend(attackerID);
  764. }
  765. // get the best tile - now the nearest
  766. int prev_distance = m_battleHelper.InfiniteDistance;
  767. int currentPos = m_cb->battleGetPos(attackerID);
  768. for (std::vector<int>::iterator it = av_tiles.begin(); it != av_tiles.end(); ++it)
  769. {
  770. int dist = m_battleHelper.GetDistanceWithObstacles(*it, m_cb->battleGetPos(attackerID));
  771. if (dist < prev_distance)
  772. {
  773. prev_distance = dist;
  774. dest_tile = *it;
  775. }
  776. if (*it == currentPos)
  777. {
  778. dest_tile = currentPos;
  779. break;
  780. }
  781. }
  782. std::vector<int> fields = m_cb->battleGetAvailableHexes(attackerID, false);
  783. if(fields.size() == 0)
  784. {
  785. return MakeDefend(attackerID);
  786. }
  787. BattleAction ba;
  788. ba.side = 1;
  789. //ba.actionType = 6; // go and attack
  790. ba.stackNumber = attackerID;
  791. ba.destinationTile = (ui16)dest_tile;
  792. //simplified checking for possibility of attack (previous was too simplified)
  793. int destStackPos = m_cb->battleGetPos(destinationID);
  794. if(BattleInfo::mutualPosition(dest_tile, destStackPos) != -1)
  795. ba.additionalInfo = destStackPos;
  796. else if(BattleInfo::mutualPosition(dest_tile, destStackPos+1) != -1)
  797. ba.additionalInfo = destStackPos+1;
  798. else if(BattleInfo::mutualPosition(dest_tile, destStackPos-1) != -1)
  799. ba.additionalInfo = destStackPos-1;
  800. else
  801. MakeDefend(attackerID);
  802. int nearest_dist = m_battleHelper.InfiniteDistance;
  803. int nearest_pos = -1;
  804. // if double wide calculate tail
  805. CStack *attackerStack = m_cb->battleGetStackByID(attackerID);
  806. assert(attackerStack != NULL);
  807. int tail_pos = -1;
  808. if (attackerStack->creature->isDoubleWide())
  809. {
  810. int x_pos = m_battleHelper.DecodeXPosition(attackerStack->position);
  811. int y_pos = m_battleHelper.DecodeYPosition(attackerStack->position);
  812. if (attackerStack->attackerOwned)
  813. {
  814. x_pos -= 1;
  815. }
  816. else
  817. {
  818. x_pos += 1;
  819. }
  820. // if creature can perform attack without movement - do it!
  821. tail_pos = m_battleHelper.GetBattleFieldPosition(x_pos, y_pos);
  822. if (dest_tile == tail_pos)
  823. {
  824. ba.additionalInfo = dest_tile;
  825. ba.actionType = action_walk_and_attack;
  826. PrintBattleAction(ba);
  827. return ba;
  828. }
  829. }
  830. for (std::vector<int>::const_iterator it = fields.begin(); it != fields.end(); ++it)
  831. {
  832. if (*it == dest_tile)
  833. {
  834. // attack!
  835. ba.actionType = action_walk_and_attack;
  836. #if defined _DEBUG
  837. PrintBattleAction(ba);
  838. #endif
  839. return ba;
  840. }
  841. int d = m_battleHelper.GetDistanceWithObstacles(dest_tile, *it);
  842. if (d < nearest_dist)
  843. {
  844. nearest_dist = d;
  845. nearest_pos = *it;
  846. }
  847. }
  848. string message;
  849. message = "Attacker position X=";
  850. message += boost::lexical_cast<std::string>(m_battleHelper.DecodeXPosition(nearest_pos)) + ", Y=";
  851. message += boost::lexical_cast<std::string>(m_battleHelper.DecodeYPosition(nearest_pos));
  852. MsgBox(message.c_str());
  853. ba.actionType = action_walk;
  854. ba.destinationTile = (ui16)nearest_pos;
  855. ba.additionalInfo = -1;
  856. #if defined _DEBUG
  857. PrintBattleAction(ba);
  858. #endif
  859. return ba;
  860. }
  861. }
  862. /**
  863. * The main idea is to perform maximum casualties.
  864. */
  865. list<int> CBattleLogic::PerformBerserkAttack(int stackID, int &additionalInfo)
  866. {
  867. CCreature c = m_cb->battleGetCreature(stackID);
  868. // attack to make biggest damage
  869. list<int> creatures;
  870. if (!m_statCasualties.empty())
  871. {
  872. //creature_to_attack = m_statCasualties.begin()->first;
  873. creature_stat_casualties::iterator it = m_statCasualties.begin();
  874. for (; it != m_statCasualties.end(); ++it)
  875. {
  876. if (it->second.amount_min <= 0)
  877. {
  878. creatures.push_back(it->first);
  879. continue;
  880. }
  881. for (creature_stat::const_iterator it2 = m_statDistance.begin(); it2 != m_statDistance.end(); ++it2)
  882. {
  883. if (it2->first == it->first && it2->second - 1 <= c.speed)
  884. {
  885. creatures.push_front(it->first);
  886. }
  887. }
  888. }
  889. creatures.push_back(m_statCasualties.begin()->first);
  890. }
  891. return creatures;
  892. }
  893. list<int> CBattleLogic::PerformDefaultAction(int stackID, int &additionalInfo)
  894. {
  895. // first approach based on the statistics and weights
  896. // if this solution was fine we would develop this idea
  897. //
  898. std::map<int, int> votes;
  899. for (creature_stat::iterator it = m_statMaxDamage.begin(); it != m_statMaxDamage.end(); ++it)
  900. {
  901. votes[it->first] = 0;
  902. }
  903. votes[m_statMaxDamage.begin()->first] += m_battleHelper.GetVoteForMaxDamage();
  904. votes[m_statMinDamage.begin()->first] += m_battleHelper.GetVoteForMinDamage();
  905. if (m_statDistanceFromShooters.size())
  906. {
  907. votes[m_statDistanceFromShooters.begin()->first] += m_battleHelper.GetVoteForDistanceFromShooters();
  908. }
  909. votes[m_statDistance.begin()->first] += m_battleHelper.GetVoteForDistance();
  910. votes[m_statHitPoints.begin()->first] += m_battleHelper.GetVoteForHitPoints();
  911. votes[m_statMaxSpeed.begin()->first] += m_battleHelper.GetVoteForMaxSpeed();
  912. // get creature to attack
  913. int max_vote = 0;
  914. list<int> creatures;
  915. for (std::map<int, int>::iterator it = votes.begin(); it != votes.end(); ++it)
  916. {
  917. if (it->second > max_vote)
  918. {
  919. max_vote = it->second;
  920. creatures.push_front(it->first);
  921. }
  922. }
  923. additionalInfo = 0; // list contains creatures which shoud be attacked
  924. return creatures;
  925. }
  926. void CBattleLogic::PrintBattleAction(const BattleAction &action) // for debug purpose
  927. {
  928. std::string message("Battle action \n");
  929. message += "\taction type - ";
  930. switch (action.actionType)
  931. {
  932. case 0:
  933. message += "Cancel BattleAction\n";
  934. break;
  935. case 1:
  936. message += "Hero cast a spell\n";
  937. break;
  938. case 2:
  939. message += "Walk\n";
  940. break;
  941. case 3:
  942. message += "Defend\n";
  943. break;
  944. case 4:
  945. message += "Retreat from the battle\n";
  946. break;
  947. case 5:
  948. message += "Surrender\n";
  949. break;
  950. case 6:
  951. message += "Walk and Attack\n";
  952. break;
  953. case 7:
  954. message += "Shoot\n";
  955. break;
  956. case 8:
  957. message += "Wait\n";
  958. break;
  959. case 9:
  960. message += "Catapult\n";
  961. break;
  962. case 10:
  963. message += "Monster casts a spell\n";
  964. break;
  965. }
  966. message += "\tDestination tile: X = ";
  967. message += boost::lexical_cast<std::string>(m_battleHelper.DecodeXPosition(action.destinationTile));
  968. message += ", Y = " + boost::lexical_cast<std::string>(m_battleHelper.DecodeYPosition(action.destinationTile));
  969. message += "\nAdditional info: ";
  970. if (action.actionType == 6)// || action.actionType == 7)
  971. {
  972. message += "stack - " + boost::lexical_cast<std::string>(m_battleHelper.DecodeXPosition(action.additionalInfo));
  973. message += ", " + boost::lexical_cast<std::string>(m_battleHelper.DecodeYPosition(action.additionalInfo));
  974. message += ", creature - ";
  975. CStack *c = m_cb->battleGetStackByPos(action.additionalInfo);
  976. if (c && c->creature)
  977. {
  978. message += c->creature->nameRef;
  979. }
  980. else
  981. {
  982. message += "NULL";
  983. }
  984. }
  985. else
  986. {
  987. message += boost::lexical_cast<std::string>(action.additionalInfo);
  988. }
  989. #ifdef _WIN32
  990. HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  991. CONSOLE_SCREEN_BUFFER_INFO csbi;
  992. GetConsoleScreenBufferInfo(hConsole, &csbi);
  993. SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
  994. #else
  995. std::string color;
  996. color = "\x1b[1;40;32m";
  997. std::cout << color;
  998. #endif
  999. std::cout << message.c_str() << std::endl;
  1000. #ifdef _WIN32
  1001. SetConsoleTextAttribute(hConsole, csbi.wAttributes);
  1002. #else
  1003. color = "\x1b[0m";
  1004. std::cout << color;
  1005. #endif
  1006. }