CGeniusAI.cpp 28 KB

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