CGeniusAI.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  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);
  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. return battleFieldPosition - (DecodeYPosition(battleFieldPosition) - 1) * 17;
  299. }
  300. int CBattleHelper::DecodeYPosition(int battleFieldPosition)
  301. {
  302. double y = battleFieldPosition / 17;
  303. if (y - (int)y > 0.0)
  304. {
  305. return (int)y + 1;
  306. }
  307. return (int)y;
  308. }
  309. int CBattleHelper::GetShortestDistance(int pointA, int pointB)
  310. {
  311. /**
  312. * TODO: function hasn't been checked!
  313. */
  314. int x1 = DecodeXPosition(pointA);
  315. int y1 = DecodeYPosition(pointA);
  316. //
  317. int x2 = DecodeXPosition(pointB);
  318. int y2 = DecodeYPosition(pointB);
  319. //
  320. double dx = x1 - x2;
  321. double dy = y1 - y2;
  322. return (int)sqrt(dx * dx + dy * dy);
  323. }
  324. int CBattleHelper::GetDistanceWithObstacles(int pointA, int pointB)
  325. {
  326. // TODO - implement this!
  327. return GetShortestDistance(pointA, pointB);
  328. }
  329. /**
  330. * Implementation of CBattleLogic class.
  331. */
  332. CBattleLogic::CBattleLogic(ICallback *cb) :
  333. m_cb(cb),
  334. m_iCurrentTurn(-2),
  335. m_bIsAttacker(false)
  336. {
  337. const int max_enemy_creatures = 12;
  338. m_statMaxDamage.reserve(max_enemy_creatures);
  339. m_statMinDamage.reserve(max_enemy_creatures);
  340. m_statMaxSpeed.reserve(max_enemy_creatures);
  341. m_statDistance.reserve(max_enemy_creatures);
  342. m_statDistanceFromShooters.reserve(max_enemy_creatures);
  343. m_statHitPoints.reserve(max_enemy_creatures);
  344. }
  345. CBattleLogic::~CBattleLogic()
  346. {}
  347. void CBattleLogic::SetCurrentTurn(int turn)
  348. {
  349. m_iCurrentTurn = turn;
  350. }
  351. void CBattleLogic::MakeStatistics(int currentCreatureId)
  352. {
  353. typedef std::map<int, CStack> map_stacks;
  354. map_stacks allStacks = m_cb->battleGetStacks();
  355. const CStack *currentStack = m_cb->battleGetStackByID(currentCreatureId);
  356. /*
  357. // find all creatures belong to the enemy
  358. std::for_each(allStacks.begin(), allStacks.end(),
  359. if_(bind<ui8>(&CStack::attackerOwned, bind<CStack>(&map_stacks::value_type::second, _1)) == m_bIsAttacker)
  360. [
  361. var(enemy)[ret<int>(bind<int>(&map_stacks::value_type::first, _1))] =
  362. ret<CStack>(bind<CStack>(&map_stacks::value_type::second, _1))
  363. ]
  364. );
  365. // fill other containers
  366. // max damage
  367. std::for_each(enemy.begin(), enemy.end(),
  368. var(m_statMaxDamage)[ret<int>(bind<int>(&map_stacks::value_type::first, _1))] =
  369. ret<int>(bind<int>(&CCreature::damageMax, bind<CCreature*>(&CStack::creature,
  370. bind<CStack>(&map_stacks::value_type::second, _1))))
  371. );
  372. // min damage
  373. std::for_each(enemy.begin(), enemy.end(),
  374. var(m_statMinDamage)[ret<int>(bind<int>(&map_stacks::value_type::first, _1))] =
  375. ret<int>(bind<int>(&CCreature::damageMax, bind<CCreature*>(&CStack::creature,
  376. bind<CStack>(&map_stacks::value_type::second, _1))))
  377. );
  378. */
  379. m_statMaxDamage.clear();
  380. m_statMinDamage.clear();
  381. m_statHitPoints.clear();
  382. m_statMaxSpeed.clear();
  383. m_statDistanceFromShooters.clear();
  384. m_statDistance.clear();
  385. m_statDistance.clear();
  386. for (map_stacks::const_iterator it = allStacks.begin(); it != allStacks.end(); ++it)
  387. {
  388. if (it->second.attackerOwned != m_bIsAttacker)
  389. {
  390. int id = it->first;
  391. const CStack *st = &it->second;
  392. if (st->amount < 1)
  393. {
  394. continue;
  395. }
  396. // make stats
  397. m_statMaxDamage.push_back(std::pair<int, int>(id, st->creature->damageMax * st->amount));
  398. m_statMinDamage.push_back(std::pair<int, int>(id, st->creature->damageMin * st->amount));
  399. m_statHitPoints.push_back(std::pair<int, int>(id, st->creature->hitPoints * st->amount));
  400. m_statMaxSpeed.push_back(std::pair<int, int>(id, st->creature->speed));
  401. if (st->creature->isShooting() && st->shots > 0)
  402. {
  403. m_statDistanceFromShooters.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
  404. }
  405. if (currentStack->creature->isFlying() || (currentStack->creature->isShooting() && currentStack->shots > 0))
  406. {
  407. m_statDistance.push_back(std::pair<int, int>(id, m_battleHelper.GetShortestDistance(currentStack->position, st->position)));
  408. }
  409. else
  410. {
  411. m_statDistance.push_back(std::pair<int, int>(id, m_battleHelper.GetDistanceWithObstacles(currentStack->position, st->position)));
  412. }
  413. }
  414. }
  415. // sort max damage
  416. std::sort(m_statMaxDamage.begin(), m_statMaxDamage.end(),
  417. bind(&creature_stat::value_type::second, _1) > bind(&creature_stat::value_type::second, _2));
  418. // sort min damage
  419. std::sort(m_statMinDamage.begin(), m_statMinDamage.end(),
  420. bind(&creature_stat::value_type::second, _1) > bind(&creature_stat::value_type::second, _2));
  421. // sort max speed
  422. std::sort(m_statMaxSpeed.begin(), m_statMaxSpeed.end(),
  423. bind(&creature_stat::value_type::second, _1) > bind(&creature_stat::value_type::second, _2));
  424. // sort distance
  425. std::sort(m_statDistance.begin(), m_statDistance.end(),
  426. bind(&creature_stat::value_type::second, _1) < bind(&creature_stat::value_type::second, _2));
  427. // sort distance from shooters
  428. std::sort(m_statDistanceFromShooters.begin(), m_statDistanceFromShooters.end(),
  429. bind(&creature_stat::value_type::second, _1) < bind(&creature_stat::value_type::second, _2));
  430. // sort hit points
  431. std::sort(m_statHitPoints.begin(), m_statHitPoints.end(),
  432. bind(&creature_stat::value_type::second, _1) > bind(&creature_stat::value_type::second, _2));
  433. }
  434. BattleAction CBattleLogic::MakeDecision(int stackID)
  435. {
  436. MakeStatistics(stackID);
  437. // first approach based on the statistics and weights
  438. // if this solution was fine we would develop this idea
  439. int creature_to_attack = -1;
  440. //
  441. std::map<int, int> votes;
  442. for (creature_stat::iterator it = m_statMaxDamage.begin(); it != m_statMaxDamage.end(); ++it)
  443. {
  444. votes[it->first] = 0;
  445. }
  446. votes[m_statMaxDamage.begin()->first] += m_battleHelper.GetVoteForMaxDamage();
  447. votes[m_statMinDamage.begin()->first] += m_battleHelper.GetVoteForMinDamage();
  448. if (m_statDistanceFromShooters.size())
  449. {
  450. votes[m_statDistanceFromShooters.begin()->first] += m_battleHelper.GetVoteForDistanceFromShooters();
  451. }
  452. votes[m_statDistance.begin()->first] += m_battleHelper.GetVoteForDistance();
  453. votes[m_statHitPoints.begin()->first] += m_battleHelper.GetVoteForHitPoints();
  454. votes[m_statMaxSpeed.begin()->first] += m_battleHelper.GetVoteForMaxSpeed();
  455. // get creature to attack
  456. int max_vote = 0;
  457. for (std::map<int, int>::iterator it = votes.begin(); it != votes.end(); ++it)
  458. {
  459. if (it->second > max_vote)
  460. {
  461. max_vote = it->second;
  462. creature_to_attack = it->first;
  463. }
  464. }
  465. if (creature_to_attack == -1)
  466. {
  467. // defend
  468. return MakeDefend(stackID);
  469. }
  470. if (m_cb->battleCanShoot(stackID, m_cb->battleGetPos(creature_to_attack)))
  471. {
  472. // shoot
  473. BattleAction ba;
  474. ba.side = 1;
  475. ba.actionType = action_shoot; // shoot
  476. ba.stackNumber = stackID;
  477. ba.destinationTile = m_cb->battleGetPos(creature_to_attack);
  478. return ba;
  479. }
  480. else
  481. {
  482. // go or go&attack
  483. int dest_tile = -1; //m_cb->battleGetPos(creature_to_attack) + 1;
  484. std::vector<int> av_tiles = GetAvailableHexesForAttacker(m_cb->battleGetStackByID(creature_to_attack));
  485. if (av_tiles.size() < 1)
  486. {
  487. // TODO: shouldn't be like that
  488. return MakeDefend(stackID);
  489. }
  490. // get the best tile - now the nearest
  491. int prev_distance = m_battleHelper.InfiniteDistance;
  492. for (std::vector<int>::iterator it = av_tiles.begin(); it != av_tiles.end(); ++it)
  493. {
  494. int dist = m_battleHelper.GetDistanceWithObstacles(*it, m_cb->battleGetPos(stackID));
  495. if (dist < prev_distance)
  496. {
  497. prev_distance = dist;
  498. dest_tile = *it;
  499. }
  500. }
  501. std::vector<int> fields = m_cb->battleGetAvailableHexes(stackID);
  502. BattleAction ba;
  503. ba.side = 1;
  504. //ba.actionType = 6; // go and attack
  505. ba.stackNumber = stackID;
  506. ba.destinationTile = dest_tile;
  507. ba.additionalInfo = m_cb->battleGetPos(creature_to_attack);
  508. int nearest_dist = m_battleHelper.InfiniteDistance;
  509. int nearest_pos = -1;
  510. for (std::vector<int>::const_iterator it = fields.begin(); it != fields.end(); ++it)
  511. {
  512. if (*it == dest_tile)
  513. {
  514. // attack!
  515. ba.actionType = action_walk_and_attack;
  516. return ba;
  517. }
  518. int d = m_battleHelper.GetDistanceWithObstacles(dest_tile, *it);
  519. if (d < nearest_dist)
  520. {
  521. nearest_dist = d;
  522. nearest_pos = *it;
  523. }
  524. }
  525. ba.actionType = action_walk;
  526. ba.destinationTile = nearest_pos;
  527. ba.additionalInfo = -1;
  528. return ba;
  529. }
  530. }
  531. std::vector<int> CBattleLogic::GetAvailableHexesForAttacker(CStack *defender)
  532. {
  533. int x = m_battleHelper.DecodeXPosition(defender->position);
  534. int y = m_battleHelper.DecodeYPosition(defender->position);
  535. // TOTO: should be std::vector<int> but for debug purpose std::pair is used
  536. std::vector<std::pair<int, int> > candidates;
  537. std::vector<int> fields;
  538. // find neighbourhood
  539. bool upLimit = false;
  540. bool downLimit = false;
  541. bool leftLimit = false;
  542. bool rightLimit = false;
  543. if (x == 1)
  544. {
  545. leftLimit = true;
  546. }
  547. else if (x == m_battleHelper.BattlefieldWidth) // x+ 1
  548. {
  549. rightLimit = true;
  550. }
  551. if (y == 1)
  552. {
  553. upLimit = true;
  554. }
  555. else if (y == m_battleHelper.BattlefieldHeight)
  556. {
  557. downLimit = true;
  558. }
  559. if (!downLimit)
  560. {
  561. candidates.push_back(std::pair<int, int>(x, y + 1));
  562. }
  563. if (!downLimit && !leftLimit)
  564. {
  565. candidates.push_back(std::pair<int, int>(x - 1, y + 1));
  566. }
  567. if (!downLimit && !rightLimit)
  568. {
  569. candidates.push_back(std::pair<int, int>(x + 1, y + 1));
  570. }
  571. if (!upLimit)
  572. {
  573. candidates.push_back(std::pair<int, int>(x, y - 1));
  574. }
  575. if (!upLimit && !leftLimit)
  576. {
  577. candidates.push_back(std::pair<int, int>(x - 1, y - 1));
  578. }
  579. if (!upLimit && !rightLimit)
  580. {
  581. candidates.push_back(std::pair<int, int>(x + 1, y - 1));
  582. }
  583. // check if these field are empty
  584. for (std::vector<std::pair<int, int> >::iterator it = candidates.begin(); it != candidates.end(); ++it)
  585. {
  586. int new_pos = m_battleHelper.GetBattleFieldPosition(it->first, it->second);
  587. CStack *st = m_cb->battleGetStackByPos(new_pos);
  588. // int obstacle = m_cb->battleGetObstaclesAtTile(new_pos); // TODO: wait for battleGetObstaclesAtTile function
  589. if (st == NULL || st->amount < 1)
  590. {
  591. fields.push_back(new_pos);
  592. }
  593. }
  594. return fields;
  595. }
  596. BattleAction CBattleLogic::MakeDefend(int stackID)
  597. {
  598. BattleAction ba;
  599. ba.side = 1;
  600. ba.actionType = action_defend;
  601. ba.stackNumber = stackID;
  602. ba.additionalInfo = -1;
  603. return ba;
  604. }